diff options
107 files changed, 2867 insertions, 923 deletions
@@ -25,7 +25,7 @@ ######################################################## SAMBA_VERSION_MAJOR=4 SAMBA_VERSION_MINOR=1 -SAMBA_VERSION_RELEASE=11 +SAMBA_VERSION_RELEASE=12 ######################################################## # If a official release has a serious bug # diff --git a/WHATSNEW.txt b/WHATSNEW.txt index 521ea1fd0b8..25059275056 100644 --- a/WHATSNEW.txt +++ b/WHATSNEW.txt @@ -1,4 +1,137 @@ ============================== + Release Notes for Samba 4.1.12 + September 8, 2014 + ============================== + + +This is the latest stable release of Samba 4.1. + +Major enhancements in Samba 4.1.12 include: + +o New parameter "winbind request timeout" has been added (bug #3204). Please + see smb.conf man page for details. +o Fix smbd crashes when filename contains non-ascii character (bug #10716). +o dnsserver: Handle updates of tombstoned dnsNode objects (bug #10749). + + +Changes since 4.1.11: +--------------------- + +o Michael Adam <obnox@samba.org> + * BUG 10369: build: Fix configure to honour '--without-dmapi'. + * BUG 10737: s3:idmap: Don't log missing range config if range checking not + requested. + * BUG 10741: Fix flapping VFS gpfs offline bit. + + +o Jeremy Allison <jra@samba.org> + * BUG 3204: s3: winbindd: On new client connect, prune idle or hung + connections older than "winbind request timeout". Add new parameter + "winbind request timeout". + * BUG 10640: lib: tevent: make TEVENT_SIG_INCREMENT atomic. + * BUG 10650: Make "case sensitive = True" option working with + "max protocol = SMB2" or higher in large directories. + * BUG 10716: Fix smbd crashes when filename contains non-ascii + character. + * BUG 10728: 'net time': Fix usage and core dump. + * BUG 10773: s3: smbd: POSIX ACLs. Remove incorrect check for + SECINFO_PROTECTED_DACL in incoming security_information flags in + posix_get_nt_acl_common(). + * BUG 10794: vfs_dirsort: Fix an off-by-one error that can cause + uninitialized memory read. + + +o Björn Baumbach <bb@sernet.de> + * BUG 10543: s3: Enforce a positive allocation_file_size for non-empty + files. + + +o Kai Blin <kai@samba.org> + * BUG 10466: provision: Correctly provision the SOA record minimum TTL. + + +o David Disseldorp <ddiss@samba.org> + * BUG 10652: Samba 4 consuming a lot of CPU when re-reading printcap info. + * BUG 10787: dosmode: Fix FSCTL_SET_SPARSE request validation. + + +o Amitay Isaacs <amitay@gmail.com> + * BUG 10742: s4-rpc: dnsserver: Allow . to be specified for @ record. + + +o Daniel Kobras <d.kobras@science-computing.de> + * BUG 10731: sys_poll_intr: Fix timeout arithmetic. + + +o Ross Lagerwall <rosslagerwall@gmail.com> + * BUG 10778: s3:libsmb: Set a max charge for SMB2 connections. + + +o Volker Lendecke <vl@samba.org> + * BUG 10716: lib: strings: Simplify strcasecmp. + * BUG 10758: lib: Remove unused nstrcpy. + * BUG 10782: smbd: Properly initialize mangle_hash. + + +o Stefan Metzmacher <metze@samba.org> + * BUG 9831: s4:setup/dns_update_list: make use of the new substitution + variables. + * BUG 10723: Allow netr_ServerReqChallenge() and netr_ServerAuthenticate3() + on different connections. + * BUG 10749: s4-rpc: dnsserver: Handle updates of tombstoned dnsNode + objects. + * BUG 10751: s4-rpc: dnsserver: return DNS_RANK_NS_GLUE recors when + explicitly asked for. + * BUG 10773: libcli/security: Add better detection of + SECINFO_[UN]PROTECTED_[D|S]ACL in get_sec_info(). + + +o Marc Muehlfeld <mmuehlfeld@samba.org> + * BUG 10761: docs: Fix typos in smb.conf (inherit acls). + + +o Shirish Pargaonkar <spargaonkar@suse.com> + * BUG 10755: samba: Retain case sensitivity of cifs client. + + +o Arvid Requate <requate@univention.de> + * BUG 9570: passdb: Fix NT_STATUS_NO_SUCH_GROUP. + + +o Har Gagan Sahai <SHarGagan@novell.com> + * BUG 10759: Fix a memory leak in cli_set_mntpoint(). + + +o Roel van Meer <roel@1afa.com> + * BUG 10777: Don't discard result of checking grouptype. + + +####################################### +Reporting bugs & Development Discussion +####################################### + +Please discuss this release on the samba-technical mailing list or by +joining the #samba-technical IRC channel on irc.freenode.net. + +If you do report problems then please try to send high quality +feedback. If you don't provide vital information to help us track down +the problem then you will probably be ignored. All bug reports should +be filed under the Samba 4.1 product in the project's Bugzilla +database (https://bugzilla.samba.org/). + + +====================================================================== +== Our Code, Our Bugs, Our Responsibility. +== The Samba Team +====================================================================== + + +Release notes for older releases follow: +---------------------------------------- + +====================================================================== + + ============================== Release Notes for Samba 4.1.11 August 1, 2014 ============================== @@ -44,10 +177,8 @@ database (https://bugzilla.samba.org/). ====================================================================== -Release notes for older releases follow: ----------------------------------------- +---------------------------------------------------------------------- -====================================================================== ============================== Release Notes for Samba 4.1.10 diff --git a/docs-xml/manpages/net.8.xml b/docs-xml/manpages/net.8.xml index e2e1b8ff566..4c88008ea8a 100644 --- a/docs-xml/manpages/net.8.xml +++ b/docs-xml/manpages/net.8.xml @@ -404,7 +404,8 @@ YOU HAVE BEEN WARNED. <title>TIME</title> <para>Without any options, the <command>NET TIME</command> command -displays the time on the remote server. +displays the time on the remote server. The remote server must be +specified with the -S option. </para> </refsect3> @@ -412,21 +413,27 @@ displays the time on the remote server. <refsect3> <title>TIME SYSTEM</title> -<para>Displays the time on the remote server in a format ready for <command>/bin/date</command>.</para> +<para>Displays the time on the remote server in a format ready for <command>/bin/date</command>. +The remote server must be specified with the -S option. +</para> </refsect3> <refsect3> <title>TIME SET</title> <para>Tries to set the date and time of the local server to that on -the remote server using <command>/bin/date</command>. </para> +the remote server using <command>/bin/date</command>. +The remote server must be specified with the -S option. +</para> </refsect3> <refsect3> <title>TIME ZONE</title> -<para>Displays the timezone in hours from GMT on the remote computer.</para> +<para>Displays the timezone in hours from GMT on the remote server. +The remote server must be specified with the -S option. +</para> </refsect3> </refsect2> diff --git a/docs-xml/smbdotconf/security/inheritowner.xml b/docs-xml/smbdotconf/security/inheritowner.xml index ba4fc617cb5..0ed8285b065 100644 --- a/docs-xml/smbdotconf/security/inheritowner.xml +++ b/docs-xml/smbdotconf/security/inheritowner.xml @@ -10,9 +10,9 @@ by the ownership of the parent directory.</para> <para>Common scenarios where this behavior is useful is in - implementing drop-boxes where users can create and edit files but not - delete them and to ensure that newly create files in a user's - roaming profile directory are actually owner by the user.</para> + implementing drop-boxes, where users can create and edit files but + not delete them and ensuring that newly created files in a user's + roaming profile directory are actually owned by the user.</para> </description> <related>inherit permissions</related> diff --git a/docs-xml/smbdotconf/winbind/winbindrequesttimeout.xml b/docs-xml/smbdotconf/winbind/winbindrequesttimeout.xml new file mode 100644 index 00000000000..322087161d2 --- /dev/null +++ b/docs-xml/smbdotconf/winbind/winbindrequesttimeout.xml @@ -0,0 +1,16 @@ +<samba:parameter name="winbind request timeout" + context="G" + type="integer" + advanced="1" developer="1" + xmlns:samba="http://www.samba.org/samba/DTD/samba-doc"> +<description> + <para>This parameter specifies the number of + seconds the <citerefentry><refentrytitle>winbindd</refentrytitle> + <manvolnum>8</manvolnum></citerefentry> daemon will wait before + disconnecting either a client connection with no outstanding + requests (idle) or a client connection with a request that has + remained outstanding (hung) for longer than this number of seconds.</para> +</description> + +<value type="default">60</value> +</samba:parameter> diff --git a/lib/param/param_functions.c b/lib/param/param_functions.c index 61f00448ca3..d9d5df62726 100644 --- a/lib/param/param_functions.c +++ b/lib/param/param_functions.c @@ -341,6 +341,7 @@ FN_GLOBAL_INTEGER(winbind_cache_time, winbind_cache_time) FN_GLOBAL_INTEGER(winbind_expand_groups, winbind_expand_groups) FN_GLOBAL_INTEGER(winbind_max_clients, winbind_max_clients) FN_GLOBAL_INTEGER(winbind_reconnect_delay, winbind_reconnect_delay) +FN_GLOBAL_INTEGER(winbind_request_timeout, winbind_request_timeout) FN_GLOBAL_LIST(auth_methods, AuthMethods) FN_GLOBAL_LIST(cluster_addresses, szClusterAddresses) FN_GLOBAL_LIST(dcerpc_endpoint_servers, dcerpc_ep_servers) diff --git a/lib/param/param_table.c b/lib/param/param_table.c index 7b32998084c..8e3f952b6ec 100644 --- a/lib/param/param_table.c +++ b/lib/param/param_table.c @@ -4018,6 +4018,15 @@ static struct parm_struct parm_table[] = { .flags = FLAG_ADVANCED, }, { + .label = "winbind request timeout", + .type = P_INTEGER, + .p_class = P_GLOBAL, + .offset = GLOBAL_VAR(winbind_request_timeout), + .special = NULL, + .enum_list = NULL, + .flags = FLAG_ADVANCED, + }, + { .label = "winbind max clients", .type = P_INTEGER, .p_class = P_GLOBAL, diff --git a/lib/replace/replace.h b/lib/replace/replace.h index c0b799763a8..cd0c25e2af7 100644 --- a/lib/replace/replace.h +++ b/lib/replace/replace.h @@ -899,4 +899,9 @@ int usleep(useconds_t); void rep_setproctitle(const char *fmt, ...) PRINTF_ATTRIBUTE(1, 2); #endif +/* Needed for Solaris atomic_add_XX functions. */ +#if defined(HAVE_SYS_ATOMIC_H) +#include <sys/atomic.h> +#endif + #endif /* _LIBREPLACE_REPLACE_H */ diff --git a/lib/replace/wscript b/lib/replace/wscript index 84516898003..f0040b18e02 100644 --- a/lib/replace/wscript +++ b/lib/replace/wscript @@ -106,6 +106,7 @@ struct foo bar = { .y = 'X', .x = 1 }; conf.CHECK_HEADERS('sys/extattr.h sys/ea.h sys/proplist.h sys/cdefs.h') conf.CHECK_HEADERS('utmp.h utmpx.h lastlog.h malloc.h') conf.CHECK_HEADERS('syscall.h sys/syscall.h inttypes.h') + conf.CHECK_HEADERS('sys/atomic.h') # Check for process set name support conf.CHECK_CODE(''' @@ -225,6 +226,30 @@ struct foo bar = { .y = 'X', .x = 1 }; msg="Checking whether we have ucontext_t", headers='signal.h sys/ucontext.h') + # Check for atomic builtins. */ + conf.CHECK_CODE(''' + int main(void) { + int i; + (void)__sync_fetch_and_add(&i, 1); + return 0; + } + ''', + 'HAVE___SYNC_FETCH_AND_ADD', + msg='Checking for __sync_fetch_and_add compiler builtin') + + conf.CHECK_CODE(''' + #include <stdint.h> + #include <sys/atomic.h> + int main(void) { + int32_t i; + atomic_add_32(&i, 1); + return 0; + } + ''', + 'HAVE_ATOMIC_ADD_32', + headers='stdint.h sys/atomic.h', + msg='Checking for atomic_add_32 compiler builtin') + # these may be builtins, so we need the link=False strategy conf.CHECK_FUNCS('strdup memmem printf memset memcpy memmove strcpy strncpy bzero', link=False) diff --git a/lib/tevent/tevent_signal.c b/lib/tevent/tevent_signal.c index 0fdf646c8de..95a099d6ab5 100644 --- a/lib/tevent/tevent_signal.c +++ b/lib/tevent/tevent_signal.c @@ -42,7 +42,13 @@ struct tevent_sigcounter { uint32_t seen; }; +#if defined(HAVE___SYNC_FETCH_AND_ADD) +#define TEVENT_SIG_INCREMENT(s) __sync_fetch_and_add(&((s).count), 1) +#elif defined(HAVE_ATOMIC_ADD_32) +#define TEVENT_SIG_INCREMENT(s) atomic_add_32(&((s).count), 1) +#else #define TEVENT_SIG_INCREMENT(s) (s).count++ +#endif #define TEVENT_SIG_SEEN(s, n) (s).seen += (n) #define TEVENT_SIG_PENDING(s) ((s).seen != (s).count) diff --git a/lib/util/charset/tests/charset.c b/lib/util/charset/tests/charset.c index 70b67418763..a47670e6665 100644 --- a/lib/util/charset/tests/charset.c +++ b/lib/util/charset/tests/charset.c @@ -50,12 +50,18 @@ static bool test_codepoint_cmpi(struct torture_context *tctx) static bool test_strcasecmp_m(struct torture_context *tctx) { + /* file.{accented e} in iso8859-1 */ + const char file_iso8859_1[7] = { 0x66, 0x69, 0x6c, 0x65, 0x2d, 0xe9, 0 }; + /* file.{accented e} in utf8 */ + const char file_utf8[8] = { 0x66, 0x69, 0x6c, 0x65, 0x2d, 0xc3, 0xa9, 0 }; torture_assert(tctx, strcasecmp_m("foo", "bar") != 0, "different strings"); torture_assert(tctx, strcasecmp_m("foo", "foo") == 0, "same case strings"); torture_assert(tctx, strcasecmp_m("foo", "Foo") == 0, "different case strings"); torture_assert(tctx, strcasecmp_m(NULL, "Foo") != 0, "one NULL"); torture_assert(tctx, strcasecmp_m("foo", NULL) != 0, "other NULL"); torture_assert(tctx, strcasecmp_m(NULL, NULL) == 0, "both NULL"); + torture_assert(tctx, strcasecmp_m(file_iso8859_1, file_utf8) != 0, + "file.{accented e} should differ"); return true; } @@ -102,6 +108,10 @@ static bool test_string_replace_m(struct torture_context *tctx) static bool test_strncasecmp_m(struct torture_context *tctx) { + /* file.{accented e} in iso8859-1 */ + const char file_iso8859_1[7] = { 0x66, 0x69, 0x6c, 0x65, 0x2d, 0xe9, 0 }; + /* file.{accented e} in utf8 */ + const char file_utf8[8] = { 0x66, 0x69, 0x6c, 0x65, 0x2d, 0xc3, 0xa9, 0 }; torture_assert(tctx, strncasecmp_m("foo", "bar", 3) != 0, "different strings"); torture_assert(tctx, strncasecmp_m("foo", "foo", 3) == 0, "same case strings"); torture_assert(tctx, strncasecmp_m("foo", "Foo", 3) == 0, "different case strings"); @@ -111,6 +121,8 @@ static bool test_strncasecmp_m(struct torture_context *tctx) torture_assert(tctx, strncasecmp_m(NULL, "Foo", 3) != 0, "one NULL"); torture_assert(tctx, strncasecmp_m("foo", NULL, 3) != 0, "other NULL"); torture_assert(tctx, strncasecmp_m(NULL, NULL, 3) == 0, "both NULL"); + torture_assert(tctx, strncasecmp_m(file_iso8859_1, file_utf8, 6) != 0, + "file.{accented e} should differ"); return true; } diff --git a/lib/util/charset/util_str.c b/lib/util/charset/util_str.c index 688ab5a0a1c..d2e6cbbc620 100644 --- a/lib/util/charset/util_str.c +++ b/lib/util/charset/util_str.c @@ -47,6 +47,11 @@ _PUBLIC_ int strcasecmp_m_handle(struct smb_iconv_handle *iconv_handle, c1 = next_codepoint_handle(iconv_handle, s1, &size1); c2 = next_codepoint_handle(iconv_handle, s2, &size2); + if (c1 == INVALID_CODEPOINT || + c2 == INVALID_CODEPOINT) { + return strcasecmp(s1, s2); + } + s1 += size1; s2 += size2; @@ -54,12 +59,6 @@ _PUBLIC_ int strcasecmp_m_handle(struct smb_iconv_handle *iconv_handle, continue; } - if (c1 == INVALID_CODEPOINT || - c2 == INVALID_CODEPOINT) { - /* what else can we do?? */ - return strcasecmp(s1, s2); - } - if (toupper_m(c1) != toupper_m(c2)) { return c1 - c2; } @@ -97,6 +96,26 @@ _PUBLIC_ int strncasecmp_m_handle(struct smb_iconv_handle *iconv_handle, c1 = next_codepoint_handle(iconv_handle, s1, &size1); c2 = next_codepoint_handle(iconv_handle, s2, &size2); + if (c1 == INVALID_CODEPOINT || + c2 == INVALID_CODEPOINT) { + /* + * n was specified in characters, + * now we must convert it to bytes. + * As bytes are the smallest + * character unit, the following + * increment and strncasecmp is always + * safe. + * + * The source string was already known + * to be n characters long, so we are + * guaranteed to be able to look at the + * (n remaining + size1) bytes from the + * s1 position). + */ + n += size1; + return strncasecmp(s1, s2, n); + } + s1 += size1; s2 += size2; @@ -104,12 +123,6 @@ _PUBLIC_ int strncasecmp_m_handle(struct smb_iconv_handle *iconv_handle, continue; } - if (c1 == INVALID_CODEPOINT || - c2 == INVALID_CODEPOINT) { - /* what else can we do?? */ - return strcasecmp(s1, s2); - } - if (toupper_m(c1) != toupper_m(c2)) { return c1 - c2; } diff --git a/source3/lib/memcache.c b/lib/util/memcache.c index 88453f32dd8..50e59fc7040 100644 --- a/source3/lib/memcache.c +++ b/lib/util/memcache.c @@ -17,8 +17,13 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#include "memcache.h" +#include "replace.h" +#include <talloc.h> +#include "../lib/util/samba_util.h" +#include "../lib/util/debug.h" +#include "../lib/util/dlinklist.h" #include "../lib/util/rbtree.h" +#include "memcache.h" static struct memcache *global_cache; @@ -26,7 +31,7 @@ struct memcache_element { struct rb_node rb_node; struct memcache_element *prev, *next; size_t keylength, valuelength; - uint8 n; /* This is really an enum, but save memory */ + uint8_t n; /* This is really an enum, but save memory */ char data[1]; /* placeholder for offsetof */ }; @@ -63,7 +68,7 @@ static int memcache_destructor(struct memcache *cache) { for (e = cache->mru; e != NULL; e = next) { next = e->next; - SAFE_FREE(e); + TALLOC_FREE(e); } return 0; } @@ -96,7 +101,7 @@ static struct memcache_element *memcache_node2elem(struct rb_node *node) static void memcache_element_parse(struct memcache_element *e, DATA_BLOB *key, DATA_BLOB *value) { - key->data = ((uint8 *)e) + offsetof(struct memcache_element, data); + key->data = ((uint8_t *)e) + offsetof(struct memcache_element, data); key->length = e->keylength; value->data = key->data + e->keylength; value->length = e->valuelength; @@ -206,7 +211,7 @@ static void memcache_delete_element(struct memcache *cache, cache->size -= memcache_element_size(e->keylength, e->valuelength); - SAFE_FREE(e); + TALLOC_FREE(e); } static void memcache_trim(struct memcache *cache) @@ -285,13 +290,12 @@ void memcache_add(struct memcache *cache, enum memcache_number n, element_size = memcache_element_size(key.length, value.length); - - e = (struct memcache_element *)SMB_MALLOC(element_size); - + e = talloc_size(cache, element_size); if (e == NULL) { - DEBUG(0, ("malloc failed\n")); + DEBUG(0, ("talloc failed\n")); return; } + talloc_set_type(e, struct memcache_element); e->n = n; e->keylength = key.length; diff --git a/source3/include/memcache.h b/lib/util/memcache.h index 9362483cea4..11e59718c9f 100644 --- a/source3/include/memcache.h +++ b/lib/util/memcache.h @@ -20,8 +20,6 @@ #ifndef __MEMCACHE_H__ #define __MEMCACHE_H__ -#include "includes.h" - struct memcache; /* diff --git a/lib/util/select.c b/lib/util/select.c index 5e66344c9da..99cd772bae8 100644 --- a/lib/util/select.c +++ b/lib/util/select.c @@ -42,9 +42,19 @@ int sys_poll_intr(struct pollfd *fds, int num_fds, int timeout) if (errno != EINTR) { break; } + /* Infinite timeout, no need to adjust. */ + if (timeout < 0) { + continue; + } clock_gettime_mono(&now); - elapsed = nsec_time_diff(&now, &start); - timeout = (orig_timeout - elapsed) / 1000000; + elapsed = nsec_time_diff(&now, &start) / 1000000; + timeout = orig_timeout - elapsed; + /* Unlikely, but might happen eg. when getting traced. + * Make sure we're not hanging in this case. + */ + if (timeout < 0) { + timeout = 0; + } }; return ret; } diff --git a/lib/util/string_wrappers.h b/lib/util/string_wrappers.h index 243fafc27ef..fcc088ca040 100644 --- a/lib/util/string_wrappers.h +++ b/lib/util/string_wrappers.h @@ -43,11 +43,6 @@ do { \ const char *_fstrcat_src = (const char *)(s); \ strlcat((d),_fstrcat_src ? _fstrcat_src : "",sizeof(fstring)); \ } while (0) -#define nstrcpy(d,s) \ -do { \ - const char *_nstrcpy_src = (const char *)(s); \ - strlcpy((d),_nstrcpy_src ? _nstrcpy_src : "",sizeof(fstring)); \ -} while (0) #define unstrcpy(d,s) \ do { \ const char *_unstrcpy_src = (const char *)(s); \ diff --git a/lib/util/wscript_build b/lib/util/wscript_build index 5087116174a..f161f963512 100755 --- a/lib/util/wscript_build +++ b/lib/util/wscript_build @@ -8,7 +8,7 @@ bld.SAMBA_LIBRARY('samba-util', util_strlist.c util_paths.c idtree.c debug.c fault.c base64.c util_str.c util_str_common.c substitute.c ms_fnmatch.c server_id.c dprintf.c parmlist.c bitmap.c pidfile.c - tevent_debug.c util_process.c''', + tevent_debug.c util_process.c memcache.c''', deps='DYNCONFIG', public_deps='talloc tevent execinfo uid_wrapper pthread LIBCRYPTO charset util_setid systemd-daemon', public_headers='debug.h attr.h byteorder.h data_blob.h memory.h safe_string.h time.h talloc_stack.h xfile.h dlinklist.h samba_util.h string_wrappers.h', diff --git a/libcli/auth/credentials.c b/libcli/auth/credentials.c index 7c8d53cf8d4..fb77ede197a 100644 --- a/libcli/auth/credentials.c +++ b/libcli/auth/credentials.c @@ -263,6 +263,7 @@ next comes the client specific functions struct netlogon_creds_CredentialState *netlogon_creds_client_init(TALLOC_CTX *mem_ctx, const char *client_account, const char *client_computer_name, + uint16_t secure_channel_type, const struct netr_Credential *client_challenge, const struct netr_Credential *server_challenge, const struct samr_Password *machine_password, @@ -277,6 +278,7 @@ struct netlogon_creds_CredentialState *netlogon_creds_client_init(TALLOC_CTX *me creds->sequence = time(NULL); creds->negotiate_flags = negotiate_flags; + creds->secure_channel_type = secure_channel_type; creds->computer_name = talloc_strdup(creds, client_computer_name); if (!creds->computer_name) { diff --git a/libcli/auth/proto.h b/libcli/auth/proto.h index 89a732e0525..6bc18d78205 100644 --- a/libcli/auth/proto.h +++ b/libcli/auth/proto.h @@ -26,6 +26,7 @@ next comes the client specific functions struct netlogon_creds_CredentialState *netlogon_creds_client_init(TALLOC_CTX *mem_ctx, const char *client_account, const char *client_computer_name, + uint16_t secure_channel_type, const struct netr_Credential *client_challenge, const struct netr_Credential *server_challenge, const struct samr_Password *machine_password, diff --git a/libcli/dns/dns_hosts_file.c b/libcli/dns/dns_hosts_file.c index 94d1d9704a3..4b1bc531675 100644 --- a/libcli/dns/dns_hosts_file.c +++ b/libcli/dns/dns_hosts_file.c @@ -88,9 +88,14 @@ static bool getdns_hosts_fileent(TALLOC_CTX *ctx, XFILE *fp, char **pp_name, cha if (next_token_talloc(ctx, &ptr, &name_type, NULL)) ++count; + if (count == 0) { + continue; + } if (next_token_talloc(ctx, &ptr, &name, NULL)) ++count; - if (name_type && strcasecmp(name_type, "A") == 0) { + if ((strcasecmp(name_type, "A") == 0) || + (strcasecmp(name_type, "AAAA") == 0)) + { if (next_token_talloc(ctx, &ptr, &ip, NULL)) ++count; } else if (name_type && strcasecmp(name_type, "SRV") == 0) { @@ -101,13 +106,18 @@ static bool getdns_hosts_fileent(TALLOC_CTX *ctx, XFILE *fp, char **pp_name, cha } else if (name_type && strcasecmp(name_type, "CNAME") == 0) { if (next_token_talloc(ctx, &ptr, &next_name, NULL)) ++count; + } else if (name_type && strcasecmp(name_type, "NS") == 0) { + if (next_token_talloc(ctx, &ptr, &next_name, NULL)) + ++count; } if (count <= 0) continue; - if (strcasecmp(name_type, "A") == 0) { + if ((strcasecmp(name_type, "A") == 0) || + (strcasecmp(name_type, "AAAA") == 0)) + { if (count != 3) { - DEBUG(0,("getdns_hosts_fileent: Ill formed hosts A record [%s]\n", + DEBUG(0,("getdns_hosts_fileent: Ill formed hosts A[AAA] record [%s]\n", line)); continue; } @@ -148,6 +158,15 @@ static bool getdns_hosts_fileent(TALLOC_CTX *ctx, XFILE *fp, char **pp_name, cha if (!*pp_next_name) { return false; } + } else if (strcasecmp(name_type, "NS") == 0) { + if (count != 3) { + DEBUG(0,("getdns_hosts_fileent: Ill formed hosts NS record [%s]\n", + line)); + continue; + } + DEBUG(4, ("getdns_hosts_fileent: NS entry: %s %s %s\n", + name_type, name, next_name)); + continue; } else { DEBUG(0,("getdns_hosts_fileent: unknown type %s\n", name_type)); continue; @@ -215,7 +234,7 @@ static NTSTATUS resolve_dns_hosts_file_as_dns_rr_recurse(const char *dns_hosts_f DEBUG(3,("resolve_dns_hosts: (%d) " "Attempting %s dns_hosts lookup for name %s\n", - level, srv_lookup ? "SRV" : "A", name)); + level, srv_lookup ? "SRV" : "A[AAA]", name)); fp = startdns_hosts_file(dns_hosts_file); @@ -278,7 +297,9 @@ static NTSTATUS resolve_dns_hosts_file_as_dns_rr_recurse(const char *dns_hosts_f mem_ctx, return_rr, return_count); talloc_free(ip_list_ctx); return status; - } else if (strcasecmp(name_type, "A") == 0) { + } else if ((strcasecmp(name_type, "A") == 0) || + (strcasecmp(name_type, "AAAA") == 0)) + { if (*return_count == 0) { /* We are happy to keep looking for other possible A record matches */ rr = talloc_zero(ip_list_ctx, @@ -405,11 +426,11 @@ NTSTATUS resolve_dns_hosts_file_as_dns_rr(const char *dns_hosts_file, if (NT_STATUS_IS_OK(status)) { DEBUG(3,("resolve_dns_hosts (dns_rr): " "Found %d %s result records for for name %s\n", - *return_count, srv_lookup ? "SRV" : "A", name)); + *return_count, srv_lookup ? "SRV" : "A[AAA]", name)); } else { DEBUG(3,("resolve_dns_hosts (dns_rr): " "failed to obtain %s result records for for name %s: %s\n", - srv_lookup ? "SRV" : "A", name, nt_errstr(status))); + srv_lookup ? "SRV" : "A[AAA]", name, nt_errstr(status))); } return status; } diff --git a/libcli/security/secdesc.c b/libcli/security/secdesc.c index 8570334f36c..62207a00845 100644 --- a/libcli/security/secdesc.c +++ b/libcli/security/secdesc.c @@ -24,13 +24,6 @@ #include "librpc/gen_ndr/ndr_security.h" #include "libcli/security/security.h" -#define ALL_SECURITY_INFORMATION (SECINFO_OWNER|SECINFO_GROUP|\ - SECINFO_DACL|SECINFO_SACL|\ - SECINFO_UNPROTECTED_SACL|\ - SECINFO_UNPROTECTED_DACL|\ - SECINFO_PROTECTED_SACL|\ - SECINFO_PROTECTED_DACL) - /* Map generic permissions to file object specific permissions */ const struct generic_mapping file_generic_mapping = { @@ -46,21 +39,32 @@ const struct generic_mapping file_generic_mapping = { uint32_t get_sec_info(const struct security_descriptor *sd) { - uint32_t sec_info = ALL_SECURITY_INFORMATION; + uint32_t sec_info = 0; SMB_ASSERT(sd); - if (sd->owner_sid == NULL) { - sec_info &= ~SECINFO_OWNER; + if (sd->owner_sid != NULL) { + sec_info |= SECINFO_OWNER; + } + if (sd->group_sid != NULL) { + sec_info |= SECINFO_GROUP; } - if (sd->group_sid == NULL) { - sec_info &= ~SECINFO_GROUP; + if (sd->sacl != NULL) { + sec_info |= SECINFO_SACL; } - if (sd->sacl == NULL) { - sec_info &= ~SECINFO_SACL; + if (sd->dacl != NULL) { + sec_info |= SECINFO_DACL; + } + + if (sd->type & SEC_DESC_SACL_PROTECTED) { + sec_info |= SECINFO_PROTECTED_SACL; + } else if (sd->type & SEC_DESC_SACL_AUTO_INHERITED) { + sec_info |= SECINFO_UNPROTECTED_SACL; } - if (sd->dacl == NULL) { - sec_info &= ~SECINFO_DACL; + if (sd->type & SEC_DESC_DACL_PROTECTED) { + sec_info |= SECINFO_PROTECTED_DACL; + } else if (sd->type & SEC_DESC_DACL_AUTO_INHERITED) { + sec_info |= SECINFO_UNPROTECTED_DACL; } return sec_info; diff --git a/librpc/idl/security.idl b/librpc/idl/security.idl index 381d6e5632e..eb80a869b0d 100644 --- a/librpc/idl/security.idl +++ b/librpc/idl/security.idl @@ -630,6 +630,24 @@ interface security SECINFO_PROTECTED_DACL = 0x80000000 } security_secinfo; + /* + * a SMB server should only support the following flags + * and ignore all others. + * + * See AdditionalInformation in [MS-SMB2] 2.2.37 SMB2 QUERY_INFO Request + * and 2.2.39 SMB2 SET_INFO Request. + */ + const int SMB_SUPPORTED_SECINFO_FLAGS = ( + SECINFO_OWNER | + SECINFO_GROUP | + SECINFO_DACL | + SECINFO_SACL | + SECINFO_LABEL | + SECINFO_ATTRIBUTE | + SECINFO_SCOPE | + SECINFO_BACKUP | + 0); + typedef [public,bitmap32bit] bitmap { KERB_ENCTYPE_DES_CBC_CRC = 0x00000001, KERB_ENCTYPE_DES_CBC_MD5 = 0x00000002, diff --git a/python/samba/join.py b/python/samba/join.py index 7d2f913b571..ee973a1bd8c 100644 --- a/python/samba/join.py +++ b/python/samba/join.py @@ -133,7 +133,7 @@ class dc_join(object): else: ctx.dns_backend = dns_backend - ctx.dnshostname = "%s.%s" % (ctx.myname, ctx.dnsdomain) + ctx.dnshostname = "%s.%s" % (ctx.myname.lower(), ctx.dnsdomain) ctx.realm = ctx.dnsdomain @@ -1198,7 +1198,7 @@ def join_subdomain(server=None, creds=None, lp=None, site=None, ctx.base_dn = samba.dn_from_dns_name(dnsdomain) ctx.domsid = str(security.random_sid()) ctx.acct_dn = None - ctx.dnshostname = "%s.%s" % (ctx.myname, ctx.dnsdomain) + ctx.dnshostname = "%s.%s" % (ctx.myname.lower(), ctx.dnsdomain) ctx.trustdom_pass = samba.generate_random_password(128, 128) ctx.userAccountControl = samba.dsdb.UF_SERVER_TRUST_ACCOUNT | samba.dsdb.UF_TRUSTED_FOR_DELEGATION diff --git a/python/samba/provision/sambadns.py b/python/samba/provision/sambadns.py index 53f10826080..ce4a46a8ecc 100644 --- a/python/samba/provision/sambadns.py +++ b/python/samba/provision/sambadns.py @@ -125,6 +125,7 @@ class SOARecord(dnsp.DnssrvRpcRecord): soa.expire = expire soa.mname = mname soa.rname = rname + soa.minimum = minimum self.data = soa diff --git a/python/samba/tests/dcerpc/dnsserver.py b/python/samba/tests/dcerpc/dnsserver.py index 59d6eee7618..e2c666727d0 100644 --- a/python/samba/tests/dcerpc/dnsserver.py +++ b/python/samba/tests/dcerpc/dnsserver.py @@ -19,7 +19,7 @@ from samba.dcerpc import dnsp, dnsserver from samba.tests import RpcInterfaceTestCase, env_get_var_value -from samba.netcmd.dns import ARecord +from samba.netcmd.dns import ARecord, NSRecord class DnsserverTests(RpcInterfaceTestCase): @@ -239,3 +239,42 @@ class DnsserverTests(RpcInterfaceTestCase): select_flags, None, None) + + def test_updaterecords2_soa(self): + client_version = dnsserver.DNS_CLIENT_VERSION_LONGHORN + record_type = dnsp.DNS_TYPE_NS + select_flags = (dnsserver.DNS_RPC_VIEW_AUTHORITY_DATA | + dnsserver.DNS_RPC_VIEW_NO_CHILDREN) + + nameserver = 'ns.example.local' + rec = NSRecord(nameserver) + + # Add record + add_rec_buf = dnsserver.DNS_RPC_RECORD_BUF() + add_rec_buf.rec = rec + self.conn.DnssrvUpdateRecord2(client_version, + 0, + self.server, + self.zone, + '.', + add_rec_buf, + None) + + buflen, result = self.conn.DnssrvEnumRecords2(client_version, + 0, + self.server, + self.zone, + '@', + None, + record_type, + select_flags, + None, + None) + self.assertEquals(1, result.count) + self.assertEquals(2, result.rec[0].wRecordCount) + match = False + for i in range(2): + self.assertEquals(dnsp.DNS_TYPE_NS, result.rec[0].records[i].wType) + if result.rec[0].records[i].data.str.rstrip('.') == nameserver: + match = True + self.assertEquals(match, True) diff --git a/python/samba/tests/dns.py b/python/samba/tests/dns.py index 79e4158b67b..2983de34ac5 100644 --- a/python/samba/tests/dns.py +++ b/python/samba/tests/dns.py @@ -289,6 +289,7 @@ class TestSimpleQueries(DNSTest): self.assert_dns_rcode_equals(response, dns.DNS_RCODE_OK) self.assert_dns_opcode_equals(response, dns.DNS_OPCODE_QUERY) self.assertEquals(response.ancount, 1) + self.assertEquals(response.answers[0].rdata.minimum, 3600) class TestDNSUpdates(DNSTest): diff --git a/selftest/README b/selftest/README index c23d730d640..d9ad0202683 100644 --- a/selftest/README +++ b/selftest/README @@ -89,6 +89,7 @@ The environments are currently available include * REALM: Realm name * SERVER: DC host name * SERVER_IP: DC IPv4 address + * SERVER_IPV6: DC IPv6 address * NETBIOSNAME: DC NetBIOS name * NETIOSALIAS: DC NetBIOS alias diff --git a/selftest/knownfail b/selftest/knownfail index 3768f8233fc..c493dbac4ed 100644 --- a/selftest/knownfail +++ b/selftest/knownfail @@ -106,6 +106,7 @@ ^samba4.rpc.netlogon.*.GetTrustPasswords ^samba4.rpc.netlogon.*.DatabaseRedo ^samba4.rpc.netlogon.*.ServerGetTrustInfo +^samba4.rpc.netlogon.*.invalidAuthenticate2 ^samba4.rpc.samr.passwords.badpwdcount # Not provided by Samba 4 yet ^samba4.rpc.samr.passwords.lockout ^samba4.base.charset.*.Testing partial surrogate diff --git a/selftest/selftest.pl b/selftest/selftest.pl index 2b1e3ce4b08..25e0f936e04 100755 --- a/selftest/selftest.pl +++ b/selftest/selftest.pl @@ -663,35 +663,41 @@ my @exported_envvars = ( # domain controller stuff "DC_SERVER", "DC_SERVER_IP", + "DC_SERVER_IPV6", "DC_NETBIOSNAME", "DC_NETBIOSALIAS", # domain member "MEMBER_SERVER", "MEMBER_SERVER_IP", + "MEMBER_SERVER_IPV6", "MEMBER_NETBIOSNAME", "MEMBER_NETBIOSALIAS", # rpc proxy controller stuff "RPC_PROXY_SERVER", "RPC_PROXY_SERVER_IP", + "RPC_PROXY_SERVER_IPV6", "RPC_PROXY_NETBIOSNAME", "RPC_PROXY_NETBIOSALIAS", # domain controller stuff for Vampired DC "VAMPIRE_DC_SERVER", "VAMPIRE_DC_SERVER_IP", + "VAMPIRE_DC_SERVER_IPV6", "VAMPIRE_DC_NETBIOSNAME", "VAMPIRE_DC_NETBIOSALIAS", "PROMOTED_DC_SERVER", "PROMOTED_DC_SERVER_IP", + "PROMOTED_DC_SERVER_IPV6", "PROMOTED_DC_NETBIOSNAME", "PROMOTED_DC_NETBIOSALIAS", # server stuff "SERVER", "SERVER_IP", + "SERVER_IPV6", "NETBIOSNAME", "NETBIOSALIAS", diff --git a/selftest/target/Samba3.pm b/selftest/target/Samba3.pm index bc07403dcc4..ba01154e524 100755 --- a/selftest/target/Samba3.pm +++ b/selftest/target/Samba3.pm @@ -207,6 +207,7 @@ sub setup_s3dc($$) $vars->{DC_SERVER} = $vars->{SERVER}; $vars->{DC_SERVER_IP} = $vars->{SERVER_IP}; + $vars->{DC_SERVER_IPV6} = $vars->{SERVER_IPV6}; $vars->{DC_NETBIOSNAME} = $vars->{NETBIOSNAME}; $vars->{DC_USERNAME} = $vars->{USERNAME}; $vars->{DC_PASSWORD} = $vars->{PASSWORD}; @@ -250,6 +251,7 @@ sub setup_member($$$) $ret->{DC_SERVER} = $s3dcvars->{SERVER}; $ret->{DC_SERVER_IP} = $s3dcvars->{SERVER_IP}; + $ret->{DC_SERVER_IPV6} = $s3dcvars->{SERVER_IPV6}; $ret->{DC_NETBIOSNAME} = $s3dcvars->{NETBIOSNAME}; $ret->{DC_USERNAME} = $s3dcvars->{USERNAME}; $ret->{DC_PASSWORD} = $s3dcvars->{PASSWORD}; @@ -321,6 +323,7 @@ sub setup_admember($$$$) $ret->{DC_SERVER} = $dcvars->{SERVER}; $ret->{DC_SERVER_IP} = $dcvars->{SERVER_IP}; + $ret->{DC_SERVER_IPV6} = $dcvars->{SERVER_IPV6}; $ret->{DC_NETBIOSNAME} = $dcvars->{NETBIOSNAME}; $ret->{DC_USERNAME} = $dcvars->{USERNAME}; $ret->{DC_PASSWORD} = $dcvars->{PASSWORD}; @@ -400,6 +403,7 @@ sub setup_admember_rfc2307($$$$) $ret->{DC_SERVER} = $dcvars->{SERVER}; $ret->{DC_SERVER_IP} = $dcvars->{SERVER_IP}; + $ret->{DC_SERVER_IPV6} = $dcvars->{SERVER_IPV6}; $ret->{DC_NETBIOSNAME} = $dcvars->{NETBIOSNAME}; $ret->{DC_USERNAME} = $dcvars->{USERNAME}; $ret->{DC_PASSWORD} = $dcvars->{PASSWORD}; @@ -770,6 +774,7 @@ sub provision($$$$$$) my $swiface = Samba::get_interface($server); my %ret = (); my $server_ip = "127.0.0.$swiface"; + my $server_ipv6 = sprintf("fd00:0000:0000:0000:0000:0000:5357:5f%02x", $swiface); my $domain = "SAMBA-TEST"; my $unix_name = ($ENV{USER} or $ENV{LOGNAME} or `PATH=/usr/ucb:$ENV{PATH} whoami`); @@ -872,7 +877,7 @@ sub provision($$$$$$) close(MSDFS_TARGET); chmod 0666, $msdfs_target; symlink "msdfs:$server_ip\\ro-tmp", "$msdfs_shrdir/msdfs-src1"; - symlink "msdfs:$server_ip\\ro-tmp", "$msdfs_shrdir/deeppath/msdfs-src2"; + symlink "msdfs:$server_ipv6\\ro-tmp", "$msdfs_shrdir/deeppath/msdfs-src2"; my $conffile="$libdir/server.conf"; @@ -925,7 +930,7 @@ sub provision($$$$$$) print CONF " [global] netbios name = $server - interfaces = $server_ip/8 + interfaces = $server_ip/8 $server_ipv6/64 bind interfaces only = yes panic action = $self->{srcdir}/selftest/gdb_backtrace %d %\$(MAKE_TEST_BINARY) smbd:suicide mode = yes @@ -1176,14 +1181,16 @@ domadmins:X:$gid_domadmins: print "DONE\n"; open(DNS_UPDATE_LIST, ">$prefix/dns_update_list") or die("Unable to open $$prefix/dns_update_list"); - print DNS_UPDATE_LIST "A $server. $server_ip"; + print DNS_UPDATE_LIST "A $server. $server_ip\n"; + print DNS_UPDATE_LIST "AAAA $server. $server_ipv6\n"; close(DNS_UPDATE_LIST); - if (system("$ENV{SRCDIR_ABS}/source4/scripting/bin/samba_dnsupdate --all-interfaces --use-file=$dns_host_file -s $conffile --update-list=$prefix/dns_update_list --no-substiutions --no-credentials") != 0) { + if (system("$ENV{SRCDIR_ABS}/source4/scripting/bin/samba_dnsupdate --all-interfaces --use-file=$dns_host_file -s $conffile --update-list=$prefix/dns_update_list --update-cache=$prefix/dns_update_cache --no-substiutions --no-credentials") != 0) { die "Unable to update hostname into $dns_host_file"; } $ret{SERVER_IP} = $server_ip; + $ret{SERVER_IPV6} = $server_ipv6; $ret{NMBD_TEST_LOG} = "$prefix/nmbd_test.log"; $ret{NMBD_TEST_LOG_POS} = 0; $ret{WINBINDD_TEST_LOG} = "$prefix/winbindd_test.log"; diff --git a/selftest/target/Samba4.pm b/selftest/target/Samba4.pm index 8a3f51d3614..ac2fdd97c5e 100644 --- a/selftest/target/Samba4.pm +++ b/selftest/target/Samba4.pm @@ -516,7 +516,8 @@ sub provision_raw_prepare($$$$$$$$$$) $ctx->{tlsdir} = "$ctx->{privatedir}/tls"; $ctx->{ipv4} = "127.0.0.$swiface"; - $ctx->{interfaces} = "$ctx->{ipv4}/8"; + $ctx->{ipv6} = sprintf("fd00:0000:0000:0000:0000:0000:5357:5f%02x", $swiface); + $ctx->{interfaces} = "$ctx->{ipv4}/8 $ctx->{ipv6}/64"; push(@{$ctx->{directories}}, $ctx->{privatedir}); push(@{$ctx->{directories}}, $ctx->{etcdir}); @@ -691,6 +692,7 @@ nogroup:x:65534:nobody PIDDIR => $ctx->{piddir}, SERVER => $ctx->{hostname}, SERVER_IP => $ctx->{ipv4}, + SERVER_IPV6 => $ctx->{ipv6}, NETBIOSNAME => $ctx->{netbiosname}, DOMAIN => $ctx->{domain}, USERNAME => $ctx->{username}, @@ -914,12 +916,14 @@ sub provision_member($$$) $ret->{MEMBER_SERVER} = $ret->{SERVER}; $ret->{MEMBER_SERVER_IP} = $ret->{SERVER_IP}; + $ret->{MEMBER_SERVER_IPV6} = $ret->{SERVER_IPV6}; $ret->{MEMBER_NETBIOSNAME} = $ret->{NETBIOSNAME}; $ret->{MEMBER_USERNAME} = $ret->{USERNAME}; $ret->{MEMBER_PASSWORD} = $ret->{PASSWORD}; $ret->{DC_SERVER} = $dcvars->{DC_SERVER}; $ret->{DC_SERVER_IP} = $dcvars->{DC_SERVER_IP}; + $ret->{DC_SERVER_IPV6} = $dcvars->{DC_SERVER_IPV6}; $ret->{DC_NETBIOSNAME} = $dcvars->{DC_NETBIOSNAME}; $ret->{DC_USERNAME} = $dcvars->{DC_USERNAME}; $ret->{DC_PASSWORD} = $dcvars->{DC_PASSWORD}; @@ -1006,12 +1010,14 @@ sub provision_rpc_proxy($$$) $ret->{RPC_PROXY_SERVER} = $ret->{SERVER}; $ret->{RPC_PROXY_SERVER_IP} = $ret->{SERVER_IP}; + $ret->{RPC_PROXY_SERVER_IPV6} = $ret->{SERVER_IPV6}; $ret->{RPC_PROXY_NETBIOSNAME} = $ret->{NETBIOSNAME}; $ret->{RPC_PROXY_USERNAME} = $ret->{USERNAME}; $ret->{RPC_PROXY_PASSWORD} = $ret->{PASSWORD}; $ret->{DC_SERVER} = $dcvars->{DC_SERVER}; $ret->{DC_SERVER_IP} = $dcvars->{DC_SERVER_IP}; + $ret->{DC_SERVER_IPV6} = $dcvars->{DC_SERVER_IPV6}; $ret->{DC_NETBIOSNAME} = $dcvars->{DC_NETBIOSNAME}; $ret->{DC_USERNAME} = $dcvars->{DC_USERNAME}; $ret->{DC_PASSWORD} = $dcvars->{DC_PASSWORD}; @@ -1082,10 +1088,12 @@ sub provision_promoted_dc($$$) $ret->{PROMOTED_DC_SERVER} = $ret->{SERVER}; $ret->{PROMOTED_DC_SERVER_IP} = $ret->{SERVER_IP}; + $ret->{PROMOTED_DC_SERVER_IPV6} = $ret->{SERVER_IPV6}; $ret->{PROMOTED_DC_NETBIOSNAME} = $ret->{NETBIOSNAME}; $ret->{DC_SERVER} = $dcvars->{DC_SERVER}; $ret->{DC_SERVER_IP} = $dcvars->{DC_SERVER_IP}; + $ret->{DC_SERVER_IPV6} = $dcvars->{DC_SERVER_IPV6}; $ret->{DC_NETBIOSNAME} = $dcvars->{DC_NETBIOSNAME}; $ret->{DC_USERNAME} = $dcvars->{DC_USERNAME}; $ret->{DC_PASSWORD} = $dcvars->{DC_PASSWORD}; @@ -1143,10 +1151,12 @@ sub provision_vampire_dc($$$) $ret->{VAMPIRE_DC_SERVER} = $ret->{SERVER}; $ret->{VAMPIRE_DC_SERVER_IP} = $ret->{SERVER_IP}; + $ret->{VAMPIRE_DC_SERVER_IPV6} = $ret->{SERVER_IPV6}; $ret->{VAMPIRE_DC_NETBIOSNAME} = $ret->{NETBIOSNAME}; $ret->{DC_SERVER} = $dcvars->{DC_SERVER}; $ret->{DC_SERVER_IP} = $dcvars->{DC_SERVER_IP}; + $ret->{DC_SERVER_IPV6} = $dcvars->{DC_SERVER_IPV6}; $ret->{DC_NETBIOSNAME} = $dcvars->{DC_NETBIOSNAME}; $ret->{DC_USERNAME} = $dcvars->{DC_USERNAME}; $ret->{DC_PASSWORD} = $dcvars->{DC_PASSWORD}; @@ -1208,10 +1218,12 @@ sub provision_subdom_dc($$$) $ret->{SUBDOM_DC_SERVER} = $ret->{SERVER}; $ret->{SUBDOM_DC_SERVER_IP} = $ret->{SERVER_IP}; + $ret->{SUBDOM_DC_SERVER_IPV6} = $ret->{SERVER_IPV6}; $ret->{SUBDOM_DC_NETBIOSNAME} = $ret->{NETBIOSNAME}; $ret->{DC_SERVER} = $dcvars->{DC_SERVER}; $ret->{DC_SERVER_IP} = $dcvars->{DC_SERVER_IP}; + $ret->{DC_SERVER_IPV6} = $dcvars->{DC_SERVER_IPV6}; $ret->{DC_NETBIOSNAME} = $dcvars->{DC_NETBIOSNAME}; $ret->{DC_USERNAME} = $dcvars->{DC_USERNAME}; $ret->{DC_PASSWORD} = $dcvars->{DC_PASSWORD}; @@ -1242,6 +1254,7 @@ sub provision_dc($$) $ret->{NETBIOSALIAS} = "localdc1-a"; $ret->{DC_SERVER} = $ret->{SERVER}; $ret->{DC_SERVER_IP} = $ret->{SERVER_IP}; + $ret->{DC_SERVER_IPV6} = $ret->{SERVER_IPV6}; $ret->{DC_NETBIOSNAME} = $ret->{NETBIOSNAME}; $ret->{DC_USERNAME} = $ret->{USERNAME}; $ret->{DC_PASSWORD} = $ret->{PASSWORD}; @@ -1385,10 +1398,12 @@ sub provision_rodc($$$) $ret->{RODC_DC_SERVER} = $ret->{SERVER}; $ret->{RODC_DC_SERVER_IP} = $ret->{SERVER_IP}; + $ret->{RODC_DC_SERVER_IPV6} = $ret->{SERVER_IPV6}; $ret->{RODC_DC_NETBIOSNAME} = $ret->{NETBIOSNAME}; $ret->{DC_SERVER} = $dcvars->{DC_SERVER}; $ret->{DC_SERVER_IP} = $dcvars->{DC_SERVER_IP}; + $ret->{DC_SERVER_IPV6} = $dcvars->{DC_SERVER_IPV6}; $ret->{DC_NETBIOSNAME} = $dcvars->{DC_NETBIOSNAME}; $ret->{DC_USERNAME} = $dcvars->{DC_USERNAME}; $ret->{DC_PASSWORD} = $dcvars->{DC_PASSWORD}; @@ -1503,6 +1518,7 @@ sub provision_plugin_s4_dc($$) $ret->{DC_SERVER} = $ret->{SERVER}; $ret->{DC_SERVER_IP} = $ret->{SERVER_IP}; + $ret->{DC_SERVER_IPV6} = $ret->{SERVER_IPV6}; $ret->{DC_NETBIOSNAME} = $ret->{NETBIOSNAME}; $ret->{DC_USERNAME} = $ret->{USERNAME}; $ret->{DC_PASSWORD} = $ret->{PASSWORD}; @@ -1542,6 +1558,7 @@ sub provision_chgdcpass($$) $ret->{DC_SERVER} = $ret->{SERVER}; $ret->{DC_SERVER_IP} = $ret->{SERVER_IP}; + $ret->{DC_SERVER_IPV6} = $ret->{SERVER_IPV6}; $ret->{DC_NETBIOSNAME} = $ret->{NETBIOSNAME}; $ret->{DC_USERNAME} = $ret->{USERNAME}; $ret->{DC_PASSWORD} = $ret->{PASSWORD}; diff --git a/source3/auth/token_util.c b/source3/auth/token_util.c index be44ce949d9..039387f4232 100644 --- a/source3/auth/token_util.c +++ b/source3/auth/token_util.c @@ -28,7 +28,7 @@ #include "system/passwd.h" #include "auth.h" #include "secrets.h" -#include "memcache.h" +#include "../lib/util/memcache.h" #include "../librpc/gen_ndr/netlogon.h" #include "../libcli/security/security.h" #include "../lib/util/util_pw.h" diff --git a/source3/include/includes.h b/source3/include/includes.h index 1b22a5770cc..e8434f2b2ef 100644 --- a/source3/include/includes.h +++ b/source3/include/includes.h @@ -322,14 +322,6 @@ struct stat_ex { uint32_t st_ex_flags; uint32_t st_ex_mask; - - /* - * Add space for VFS internal extensions. The initial user of this - * would be the onefs modules, passing the snapid from the stat calls - * to the file_id_create call. Maybe we'll have to expand this later, - * but the core of Samba should never look at this field. - */ - uint64_t vfs_private; }; typedef struct stat_ex SMB_STRUCT_STAT; diff --git a/source3/include/proto.h b/source3/include/proto.h index a42faf8148b..cbad7ac36a2 100644 --- a/source3/include/proto.h +++ b/source3/include/proto.h @@ -1357,6 +1357,7 @@ int lp_smb_encrypt(int ); char lp_magicchar(const struct share_params *p ); int lp_winbind_cache_time(void); int lp_winbind_reconnect_delay(void); +int lp_winbind_request_timeout(void); int lp_winbind_max_clients(void); const char **lp_winbind_nss_info(void); int lp_algorithmic_rid_base(void); diff --git a/source3/lib/access.c b/source3/lib/access.c index 044c079cc5e..b664dc83c56 100644 --- a/source3/lib/access.c +++ b/source3/lib/access.c @@ -11,7 +11,7 @@ */ #include "includes.h" -#include "memcache.h" +#include "../lib/util/memcache.h" #include "lib/socket/interfaces.h" #define NAME_INDEX 0 diff --git a/source3/lib/id_cache.c b/source3/lib/id_cache.c index e6e345741c1..3a703ae4af3 100644 --- a/source3/lib/id_cache.c +++ b/source3/lib/id_cache.c @@ -28,7 +28,7 @@ #include "includes.h" #include "messages.h" #include "lib/id_cache.h" -#include "include/memcache.h" +#include "../lib/util/memcache.h" #include "idmap_cache.h" #include "../librpc/gen_ndr/ndr_security.h" #include "../libcli/security/dom_sid.h" diff --git a/source3/lib/username.c b/source3/lib/username.c index 665fbb42536..d44db7518df 100644 --- a/source3/lib/username.c +++ b/source3/lib/username.c @@ -21,7 +21,7 @@ #include "includes.h" #include "system/passwd.h" -#include "memcache.h" +#include "../lib/util/memcache.h" #include "../lib/util/util_pw.h" /* internal functions */ diff --git a/source3/lib/util_sock.c b/source3/lib/util_sock.c index 54286b3f311..22ba764d13c 100644 --- a/source3/lib/util_sock.c +++ b/source3/lib/util_sock.c @@ -21,7 +21,7 @@ #include "includes.h" #include "system/filesys.h" -#include "memcache.h" +#include "../lib/util/memcache.h" #include "../lib/async_req/async_sock.h" #include "../lib/util/select.h" #include "lib/socket/interfaces.h" diff --git a/source3/librpc/idl/open_files.idl b/source3/librpc/idl/open_files.idl index 686bc02548a..42ecb02210b 100644 --- a/source3/librpc/idl/open_files.idl +++ b/source3/librpc/idl/open_files.idl @@ -77,7 +77,6 @@ interface open_files hyper st_ex_blocks; uint32 st_ex_flags; uint32 st_ex_mask; - hyper vfs_private; } vfs_default_durable_stat; typedef [public] struct { diff --git a/source3/libsmb/clidfs.c b/source3/libsmb/clidfs.c index ff48719c76b..840084f8b6d 100644 --- a/source3/libsmb/clidfs.c +++ b/source3/libsmb/clidfs.c @@ -280,13 +280,15 @@ static NTSTATUS do_connect(TALLOC_CTX *ctx, static void cli_set_mntpoint(struct cli_state *cli, const char *mnt) { - char *name = clean_name(NULL, mnt); + TALLOC_CTX *frame = talloc_stackframe(); + char *name = clean_name(frame, mnt); if (!name) { + TALLOC_FREE(frame); return; } TALLOC_FREE(cli->dfs_mountpoint); cli->dfs_mountpoint = talloc_strdup(cli, name); - TALLOC_FREE(name); + TALLOC_FREE(frame); } /******************************************************************** diff --git a/source3/libsmb/libsmb_server.c b/source3/libsmb/libsmb_server.c index 517753d5314..d4254dadac8 100644 --- a/source3/libsmb/libsmb_server.c +++ b/source3/libsmb/libsmb_server.c @@ -459,6 +459,11 @@ SMBC_server_internal(TALLOC_CTX *ctx, return NULL; } + if (smbXcli_conn_protocol(c->conn) >= PROTOCOL_SMB2_02) { + /* Ensure we ask for some initial credits. */ + smb2cli_conn_set_max_credits(c->conn, DEFAULT_SMB2_MAX_CREDITS); + } + username_used = *pp_username; if (!NT_STATUS_IS_OK(cli_session_setup(c, username_used, diff --git a/source3/modules/vfs_default.c b/source3/modules/vfs_default.c index 7fda4b82271..ed14c673b51 100644 --- a/source3/modules/vfs_default.c +++ b/source3/modules/vfs_default.c @@ -1530,6 +1530,18 @@ static uint64_t vfswrap_get_alloc_size(vfs_handle_struct *handle, #else #error SIZEOF_BLKCNT_T_NOT_A_SUPPORTED_VALUE #endif + if (result == 0) { + /* + * Some file systems do not allocate a block for very + * small files. But for non-empty file should report a + * positive size. + */ + + uint64_t filesize = get_file_size_stat(sbuf); + if (filesize > 0) { + result = MIN((uint64_t)STAT_ST_BLOCKSIZE, filesize); + } + } #else result = get_file_size_stat(sbuf); #endif diff --git a/source3/modules/vfs_dirsort.c b/source3/modules/vfs_dirsort.c index 98109c280bf..d6b33941fad 100644 --- a/source3/modules/vfs_dirsort.c +++ b/source3/modules/vfs_dirsort.c @@ -256,7 +256,7 @@ static void dirsort_seekdir(vfs_handle_struct *handle, DIR *dirp, if (data == NULL) { return; } - if (offset > data->number_of_entries) { + if (offset >= data->number_of_entries) { return; } data->pos = offset; diff --git a/source3/modules/vfs_gpfs.c b/source3/modules/vfs_gpfs.c index 4a53bf84e55..f9eb7e86b12 100644 --- a/source3/modules/vfs_gpfs.c +++ b/source3/modules/vfs_gpfs.c @@ -1444,7 +1444,6 @@ static int vfs_gpfs_stat(struct vfs_handle_struct *handle, smb_fname->st.st_ex_calculated_birthtime = false; smb_fname->st.st_ex_btime.tv_sec = attrs.creationTime.tv_sec; smb_fname->st.st_ex_btime.tv_nsec = attrs.creationTime.tv_nsec; - smb_fname->st.vfs_private = attrs.winAttrs; } return 0; } @@ -1512,7 +1511,6 @@ static int vfs_gpfs_lstat(struct vfs_handle_struct *handle, smb_fname->st.st_ex_calculated_birthtime = false; smb_fname->st.st_ex_btime.tv_sec = attrs.creationTime.tv_sec; smb_fname->st.st_ex_btime.tv_nsec = attrs.creationTime.tv_nsec; - smb_fname->st.vfs_private = attrs.winAttrs; } return 0; } @@ -1636,6 +1634,7 @@ static bool vfs_gpfs_is_offline(struct vfs_handle_struct *handle, char *path = NULL; NTSTATUS status; struct gpfs_config_data *config; + int ret; SMB_VFS_HANDLE_GET_DATA(handle, config, struct gpfs_config_data, @@ -1651,17 +1650,12 @@ static bool vfs_gpfs_is_offline(struct vfs_handle_struct *handle, return -1; } - if (VALID_STAT(*sbuf)) { - attrs.winAttrs = sbuf->vfs_private; - } else { - int ret; - ret = get_gpfs_winattrs(path, &attrs); - - if (ret == -1) { - TALLOC_FREE(path); - return false; - } + ret = get_gpfs_winattrs(path, &attrs); + if (ret == -1) { + TALLOC_FREE(path); + return false; } + if ((attrs.winAttrs & GPFS_WINATTR_OFFLINE) != 0) { DEBUG(10, ("%s is offline\n", path)); TALLOC_FREE(path); @@ -1682,7 +1676,8 @@ static ssize_t vfs_gpfs_sendfile(vfs_handle_struct *handle, int tofd, files_struct *fsp, const DATA_BLOB *hdr, off_t offset, size_t n) { - if ((fsp->fsp_name->st.vfs_private & GPFS_WINATTR_OFFLINE) != 0) { + if (SMB_VFS_IS_OFFLINE(handle->conn, fsp->fsp_name, &fsp->fsp_name->st)) + { errno = ENOSYS; return -1; } @@ -1953,15 +1948,14 @@ static ssize_t vfs_gpfs_pread(vfs_handle_struct *handle, files_struct *fsp, void *data, size_t n, off_t offset) { ssize_t ret; + bool was_offline; - ret = SMB_VFS_NEXT_PREAD(handle, fsp, data, n, offset); + was_offline = SMB_VFS_IS_OFFLINE(handle->conn, fsp->fsp_name, + &fsp->fsp_name->st); - DEBUG(10, ("vfs_private = %x\n", - (unsigned int)fsp->fsp_name->st.vfs_private)); + ret = SMB_VFS_NEXT_PREAD(handle, fsp, data, n, offset); - if ((ret != -1) && - ((fsp->fsp_name->st.vfs_private & GPFS_WINATTR_OFFLINE) != 0)) { - fsp->fsp_name->st.vfs_private &= ~GPFS_WINATTR_OFFLINE; + if ((ret != -1) && was_offline) { notify_fname(handle->conn, NOTIFY_ACTION_MODIFIED, FILE_NOTIFY_CHANGE_ATTRIBUTES, fsp->fsp_name->base_name); @@ -1974,6 +1968,7 @@ struct vfs_gpfs_pread_state { struct files_struct *fsp; ssize_t ret; int err; + bool was_offline; }; static void vfs_gpfs_pread_done(struct tevent_req *subreq); @@ -1992,6 +1987,8 @@ static struct tevent_req *vfs_gpfs_pread_send(struct vfs_handle_struct *handle, if (req == NULL) { return NULL; } + state->was_offline = SMB_VFS_IS_OFFLINE(handle->conn, fsp->fsp_name, + &fsp->fsp_name->st); state->fsp = fsp; subreq = SMB_VFS_NEXT_PREAD_SEND(state, ev, handle, fsp, data, n, offset); @@ -2025,12 +2022,7 @@ static ssize_t vfs_gpfs_pread_recv(struct tevent_req *req, int *err) } *err = state->err; - DEBUG(10, ("vfs_private = %x\n", - (unsigned int)fsp->fsp_name->st.vfs_private)); - - if ((state->ret != -1) && - ((fsp->fsp_name->st.vfs_private & GPFS_WINATTR_OFFLINE) != 0)) { - fsp->fsp_name->st.vfs_private &= ~GPFS_WINATTR_OFFLINE; + if ((state->ret != -1) && state->was_offline) { DEBUG(10, ("sending notify\n")); notify_fname(fsp->conn, NOTIFY_ACTION_MODIFIED, FILE_NOTIFY_CHANGE_ATTRIBUTES, @@ -2044,15 +2036,14 @@ static ssize_t vfs_gpfs_pwrite(vfs_handle_struct *handle, files_struct *fsp, const void *data, size_t n, off_t offset) { ssize_t ret; + bool was_offline; - ret = SMB_VFS_NEXT_PWRITE(handle, fsp, data, n, offset); + was_offline = SMB_VFS_IS_OFFLINE(handle->conn, fsp->fsp_name, + &fsp->fsp_name->st); - DEBUG(10, ("vfs_private = %x\n", - (unsigned int)fsp->fsp_name->st.vfs_private)); + ret = SMB_VFS_NEXT_PWRITE(handle, fsp, data, n, offset); - if ((ret != -1) && - ((fsp->fsp_name->st.vfs_private & GPFS_WINATTR_OFFLINE) != 0)) { - fsp->fsp_name->st.vfs_private &= ~GPFS_WINATTR_OFFLINE; + if ((ret != -1) && was_offline) { notify_fname(handle->conn, NOTIFY_ACTION_MODIFIED, FILE_NOTIFY_CHANGE_ATTRIBUTES, fsp->fsp_name->base_name); @@ -2065,6 +2056,7 @@ struct vfs_gpfs_pwrite_state { struct files_struct *fsp; ssize_t ret; int err; + bool was_offline; }; static void vfs_gpfs_pwrite_done(struct tevent_req *subreq); @@ -2084,6 +2076,8 @@ static struct tevent_req *vfs_gpfs_pwrite_send( if (req == NULL) { return NULL; } + state->was_offline = SMB_VFS_IS_OFFLINE(handle->conn, fsp->fsp_name, + &fsp->fsp_name->st); state->fsp = fsp; subreq = SMB_VFS_NEXT_PWRITE_SEND(state, ev, handle, fsp, data, n, offset); @@ -2117,12 +2111,7 @@ static ssize_t vfs_gpfs_pwrite_recv(struct tevent_req *req, int *err) } *err = state->err; - DEBUG(10, ("vfs_private = %x\n", - (unsigned int)fsp->fsp_name->st.vfs_private)); - - if ((state->ret != -1) && - ((fsp->fsp_name->st.vfs_private & GPFS_WINATTR_OFFLINE) != 0)) { - fsp->fsp_name->st.vfs_private &= ~GPFS_WINATTR_OFFLINE; + if ((state->ret != -1) && state->was_offline) { DEBUG(10, ("sending notify\n")); notify_fname(fsp->conn, NOTIFY_ACTION_MODIFIED, FILE_NOTIFY_CHANGE_ATTRIBUTES, diff --git a/source3/param/loadparm.c b/source3/param/loadparm.c index a339a1db52e..4b31023cd7c 100644 --- a/source3/param/loadparm.c +++ b/source3/param/loadparm.c @@ -978,6 +978,7 @@ static void init_globals(bool reinit_globals) Globals.winbind_cache_time = 300; /* 5 minutes */ Globals.winbind_reconnect_delay = 30; /* 30 seconds */ + Globals.winbind_request_timeout = 60; /* 60 seconds */ Globals.winbind_max_clients = 200; Globals.bWinbindEnumUsers = false; Globals.bWinbindEnumGroups = false; diff --git a/source3/passdb/lookup_sid.c b/source3/passdb/lookup_sid.c index 6ec6ce8e47e..e4d41c44dcb 100644 --- a/source3/passdb/lookup_sid.c +++ b/source3/passdb/lookup_sid.c @@ -23,7 +23,7 @@ #include "passdb.h" #include "../librpc/gen_ndr/ndr_security.h" #include "secrets.h" -#include "memcache.h" +#include "../lib/util/memcache.h" #include "idmap_cache.h" #include "../libcli/security/security.h" #include "lib/winbind_util.h" diff --git a/source3/passdb/pdb_interface.c b/source3/passdb/pdb_interface.c index ea67e2faf51..d9e7bb531df 100644 --- a/source3/passdb/pdb_interface.c +++ b/source3/passdb/pdb_interface.c @@ -29,7 +29,7 @@ #include "../librpc/gen_ndr/drsblobs.h" #include "../librpc/gen_ndr/ndr_drsblobs.h" #include "../librpc/gen_ndr/idmap.h" -#include "memcache.h" +#include "../lib/util/memcache.h" #include "nsswitch/winbind_client.h" #include "../libcli/security/security.h" #include "../lib/util/util_pw.h" diff --git a/source3/passdb/pdb_samba_dsdb.c b/source3/passdb/pdb_samba_dsdb.c index 4cd7a4b6cdc..49b7cf0a63e 100644 --- a/source3/passdb/pdb_samba_dsdb.c +++ b/source3/passdb/pdb_samba_dsdb.c @@ -882,7 +882,7 @@ static NTSTATUS pdb_samba_dsdb_getgrfilter(struct pdb_methods *m, GROUP_MAP *map { struct pdb_samba_dsdb_state *state = talloc_get_type_abort( m->private_data, struct pdb_samba_dsdb_state); - const char *attrs[] = { "objectSid", "description", "samAccountName", "groupType", + const char *attrs[] = { "objectClass", "objectSid", "description", "samAccountName", "groupType", NULL }; struct ldb_message *msg; va_list ap; @@ -941,15 +941,13 @@ static NTSTATUS pdb_samba_dsdb_getgrfilter(struct pdb_methods *m, GROUP_MAP *map return NT_STATUS_INTERNAL_DB_CORRUPTION; } - map->sid_name_use = SID_NAME_DOM_GRP; - ZERO_STRUCT(id_map); id_map.sid = sid; id_maps[0] = &id_map; id_maps[1] = NULL; status = idmap_sids_to_xids(state->idmap_ctx, tmp_ctx, id_maps); - talloc_free(tmp_ctx); + if (!NT_STATUS_IS_OK(status)) { talloc_free(tmp_ctx); return status; diff --git a/source3/printing/load.c b/source3/printing/load.c index 136d055088f..238998d920d 100644 --- a/source3/printing/load.c +++ b/source3/printing/load.c @@ -65,11 +65,11 @@ load automatic printer services from pre-populated pcap cache void load_printers(struct tevent_context *ev, struct messaging_context *msg_ctx) { - SMB_ASSERT(pcap_cache_loaded()); + SMB_ASSERT(pcap_cache_loaded(NULL)); add_auto_printers(); /* load all printcap printers */ if (lp_load_printers() && lp_servicenumber(PRINTERS_NAME) >= 0) - pcap_printer_fn(lp_add_one_printer, NULL); + pcap_printer_read_fn(lp_add_one_printer, NULL); } diff --git a/source3/printing/pcap.c b/source3/printing/pcap.c index dd7ba624590..c5524ad53db 100644 --- a/source3/printing/pcap.c +++ b/source3/printing/pcap.c @@ -83,28 +83,26 @@ void pcap_cache_destroy_specific(struct pcap_cache **pp_cache) *pp_cache = NULL; } -bool pcap_cache_add(const char *name, const char *comment, const char *location) -{ - NTSTATUS status; - time_t t = time_mono(NULL); - - status = printer_list_set_printer(talloc_tos(), name, comment, location, t); - return NT_STATUS_IS_OK(status); -} - -bool pcap_cache_loaded(void) +bool pcap_cache_loaded(time_t *_last_change) { NTSTATUS status; time_t last; status = printer_list_get_last_refresh(&last); - return NT_STATUS_IS_OK(status); + if (!NT_STATUS_IS_OK(status)) { + return false; + } + if (_last_change != NULL) { + *_last_change = last; + } + return true; } bool pcap_cache_replace(const struct pcap_cache *pcache) { const struct pcap_cache *p; NTSTATUS status; + time_t t = time_mono(NULL); status = printer_list_mark_reload(); if (!NT_STATUS_IS_OK(status)) { @@ -113,7 +111,11 @@ bool pcap_cache_replace(const struct pcap_cache *pcache) } for (p = pcache; p; p = p->next) { - pcap_cache_add(p->name, p->comment, p->location); + status = printer_list_set_printer(talloc_tos(), p->name, + p->comment, p->location, t); + if (!NT_STATUS_IS_OK(status)) { + return false; + } } status = printer_list_clean_old(); @@ -132,8 +134,8 @@ void pcap_cache_reload(struct tevent_context *ev, { const char *pcap_name = lp_printcapname(); bool pcap_reloaded = False; - NTSTATUS status; bool post_cache_fill_fn_handled = false; + struct pcap_cache *pcache = NULL; DEBUG(3, ("reloading printcap cache\n")); @@ -143,12 +145,6 @@ void pcap_cache_reload(struct tevent_context *ev, return; } - status = printer_list_mark_reload(); - if (!NT_STATUS_IS_OK(status)) { - DEBUG(0, ("Failed to mark printer list for reload!\n")); - return; - } - #ifdef HAVE_CUPS if (strequal(pcap_name, "cups")) { pcap_reloaded = cups_cache_reload(ev, msg_ctx, @@ -164,26 +160,26 @@ void pcap_cache_reload(struct tevent_context *ev, #ifdef HAVE_IPRINT if (strequal(pcap_name, "iprint")) { - pcap_reloaded = iprint_cache_reload(); + pcap_reloaded = iprint_cache_reload(&pcache); goto done; } #endif #if defined(SYSV) || defined(HPUX) if (strequal(pcap_name, "lpstat")) { - pcap_reloaded = sysv_cache_reload(); + pcap_reloaded = sysv_cache_reload(&pcache); goto done; } #endif #ifdef AIX if (strstr_m(pcap_name, "/qconfig") != NULL) { - pcap_reloaded = aix_cache_reload(); + pcap_reloaded = aix_cache_reload(&pcache); goto done; } #endif - pcap_reloaded = std_pcap_cache_reload(pcap_name); + pcap_reloaded = std_pcap_cache_reload(pcap_name, &pcache); done: DEBUG(3, ("reload status: %s\n", (pcap_reloaded) ? "ok" : "error")); @@ -192,14 +188,16 @@ done: /* cleanup old entries only if the operation was successful, * otherwise keep around the old entries until we can * successfully reload */ - status = printer_list_clean_old(); - if (!NT_STATUS_IS_OK(status)) { - DEBUG(0, ("Failed to cleanup printer list!\n")); + + if (!pcap_cache_replace(pcache)) { + DEBUG(0, ("Failed to replace printer list!\n")); } + if (post_cache_fill_fn != NULL) { post_cache_fill_fn(ev, msg_ctx); } } + pcap_cache_destroy_specific(&pcache); return; } @@ -229,11 +227,11 @@ void pcap_printer_fn_specific(const struct pcap_cache *pc, return; } -void pcap_printer_fn(void (*fn)(const char *, const char *, const char *, void *), void *pdata) +void pcap_printer_read_fn(void (*fn)(const char *, const char *, const char *, void *), void *pdata) { NTSTATUS status; - status = printer_list_run_fn(fn, pdata); + status = printer_list_read_run_fn(fn, pdata); if (!NT_STATUS_IS_OK(status)) { DEBUG(3, ("Failed to run fn for all printers!\n")); } diff --git a/source3/printing/pcap.h b/source3/printing/pcap.h index 70562137ac8..8fc9e9de31c 100644 --- a/source3/printing/pcap.h +++ b/source3/printing/pcap.h @@ -35,11 +35,10 @@ struct pcap_cache; bool pcap_cache_add_specific(struct pcap_cache **ppcache, const char *name, const char *comment, const char *location); void pcap_cache_destroy_specific(struct pcap_cache **ppcache); -bool pcap_cache_add(const char *name, const char *comment, const char *location); -bool pcap_cache_loaded(void); +bool pcap_cache_loaded(time_t *_last_change); bool pcap_cache_replace(const struct pcap_cache *cache); void pcap_printer_fn_specific(const struct pcap_cache *, void (*fn)(const char *, const char *, const char *, void *), void *); -void pcap_printer_fn(void (*fn)(const char *, const char *, const char *, void *), void *); +void pcap_printer_read_fn(void (*fn)(const char *, const char *, const char *, void *), void *); void pcap_cache_reload(struct tevent_context *ev, struct messaging_context *msg_ctx, @@ -49,7 +48,7 @@ bool pcap_printername_ok(const char *printername); /* The following definitions come from printing/print_aix.c */ -bool aix_cache_reload(void); +bool aix_cache_reload(struct pcap_cache **_pcache); /* The following definitions come from printing/print_cups.c */ @@ -60,13 +59,13 @@ bool cups_cache_reload(struct tevent_context *ev, /* The following definitions come from printing/print_iprint.c */ -bool iprint_cache_reload(void); +bool iprint_cache_reload(struct pcap_cache **_pcache); /* The following definitions come from printing/print_svid.c */ -bool sysv_cache_reload(void); +bool sysv_cache_reload(struct pcap_cache **_pcache); /* The following definitions come from printing/print_standard.c */ -bool std_pcap_cache_reload(const char *pcap_name); +bool std_pcap_cache_reload(const char *pcap_name, struct pcap_cache **_pcache); #endif /* _PRINTING_PCAP_H_ */ diff --git a/source3/printing/print_aix.c b/source3/printing/print_aix.c index 23d9a86fe37..927a71b2948 100644 --- a/source3/printing/print_aix.c +++ b/source3/printing/print_aix.c @@ -29,12 +29,13 @@ #include "printing/pcap.h" #ifdef AIX -bool aix_cache_reload(void) +bool aix_cache_reload(struct pcap_cache **_pcache) { int iEtat; XFILE *pfile; char *line = NULL, *p; char *name = NULL; + struct pcap_cache *pcache = NULL; TALLOC_CTX *ctx = talloc_init("aix_cache_reload"); if (!ctx) { @@ -52,6 +53,8 @@ bool aix_cache_reload(void) iEtat = 0; /* scan qconfig file for searching <printername>: */ for (;(line = fgets_slash(NULL, 1024, pfile)); free(line)) { + bool ok; + if (*line == '*' || *line == 0) continue; @@ -67,6 +70,7 @@ bool aix_cache_reload(void) if (strcmp(p, "bsh") != 0) { name = talloc_strdup(ctx, p); if (!name) { + pcap_cache_destroy_specific(&pcache); SAFE_FREE(line); x_fclose(pfile); TALLOC_FREE(ctx); @@ -86,7 +90,10 @@ bool aix_cache_reload(void) /* name is found without stanza device */ /* probably a good printer ??? */ iEtat = 0; - if (!pcap_cache_add(name, NULL, NULL)) { + ok = pcap_cache_add_specific(&pcache, + name, NULL, NULL); + if (!ok) { + pcap_cache_destroy_specific(&pcache); SAFE_FREE(line); x_fclose(pfile); TALLOC_FREE(ctx); @@ -101,7 +108,10 @@ bool aix_cache_reload(void) } else if (strstr_m(line, "device")) { /* it's a good virtual printer */ iEtat = 0; - if (!pcap_cache_add(name, NULL, NULL)) { + ok = pcap_cache_add_specific(&pcache, + name, NULL, NULL); + if (!ok) { + pcap_cache_destroy_specific(&pcache); SAFE_FREE(line); x_fclose(pfile); TALLOC_FREE(ctx); @@ -113,6 +123,7 @@ bool aix_cache_reload(void) } } + *_pcache = pcache; x_fclose(pfile); TALLOC_FREE(ctx); return true; diff --git a/source3/printing/print_iprint.c b/source3/printing/print_iprint.c index ad61a0a3380..eeb193c6230 100644 --- a/source3/printing/print_iprint.c +++ b/source3/printing/print_iprint.c @@ -206,7 +206,8 @@ static int iprint_get_server_version(http_t *http, char* serviceUri) static int iprint_cache_add_printer(http_t *http, int reqId, - char* url) + char *url, + struct pcap_cache **pcache) { ipp_t *request = NULL, /* IPP Request */ *response = NULL; /* IPP Response */ @@ -342,7 +343,7 @@ static int iprint_cache_add_printer(http_t *http, */ if (name != NULL && !secure && smb_enabled) - pcap_cache_add(name, info, NULL); + pcap_cache_add_specific(pcache, name, info, NULL); } out: @@ -351,7 +352,7 @@ static int iprint_cache_add_printer(http_t *http, return(0); } -bool iprint_cache_reload(void) +bool iprint_cache_reload(struct pcap_cache **_pcache) { http_t *http = NULL; /* HTTP connection to server */ ipp_t *request = NULL, /* IPP Request */ @@ -359,7 +360,8 @@ bool iprint_cache_reload(void) ipp_attribute_t *attr; /* Current attribute */ cups_lang_t *language = NULL; /* Default language */ int i; - bool ret = False; + bool ret = false; + struct pcap_cache *pcache = NULL; DEBUG(5, ("reloading iprint printcap cache\n")); @@ -441,14 +443,16 @@ bool iprint_cache_reload(void) char *url = ippGetString(attr, i, NULL); if (!url || !strlen(url)) continue; - iprint_cache_add_printer(http, i+2, url); + iprint_cache_add_printer(http, i+2, url, + &pcache); } } attr = ippNextAttribute(response); } } - ret = True; + ret = true; + *_pcache = pcache; out: if (response) diff --git a/source3/printing/print_standard.c b/source3/printing/print_standard.c index c4f9c5b7ae3..b5f1056b2e6 100644 --- a/source3/printing/print_standard.c +++ b/source3/printing/print_standard.c @@ -59,10 +59,11 @@ #include "printing/pcap.h" /* handle standard printcap - moved from pcap_printer_fn() */ -bool std_pcap_cache_reload(const char *pcap_name) +bool std_pcap_cache_reload(const char *pcap_name, struct pcap_cache **_pcache) { XFILE *pcap_file; char *pcap_line; + struct pcap_cache *pcache = NULL; if ((pcap_file = x_fopen(pcap_name, O_RDONLY, 0)) == NULL) { DEBUG(0, ("Unable to open printcap file %s for read!\n", pcap_name)); @@ -117,12 +118,15 @@ bool std_pcap_cache_reload(const char *pcap_name) } } - if (*name && !pcap_cache_add(name, comment, NULL)) { + if ((*name != '\0') + && !pcap_cache_add_specific(&pcache, name, comment, NULL)) { x_fclose(pcap_file); + pcap_cache_destroy_specific(&pcache); return false; } } x_fclose(pcap_file); + *_pcache = pcache; return true; } diff --git a/source3/printing/print_svid.c b/source3/printing/print_svid.c index 222649308ca..879661bf5f3 100644 --- a/source3/printing/print_svid.c +++ b/source3/printing/print_svid.c @@ -35,10 +35,11 @@ #include "printing/pcap.h" #if defined(SYSV) || defined(HPUX) -bool sysv_cache_reload(void) +bool sysv_cache_reload(struct pcap_cache **_pcache) { char **lines; int i; + struct pcap_cache *pcache = NULL; #if defined(HPUX) DEBUG(5, ("reloading hpux printcap cache\n")); @@ -111,14 +112,16 @@ bool sysv_cache_reload(void) *tmp = '\0'; /* add it to the cache */ - if (!pcap_cache_add(name, NULL, NULL)) { + if (!pcap_cache_add_specific(&pcache, name, NULL, NULL)) { TALLOC_FREE(lines); - return False; + pcap_cache_destroy_specific(&pcache); + return false; } } TALLOC_FREE(lines); - return True; + *_pcache = pcache; + return true; } #else diff --git a/source3/printing/printer_list.c b/source3/printing/printer_list.c index 7e89ec4cd7a..815f89fbca3 100644 --- a/source3/printing/printer_list.c +++ b/source3/printing/printer_list.c @@ -283,7 +283,8 @@ done: typedef int (printer_list_trv_fn_t)(struct db_record *, void *); static NTSTATUS printer_list_traverse(printer_list_trv_fn_t *fn, - void *private_data) + void *private_data, + bool read_only) { struct db_context *db; NTSTATUS status; @@ -293,7 +294,11 @@ static NTSTATUS printer_list_traverse(printer_list_trv_fn_t *fn, return NT_STATUS_INTERNAL_DB_CORRUPTION; } - status = dbwrap_traverse(db, fn, private_data, NULL); + if (read_only) { + status = dbwrap_traverse_read(db, fn, private_data, NULL); + } else { + status = dbwrap_traverse(db, fn, private_data, NULL); + } return status; } @@ -363,7 +368,7 @@ NTSTATUS printer_list_clean_old(void) state.status = NT_STATUS_OK; - status = printer_list_traverse(printer_list_clean_fn, &state); + status = printer_list_traverse(printer_list_clean_fn, &state, false); if (NT_STATUS_EQUAL(status, NT_STATUS_UNSUCCESSFUL) && !NT_STATUS_IS_OK(state.status)) { status = state.status; @@ -416,8 +421,8 @@ static int printer_list_exec_fn(struct db_record *rec, void *private_data) return 0; } -NTSTATUS printer_list_run_fn(void (*fn)(const char *, const char *, const char *, void *), - void *private_data) +NTSTATUS printer_list_read_run_fn(void (*fn)(const char *, const char *, const char *, void *), + void *private_data) { struct printer_list_exec_state state; NTSTATUS status; @@ -426,7 +431,7 @@ NTSTATUS printer_list_run_fn(void (*fn)(const char *, const char *, const char * state.private_data = private_data; state.status = NT_STATUS_OK; - status = printer_list_traverse(printer_list_exec_fn, &state); + status = printer_list_traverse(printer_list_exec_fn, &state, true); if (NT_STATUS_EQUAL(status, NT_STATUS_UNSUCCESSFUL) && !NT_STATUS_IS_OK(state.status)) { status = state.status; diff --git a/source3/printing/printer_list.h b/source3/printing/printer_list.h index fb2e007ae6c..b12c1923e72 100644 --- a/source3/printing/printer_list.h +++ b/source3/printing/printer_list.h @@ -100,6 +100,6 @@ NTSTATUS printer_list_mark_reload(void); */ NTSTATUS printer_list_clean_old(void); -NTSTATUS printer_list_run_fn(void (*fn)(const char *, const char *, const char *, void *), - void *private_data); +NTSTATUS printer_list_read_run_fn(void (*fn)(const char *, const char *, const char *, void *), + void *private_data); #endif /* _PRINTER_LIST_H_ */ diff --git a/source3/printing/queue_process.c b/source3/printing/queue_process.c index aa0d0fb6a89..88196b4216f 100644 --- a/source3/printing/queue_process.c +++ b/source3/printing/queue_process.c @@ -33,10 +33,102 @@ #include "rpc_server/rpc_config.h" #include "printing/load.h" #include "rpc_server/spoolss/srv_spoolss_nt.h" +#include "auth.h" +#include "nt_printing.h" extern pid_t start_spoolssd(struct tevent_context *ev_ctx, struct messaging_context *msg_ctx); +/** + * @brief Purge stale printers and reload from pre-populated pcap cache. + * + * This function should normally only be called as a callback on a successful + * pcap_cache_reload(). + * + * This function can cause DELETION of printers and drivers from our registry, + * so calling it on a failed pcap reload may REMOVE permanently all printers + * and drivers. + * + * @param[in] ev The event context. + * + * @param[in] msg_ctx The messaging context. + */ +static void delete_and_reload_printers_full(struct tevent_context *ev, + struct messaging_context *msg_ctx) +{ + struct auth_session_info *session_info = NULL; + struct spoolss_PrinterInfo2 *pinfo2 = NULL; + int n_services; + int pnum; + int snum; + const char *pname; + const char *sname; + NTSTATUS status; + + n_services = lp_numservices(); + pnum = lp_servicenumber(PRINTERS_NAME); + + status = make_session_info_system(talloc_tos(), &session_info); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(3, ("reload_printers: " + "Could not create system session_info\n")); + /* can't remove stale printers before we + * are fully initilized */ + return; + } + + /* + * Add default config for printers added to smb.conf file and remove + * stale printers + */ + for (snum = 0; snum < n_services; snum++) { + /* avoid removing PRINTERS_NAME */ + if (snum == pnum) { + continue; + } + + /* skip no-printer services */ + if (!snum_is_shared_printer(snum)) { + continue; + } + + sname = lp_const_servicename(snum); + pname = lp_printername(session_info, snum); + + /* check printer, but avoid removing non-autoloaded printers */ + if (lp_autoloaded(snum) && !pcap_printername_ok(pname)) { + DEBUG(3, ("removing stale printer %s\n", pname)); + + if (is_printer_published(session_info, session_info, + msg_ctx, + NULL, + lp_servicename(session_info, + snum), + &pinfo2)) { + nt_printer_publish(session_info, + session_info, + msg_ctx, + pinfo2, + DSPRINT_UNPUBLISH); + TALLOC_FREE(pinfo2); + } + nt_printer_remove(session_info, session_info, msg_ctx, + pname); + } else { + DEBUG(8, ("Adding default registry entry for printer " + "[%s], if it doesn't exist.\n", sname)); + nt_printer_add(session_info, session_info, msg_ctx, + sname); + } + } + + /* finally, purge old snums */ + delete_and_reload_printers(ev, msg_ctx); + + TALLOC_FREE(session_info); +} + + /**************************************************************************** Notify smbds of new printcap data **************************************************************************/ @@ -50,7 +142,7 @@ static void reload_pcap_change_notify(struct tevent_context *ev, * This will block the process for some time (~1 sec per printer), but * it doesn't block smbd's servering clients. */ - delete_and_reload_printers(ev, msg_ctx); + delete_and_reload_printers_full(ev, msg_ctx); message_send_all(msg_ctx, MSG_PRINTER_PCAP, NULL, 0, NULL); } @@ -372,7 +464,8 @@ bool printing_subsystem_init(struct tevent_context *ev_ctx, ret = printing_subsystem_queue_tasks(ev_ctx, msg_ctx); /* Publish nt printers, this requires a working winreg pipe */ - pcap_cache_reload(ev_ctx, msg_ctx, &delete_and_reload_printers); + pcap_cache_reload(ev_ctx, msg_ctx, + &delete_and_reload_printers_full); return ret; } @@ -390,7 +483,7 @@ void printing_subsystem_update(struct tevent_context *ev_ctx, bool force) { if (background_lpq_updater_pid != -1) { - if (pcap_cache_loaded()) { + if (pcap_cache_loaded(NULL)) { load_printers(ev_ctx, msg_ctx); } if (force) { @@ -401,5 +494,6 @@ void printing_subsystem_update(struct tevent_context *ev_ctx, return; } - pcap_cache_reload(ev_ctx, msg_ctx, &delete_and_reload_printers); + pcap_cache_reload(ev_ctx, msg_ctx, + &delete_and_reload_printers_full); } diff --git a/source3/printing/spoolssd.c b/source3/printing/spoolssd.c index 7525fd1c2b6..59f560cd3ba 100644 --- a/source3/printing/spoolssd.c +++ b/source3/printing/spoolssd.c @@ -132,27 +132,6 @@ static void smb_conf_updated(struct messaging_context *msg, update_conf(ev_ctx, msg); } -static void update_pcap(struct tevent_context *ev_ctx, - struct messaging_context *msg_ctx) -{ - change_to_root_user(); - delete_and_reload_printers(ev_ctx, msg_ctx); -} - -static void pcap_updated(struct messaging_context *msg, - void *private_data, - uint32_t msg_type, - struct server_id server_id, - DATA_BLOB *data) -{ - struct tevent_context *ev_ctx; - - ev_ctx = talloc_get_type_abort(private_data, struct tevent_context); - - DEBUG(10, ("Got message that pcap updated. Reloading.\n")); - update_pcap(ev_ctx, msg); -} - static void spoolss_sig_term_handler(struct tevent_context *ev, struct tevent_signal *se, int signum, @@ -318,8 +297,6 @@ static bool spoolss_child_init(struct tevent_context *ev_ctx, messaging_register(msg_ctx, ev_ctx, MSG_SMB_CONF_UPDATED, smb_conf_updated); - messaging_register(msg_ctx, ev_ctx, MSG_PRINTER_PCAP, - pcap_updated); messaging_register(msg_ctx, ev_ctx, MSG_PREFORK_PARENT_EVENT, parent_ping); @@ -327,7 +304,7 @@ static bool spoolss_child_init(struct tevent_context *ev_ctx, * If so then we probably missed a message and should load_printers() * ourselves. If pcap has not been loaded yet, then ignore, we will get * a message as soon as the bq process completes the reload. */ - if (pcap_cache_loaded()) { + if (pcap_cache_loaded(NULL)) { load_printers(ev_ctx, msg_ctx); } @@ -739,16 +716,15 @@ pid_t start_spoolssd(struct tevent_context *ev_ctx, MSG_SMB_CONF_UPDATED, smb_conf_updated); messaging_register(msg_ctx, NULL, MSG_PRINTER_UPDATE, print_queue_forward); - messaging_register(msg_ctx, ev_ctx, MSG_PRINTER_PCAP, - pcap_updated); messaging_register(msg_ctx, ev_ctx, MSG_PREFORK_CHILD_EVENT, child_ping); - /* As soon as messaging is up check if pcap has been loaded already. - * If so then we probably missed a message and should load_printers() - * ourselves. If pcap has not been loaded yet, then ignore, we will get - * a message as soon as the bq process completes the reload. */ - if (pcap_cache_loaded()) { + /* + * As soon as messaging is up check if pcap has been loaded already. + * If pcap has not been loaded yet, then ignore, as we will reload on + * client enumeration anyway. + */ + if (pcap_cache_loaded(NULL)) { load_printers(ev_ctx, msg_ctx); } diff --git a/source3/rpc_client/cli_netlogon.c b/source3/rpc_client/cli_netlogon.c index 66a50a8f2d9..3d6a3e1a0a8 100644 --- a/source3/rpc_client/cli_netlogon.c +++ b/source3/rpc_client/cli_netlogon.c @@ -89,6 +89,7 @@ NTSTATUS rpccli_netlogon_setup_creds(struct rpc_pipe_client *cli, cli->dc = netlogon_creds_client_init(cli, mach_acct, clnt_name, + sec_chan_type, &clnt_chal_send, &srv_chal_recv, &password, diff --git a/source3/rpc_server/spoolss/srv_spoolss_nt.c b/source3/rpc_server/spoolss/srv_spoolss_nt.c index 64f5cbed2ff..335647bf16f 100644 --- a/source3/rpc_server/spoolss/srv_spoolss_nt.c +++ b/source3/rpc_server/spoolss/srv_spoolss_nt.c @@ -1726,6 +1726,16 @@ WERROR _spoolss_OpenPrinterEx(struct pipes_struct *p, return WERR_INVALID_PARAM; } + /* + * The printcap printer share inventory is updated on client + * enumeration. For clients that do not perform enumeration prior to + * access, such as cupssmbadd, we reinitialise the printer share + * inventory on open as well. + */ + become_root(); + delete_and_reload_printers(server_event_context(), p->msg_ctx); + unbecome_root(); + /* some sanity check because you can open a printer or a print server */ /* aka: \\server\printer or \\server */ @@ -4284,15 +4294,6 @@ static WERROR construct_printer_info8(TALLOC_CTX *mem_ctx, return WERR_OK; } - -/******************************************************************** -********************************************************************/ - -static bool snum_is_shared_printer(int snum) -{ - return (lp_browseable(snum) && lp_snum_ok(snum) && lp_print_ok(snum)); -} - /******************************************************************** Spoolss_enumprinters. ********************************************************************/ @@ -4307,7 +4308,7 @@ static WERROR enum_all_printers_info_level(TALLOC_CTX *mem_ctx, uint32_t *count_p) { int snum; - int n_services = lp_numservices(); + int n_services; union spoolss_PrinterInfo *info = NULL; uint32_t count = 0; WERROR result = WERR_OK; @@ -4319,6 +4320,15 @@ static WERROR enum_all_printers_info_level(TALLOC_CTX *mem_ctx, return WERR_NOMEM; } + /* + * printer shares are updated on client enumeration. The background + * printer process updates printer_list.tdb at regular intervals. + */ + become_root(); + delete_and_reload_printers(server_event_context(), msg_ctx); + unbecome_root(); + + n_services = lp_numservices(); *count_p = 0; *info_p = NULL; diff --git a/source3/rpc_server/srvsvc/srv_srvsvc_nt.c b/source3/rpc_server/srvsvc/srv_srvsvc_nt.c index 011d41fa522..7b9ec5c87da 100644 --- a/source3/rpc_server/srvsvc/srv_srvsvc_nt.c +++ b/source3/rpc_server/srvsvc/srv_srvsvc_nt.c @@ -484,6 +484,7 @@ static WERROR init_srv_share_info_ctr(struct pipes_struct *p, /* Ensure all the usershares are loaded. */ become_root(); + delete_and_reload_printers(server_event_context(), p->msg_ctx); load_usershare_shares(NULL, connections_snum_used); load_registry_shares(); num_services = lp_numservices(); diff --git a/source3/smbd/dir.c b/source3/smbd/dir.c index 1be5daa385e..3c3f662001f 100644 --- a/source3/smbd/dir.c +++ b/source3/smbd/dir.c @@ -24,7 +24,7 @@ #include "smbd/globals.h" #include "libcli/security/security.h" #include "lib/util/bitmap.h" -#include "memcache.h" +#include "../lib/util/memcache.h" /* This module implements directory related functions for Samba. diff --git a/source3/smbd/dosmode.c b/source3/smbd/dosmode.c index b99e18033cc..baf40d7750b 100644 --- a/source3/smbd/dosmode.c +++ b/source3/smbd/dosmode.c @@ -951,6 +951,19 @@ NTSTATUS file_set_sparse(connection_struct *conn, return NT_STATUS_ACCESS_DENIED; } + if (fsp->is_directory) { + DEBUG(9, ("invalid attempt to %s sparse flag on dir %s\n", + (sparse ? "set" : "clear"), + smb_fname_str_dbg(fsp->fsp_name))); + return NT_STATUS_INVALID_PARAMETER; + } + + if (IS_IPC(conn) || IS_PRINT(conn)) { + DEBUG(9, ("attempt to %s sparse flag over invalid conn\n", + (sparse ? "set" : "clear"))); + return NT_STATUS_INVALID_PARAMETER; + } + DEBUG(10,("file_set_sparse: setting sparse bit %u on file %s\n", sparse, smb_fname_str_dbg(fsp->fsp_name))); diff --git a/source3/smbd/durable.c b/source3/smbd/durable.c index 9b05d48680e..c3d0a6fd58b 100644 --- a/source3/smbd/durable.c +++ b/source3/smbd/durable.c @@ -121,7 +121,6 @@ NTSTATUS vfs_default_durable_cookie(struct files_struct *fsp, cookie.stat_info.st_ex_blocks = fsp->fsp_name->st.st_ex_blocks; cookie.stat_info.st_ex_flags = fsp->fsp_name->st.st_ex_flags; cookie.stat_info.st_ex_mask = fsp->fsp_name->st.st_ex_mask; - cookie.stat_info.vfs_private = fsp->fsp_name->st.vfs_private; ndr_err = ndr_push_struct_blob(cookie_blob, mem_ctx, &cookie, (ndr_push_flags_fn_t)ndr_push_vfs_default_durable_cookie); @@ -275,7 +274,6 @@ NTSTATUS vfs_default_durable_disconnect(struct files_struct *fsp, cookie.stat_info.st_ex_blocks = fsp->fsp_name->st.st_ex_blocks; cookie.stat_info.st_ex_flags = fsp->fsp_name->st.st_ex_flags; cookie.stat_info.st_ex_mask = fsp->fsp_name->st.st_ex_mask; - cookie.stat_info.vfs_private = fsp->fsp_name->st.vfs_private; ndr_err = ndr_push_struct_blob(&new_cookie_blob, mem_ctx, &cookie, (ndr_push_flags_fn_t)ndr_push_vfs_default_durable_cookie); @@ -536,18 +534,6 @@ static bool vfs_default_durable_reconnect_check_stat( return false; } - if (cookie_st->vfs_private != fsp_st->vfs_private) { - DEBUG(1, ("vfs_default_durable_reconnect (%s): " - "stat_ex.%s differs: " - "cookie:%llu != stat:%llu, " - "denying durable reconnect\n", - name, - "vfs_private", - (unsigned long long)cookie_st->vfs_private, - (unsigned long long)fsp_st->vfs_private)); - return false; - } - return true; } diff --git a/source3/smbd/globals.c b/source3/smbd/globals.c index 3eb65a13200..e03c7c4180b 100644 --- a/source3/smbd/globals.c +++ b/source3/smbd/globals.c @@ -20,7 +20,7 @@ #include "includes.h" #include "smbd/smbd.h" #include "smbd/globals.h" -#include "memcache.h" +#include "../lib/util/memcache.h" #include "messages.h" #include "tdb_compat.h" diff --git a/source3/smbd/lanman.c b/source3/smbd/lanman.c index 0a0ab6b9754..d0dae36db2e 100644 --- a/source3/smbd/lanman.c +++ b/source3/smbd/lanman.c @@ -2091,6 +2091,7 @@ static bool api_RNetShareEnum(struct smbd_server_connection *sconn, /* Ensure all the usershares are loaded. */ become_root(); + delete_and_reload_printers(sconn->ev_ctx, sconn->msg_ctx); load_registry_shares(); count = load_usershare_shares(NULL, connections_snum_used); unbecome_root(); diff --git a/source3/smbd/mangle_hash.c b/source3/smbd/mangle_hash.c index 8a44ea2d6a2..f3d85223330 100644 --- a/source3/smbd/mangle_hash.c +++ b/source3/smbd/mangle_hash.c @@ -767,6 +767,10 @@ const struct mangle_fns *mangle_hash_init(void) { mangle_reset(); + if (chartest == NULL) { + init_chartest(); + } + /* Create the in-memory tdb using our custom hash function. */ tdb_mangled_cache = tdb_open_ex("mangled_cache", 1031, TDB_INTERNAL, (O_RDWR|O_CREAT), 0644, NULL, fast_string_hash); diff --git a/source3/smbd/mangle_hash2.c b/source3/smbd/mangle_hash2.c index c2910f82c85..ac1f4b0f4fb 100644 --- a/source3/smbd/mangle_hash2.c +++ b/source3/smbd/mangle_hash2.c @@ -66,7 +66,7 @@ #include "includes.h" #include "smbd/smbd.h" #include "smbd/globals.h" -#include "memcache.h" +#include "../lib/util/memcache.h" #include "mangle.h" #if 1 diff --git a/source3/smbd/negprot.c b/source3/smbd/negprot.c index 315bd890376..b1d93cd8a3e 100644 --- a/source3/smbd/negprot.c +++ b/source3/smbd/negprot.c @@ -252,7 +252,8 @@ static void reply_nt1(struct smb_request *req, uint16 choice) if ( (req->flags2 & FLAGS2_EXTENDED_SECURITY) && ((req->flags2 & FLAGS2_SMB_SECURITY_SIGNATURES_REQUIRED) == 0) ) { - if (get_remote_arch() != RA_SAMBA) { + if ((get_remote_arch() != RA_SAMBA) && + (get_remote_arch() != RA_CIFSFS)) { set_remote_arch( RA_VISTA ); } } diff --git a/source3/smbd/nttrans.c b/source3/smbd/nttrans.c index 5a0ac39e91e..131009b14c3 100644 --- a/source3/smbd/nttrans.c +++ b/source3/smbd/nttrans.c @@ -2046,7 +2046,8 @@ static void call_nt_transact_query_security_desc(connection_struct *conn, status = smbd_do_query_security_desc(conn, talloc_tos(), fsp, - security_info_wanted, + security_info_wanted & + SMB_SUPPORTED_SECINFO_FLAGS, max_data_count, &marshalled_sd, &sd_size); @@ -2139,8 +2140,8 @@ static void call_nt_transact_set_security_desc(connection_struct *conn, return; } - status = set_sd_blob(fsp, (uint8 *)data, data_count, security_info_sent); - + status = set_sd_blob(fsp, (uint8 *)data, data_count, + security_info_sent & SMB_SUPPORTED_SECINFO_FLAGS); if (!NT_STATUS_IS_OK(status)) { reply_nterror(req, status); return; diff --git a/source3/smbd/posix_acls.c b/source3/smbd/posix_acls.c index 621457e0510..d187787e098 100644 --- a/source3/smbd/posix_acls.c +++ b/source3/smbd/posix_acls.c @@ -3288,7 +3288,7 @@ static NTSTATUS posix_get_nt_acl_common(struct connection_struct *conn, num_profile_acls = 3; } - if ((security_info & SECINFO_DACL) && !(security_info & SECINFO_PROTECTED_DACL)) { + if (security_info & SECINFO_DACL) { /* * In the optimum case Creator Owner and Creator Group would be used for diff --git a/source3/smbd/proto.h b/source3/smbd/proto.h index 6153a4965a8..327b25d370e 100644 --- a/source3/smbd/proto.h +++ b/source3/smbd/proto.h @@ -958,6 +958,7 @@ const struct security_token *sec_ctx_active_token(void); /* The following definitions come from smbd/server.c */ struct memcache *smbd_memcache(void); +bool snum_is_shared_printer(int snum); void delete_and_reload_printers(struct tevent_context *ev, struct messaging_context *msg_ctx); bool reload_services(struct smbd_server_connection *sconn, diff --git a/source3/smbd/server.c b/source3/smbd/server.c index 9e249d1a6b9..8856f43be59 100644 --- a/source3/smbd/server.c +++ b/source3/smbd/server.c @@ -29,7 +29,7 @@ #include "registry/reg_init_full.h" #include "libcli/auth/schannel.h" #include "secrets.h" -#include "memcache.h" +#include "../lib/util/memcache.h" #include "ctdbd_conn.h" #include "printing/queue_process.h" #include "rpc_server/rpc_service_setup.h" @@ -109,24 +109,6 @@ static void smbd_parent_conf_updated(struct messaging_context *msg, } /******************************************************************* - What to do when printcap is updated. - ********************************************************************/ - -static void smb_pcap_updated(struct messaging_context *msg, - void *private_data, - uint32_t msg_type, - struct server_id server_id, - DATA_BLOB *data) -{ - struct tevent_context *ev_ctx = - talloc_get_type_abort(private_data, struct tevent_context); - - DEBUG(10,("Got message saying pcap was updated. Reloading.\n")); - change_to_root_user(); - delete_and_reload_printers(ev_ctx, msg); -} - -/******************************************************************* Delete a statcache entry. ********************************************************************/ @@ -881,8 +863,6 @@ static bool open_sockets_smbd(struct smbd_parent_context *parent, messaging_register(msg_ctx, NULL, MSG_SMB_STAT_CACHE_DELETE, smb_stat_cache_delete); messaging_register(msg_ctx, NULL, MSG_DEBUG, smbd_msg_debug); - messaging_register(msg_ctx, ev_ctx, MSG_PRINTER_PCAP, - smb_pcap_updated); messaging_register(msg_ctx, NULL, MSG_SMB_BRL_VALIDATE, brl_revalidate); messaging_register(msg_ctx, NULL, MSG_SMB_FORCE_TDIS, diff --git a/source3/smbd/server_reload.c b/source3/smbd/server_reload.c index 1d6f9c2911f..971c8b86efb 100644 --- a/source3/smbd/server_reload.c +++ b/source3/smbd/server_reload.c @@ -31,20 +31,23 @@ #include "messages.h" #include "lib/param/loadparm.h" -static bool snum_is_shared_printer(int snum) +/* + * The persistent pcap cache is populated by the background print process. Per + * client smbds should only reload their printer share inventories if this + * information has changed. Use reload_last_pcap_time to detect this. + */ +static time_t reload_last_pcap_time = 0; + +bool snum_is_shared_printer(int snum) { return (lp_browseable(snum) && lp_snum_ok(snum) && lp_print_ok(snum)); } /** - * @brief Purge stale printers and reload from pre-populated pcap cache. + * @brief Purge stale printer shares and reload from pre-populated pcap cache. * * This function should normally only be called as a callback on a successful - * pcap_cache_reload() or after a MSG_PRINTER_CAP message is received. - * - * This function can cause DELETION of printers and drivers from our registry, - * so calling it on a failed pcap reload may REMOVE permanently all printers - * and drivers. + * pcap_cache_reload(), or on client enumeration. * * @param[in] ev The event context. * @@ -53,14 +56,27 @@ static bool snum_is_shared_printer(int snum) void delete_and_reload_printers(struct tevent_context *ev, struct messaging_context *msg_ctx) { - struct auth_session_info *session_info = NULL; - struct spoolss_PrinterInfo2 *pinfo2 = NULL; int n_services; int pnum; int snum; const char *pname; - const char *sname; - NTSTATUS status; + bool ok; + time_t pcap_last_update; + TALLOC_CTX *frame = talloc_stackframe(); + + ok = pcap_cache_loaded(&pcap_last_update); + if (!ok) { + DEBUG(1, ("pcap cache not loaded\n")); + talloc_free(frame); + return; + } + + if (reload_last_pcap_time == pcap_last_update) { + DEBUG(5, ("skipping printer reload, already up to date.\n")); + talloc_free(frame); + return; + } + reload_last_pcap_time = pcap_last_update; /* Get pcap printers updated */ load_printers(ev, msg_ctx); @@ -70,15 +86,6 @@ void delete_and_reload_printers(struct tevent_context *ev, DEBUG(10, ("reloading printer services from pcap cache\n")); - status = make_session_info_system(talloc_tos(), &session_info); - if (!NT_STATUS_IS_OK(status)) { - DEBUG(3, ("reload_printers: " - "Could not create system session_info\n")); - /* can't remove stale printers before we - * are fully initilized */ - return; - } - /* * Add default config for printers added to smb.conf file and remove * stale printers @@ -94,41 +101,18 @@ void delete_and_reload_printers(struct tevent_context *ev, continue; } - sname = lp_const_servicename(snum); - pname = lp_printername(session_info, snum); + pname = lp_printername(frame, snum); /* check printer, but avoid removing non-autoloaded printers */ if (lp_autoloaded(snum) && !pcap_printername_ok(pname)) { - DEBUG(3, ("removing stale printer %s\n", pname)); - - if (is_printer_published(session_info, session_info, - msg_ctx, - NULL, - lp_servicename(session_info, - snum), - &pinfo2)) { - nt_printer_publish(session_info, - session_info, - msg_ctx, - pinfo2, - DSPRINT_UNPUBLISH); - TALLOC_FREE(pinfo2); - } - nt_printer_remove(session_info, session_info, msg_ctx, - pname); lp_killservice(snum); - } else { - DEBUG(8, ("Adding default registry entry for printer " - "[%s], if it doesn't exist.\n", sname)); - nt_printer_add(session_info, session_info, msg_ctx, - sname); } } /* Make sure deleted printers are gone */ load_printers(ev, msg_ctx); - TALLOC_FREE(session_info); + talloc_free(frame); } /**************************************************************************** diff --git a/source3/smbd/smb2_find.c b/source3/smbd/smb2_find.c index c39a35d526c..02c554f0342 100644 --- a/source3/smbd/smb2_find.c +++ b/source3/smbd/smb2_find.c @@ -224,6 +224,7 @@ static struct tevent_req *smbd_smb2_find_send(TALLOC_CTX *mem_ctx, uint32_t dirtype = FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_DIRECTORY; bool dont_descend = false; bool ask_sharemode = true; + bool wcard_has_wild; struct tm tm; char *p; @@ -252,11 +253,11 @@ static struct tevent_req *smbd_smb2_find_send(TALLOC_CTX *mem_ctx, tevent_req_nterror(req, NT_STATUS_OBJECT_NAME_INVALID); return tevent_req_post(req, ev); } - if (strcmp(in_file_name, "\\") == 0) { + if (strchr_m(in_file_name, '\\') != NULL) { tevent_req_nterror(req, NT_STATUS_OBJECT_NAME_INVALID); return tevent_req_post(req, ev); } - if (strcmp(in_file_name, "/") == 0) { + if (strchr_m(in_file_name, '/') != NULL) { tevent_req_nterror(req, NT_STATUS_OBJECT_NAME_INVALID); return tevent_req_post(req, ev); } @@ -323,11 +324,41 @@ static struct tevent_req *smbd_smb2_find_send(TALLOC_CTX *mem_ctx, dptr_CloseDir(fsp); } - if (fsp->dptr == NULL) { - bool wcard_has_wild; + wcard_has_wild = ms_has_wild(in_file_name); - wcard_has_wild = ms_has_wild(in_file_name); + /* Ensure we've canonicalized any search path if not a wildcard. */ + if (!wcard_has_wild) { + struct smb_filename *smb_fname = NULL; + const char *fullpath; + if (ISDOT(fsp->fsp_name->base_name)) { + fullpath = in_file_name; + } else { + fullpath = talloc_asprintf(state, + "%s/%s", + fsp->fsp_name->base_name, + in_file_name); + } + if (tevent_req_nomem(fullpath, req)) { + return tevent_req_post(req, ev); + } + status = filename_convert(state, + conn, + false, /* Not a DFS path. */ + fullpath, + UCF_SAVE_LCOMP | UCF_ALWAYS_ALLOW_WCARD_LCOMP, + &wcard_has_wild, + &smb_fname); + + if (!NT_STATUS_IS_OK(status)) { + tevent_req_nterror(req, status); + return tevent_req_post(req, ev); + } + + in_file_name = smb_fname->original_lcomp; + } + + if (fsp->dptr == NULL) { status = dptr_create(conn, NULL, /* req */ fsp, diff --git a/source3/smbd/smb2_getinfo.c b/source3/smbd/smb2_getinfo.c index 449aeb3f5f4..bbc838dcc27 100644 --- a/source3/smbd/smb2_getinfo.c +++ b/source3/smbd/smb2_getinfo.c @@ -478,7 +478,8 @@ static struct tevent_req *smbd_smb2_getinfo_send(TALLOC_CTX *mem_ctx, state, fsp, /* Security info wanted. */ - in_additional_information, + in_additional_information & + SMB_SUPPORTED_SECINFO_FLAGS, in_output_buffer_length, &p_marshalled_sd, &sd_size); diff --git a/source3/smbd/smb2_setinfo.c b/source3/smbd/smb2_setinfo.c index d88f7ac8a28..cda8abc2bd9 100644 --- a/source3/smbd/smb2_setinfo.c +++ b/source3/smbd/smb2_setinfo.c @@ -311,7 +311,8 @@ static struct tevent_req *smbd_smb2_setinfo_send(TALLOC_CTX *mem_ctx, status = set_sd_blob(fsp, in_input_buffer.data, in_input_buffer.length, - in_additional_information); + in_additional_information & + SMB_SUPPORTED_SECINFO_FLAGS); if (!NT_STATUS_IS_OK(status)) { tevent_req_nterror(req, status); return tevent_req_post(req, ev); diff --git a/source3/smbd/statcache.c b/source3/smbd/statcache.c index 92010c230dd..2f3b067012e 100644 --- a/source3/smbd/statcache.c +++ b/source3/smbd/statcache.c @@ -21,7 +21,7 @@ */ #include "includes.h" -#include "memcache.h" +#include "../lib/util/memcache.h" #include "smbd/smbd.h" #include "messages.h" #include "smbprofile.h" diff --git a/source3/smbd/vfs.c b/source3/smbd/vfs.c index 49609d01794..4a0588e6f5e 100644 --- a/source3/smbd/vfs.c +++ b/source3/smbd/vfs.c @@ -27,7 +27,7 @@ #include "system/filesys.h" #include "smbd/smbd.h" #include "smbd/globals.h" -#include "memcache.h" +#include "../lib/util/memcache.h" #include "transfer_file.h" #include "ntioctl.h" #include "lib/util/tevent_unix.h" diff --git a/source3/torture/torture.c b/source3/torture/torture.c index 50028872588..2e66912a942 100644 --- a/source3/torture/torture.c +++ b/source3/torture/torture.c @@ -26,7 +26,7 @@ #include "tldap.h" #include "tldap_util.h" #include "../librpc/gen_ndr/svcctl.h" -#include "memcache.h" +#include "../lib/util/memcache.h" #include "nsswitch/winbind_client.h" #include "dbwrap/dbwrap.h" #include "dbwrap/dbwrap_open.h" diff --git a/source3/utils/net_time.c b/source3/utils/net_time.c index 847b4fe445c..56ce8f71ad6 100644 --- a/source3/utils/net_time.c +++ b/source3/utils/net_time.c @@ -84,10 +84,10 @@ static const char *systime(time_t t) int net_time_usage(struct net_context *c, int argc, const char **argv) { d_printf(_( -"net time\n\tdisplays time on a server\n\n" -"net time system\n\tdisplays time on a server in a format ready for /bin/date\n\n" -"net time set\n\truns /bin/date with the time from the server\n\n" -"net time zone\n\tdisplays the timezone in hours from GMT on the remote computer\n\n" +"net time\n\tdisplays time on a server (-S server)\n\n" +"net time system\n\tdisplays time on a server (-S server) in a format ready for /bin/date\n\n" +"net time set\n\truns /bin/date with the time from the server (-S server)\n\n" +"net time zone\n\tdisplays the timezone in hours from GMT on the remote server (-S server)\n\n" "\n")); net_common_flags_usage(c, argc, argv); return -1; @@ -99,6 +99,16 @@ static int net_time_set(struct net_context *c, int argc, const char **argv) struct timeval tv; int result; + if (c->display_usage || c->opt_host == NULL) { + d_printf( "%s\n" + "net time set\n" + " %s\n", + _("Usage:"), + _("Set local time to that of remote time " + "server (-S server) ")); + return 0; + } + tv.tv_sec = nettime(c, NULL); tv.tv_usec=0; @@ -118,13 +128,13 @@ static int net_time_system(struct net_context *c, int argc, const char **argv) { time_t t; - if (c->display_usage) { + if (c->display_usage || c->opt_host == NULL) { d_printf( "%s\n" "net time system\n" " %s\n", _("Usage:"), - _("Output remote time server time in a format " - "ready for /bin/date")); + _("Output remote time server (-S server) " + "time in a format ready for /bin/date")); return 0; } @@ -144,13 +154,13 @@ static int net_time_zone(struct net_context *c, int argc, const char **argv) char zsign; time_t t; - if (c->display_usage) { + if (c->display_usage || c->opt_host == NULL) { d_printf( "%s\n" "net time zone\n" " %s\n", _("Usage:"), - _("Display the remote time server's offset to " - "UTC")); + _("Display the remote time server's (-S server) " + "offset to UTC")); return 0; } diff --git a/source3/winbindd/idmap.c b/source3/winbindd/idmap.c index 97a34d4bddb..c3c88eae138 100644 --- a/source3/winbindd/idmap.c +++ b/source3/winbindd/idmap.c @@ -197,9 +197,9 @@ static struct idmap_domain *idmap_init_domain(TALLOC_CTX *mem_ctx, range = lp_parm_const_string(-1, config_option, "range", NULL); if (range == NULL) { - DEBUG(1, ("idmap range not specified for domain %s\n", - result->name)); if (check_range) { + DEBUG(1, ("idmap range not specified for domain %s\n", + result->name)); goto fail; } } else if (sscanf(range, "%u - %u", &result->low_id, diff --git a/source3/winbindd/winbindd.c b/source3/winbindd/winbindd.c index 68ffd7e03e5..f101e52f65f 100644 --- a/source3/winbindd/winbindd.c +++ b/source3/winbindd/winbindd.c @@ -999,6 +999,41 @@ static bool remove_idle_client(void) return False; } +/* + * Terminate all clients whose requests have taken longer than + * "winbind request timeout" seconds to process, or have been + * idle for more than "winbind request timeout" seconds. + */ + +static void remove_timed_out_clients(void) +{ + struct winbindd_cli_state *state, *next = NULL; + time_t curr_time = time(NULL); + int timeout_val = lp_winbind_request_timeout(); + + for (state = winbindd_client_list(); state; state = next) { + time_t expiry_time; + + next = state->next; + expiry_time = state->last_access + timeout_val; + + if (curr_time > expiry_time) { + if (client_is_idle(state)) { + DEBUG(5,("Idle client timed out, " + "shutting down sock %d, pid %u\n", + state->sock, + (unsigned int)state->pid)); + } else { + DEBUG(5,("Client request timed out, " + "shutting down sock %d, pid %u\n", + state->sock, + (unsigned int)state->pid)); + } + remove_client(state); + } + } +} + struct winbindd_listen_state { bool privileged; int fd; @@ -1024,6 +1059,7 @@ static void winbindd_listen_fde_handler(struct tevent_context *ev, break; } } + remove_timed_out_clients(); new_connection(s->fd, s->privileged); } diff --git a/source3/wscript b/source3/wscript index fda984cfac0..bac3dd588a2 100644 --- a/source3/wscript +++ b/source3/wscript @@ -194,27 +194,32 @@ main() { Logs.warn('no suitable FAM library found') # check for DMAPI libs - Logs.info("Checking for DMAPI library existence") - conf.env['dmapi_lib'] = '' - samba_dmapi_lib = '' - if conf.CHECK_FUNCS_IN('dm_get_eventlist', 'dm'): - samba_dmapi_lib = 'dm' + if Options.options.with_dmapi == False: + have_dmapi = False else: - if conf.CHECK_FUNCS_IN('dm_get_eventlist', 'jfsdm'): - samba_dmapi_lib = 'jfsdm' + have_dmapi = True + Logs.info("Checking for DMAPI library existence") + samba_dmapi_lib = '' + if conf.CHECK_FUNCS_IN('dm_get_eventlist', 'dm'): + samba_dmapi_lib = 'dm' else: - if conf.CHECK_FUNCS_IN('dm_get_eventlist', 'dmapi'): - samba_dmapi_lib = 'dmapi' + if conf.CHECK_FUNCS_IN('dm_get_eventlist', 'jfsdm'): + samba_dmapi_lib = 'jfsdm' else: - if conf.CHECK_FUNCS_IN('dm_get_eventlist', 'xdsm'): - samba_dmapi_lib = 'xdsm' - # only bother to test headers and compilation when a candidate - # library has been found - if Options.options.with_dmapi == True and samba_dmapi_lib == '': - conf.fatal('DMAPI support requested, but no suitable DMAPI library found') - else: - conf.CHECK_HEADERS('sys/dmi.h xfs/dmapi.h sys/jfsdmapi.h sys/dmapi.h dmapi.h') - conf.CHECK_CODE(''' + if conf.CHECK_FUNCS_IN('dm_get_eventlist', 'dmapi'): + samba_dmapi_lib = 'dmapi' + else: + if conf.CHECK_FUNCS_IN('dm_get_eventlist', 'xdsm'): + samba_dmapi_lib = 'xdsm' + # only bother to test headers and compilation when a candidate + # library has been found + if samba_dmapi_lib == '': + have_dmapi = False + broken_dmapi = "no suitable DMAPI library found" + + if have_dmapi: + conf.CHECK_HEADERS('sys/dmi.h xfs/dmapi.h sys/jfsdmapi.h sys/dmapi.h dmapi.h') + conf.CHECK_CODE(''' #include <time.h> /* needed by Tru64 */ #include <sys/types.h> /* needed by AIX */ #ifdef HAVE_XFS_DMAPI_H @@ -244,17 +249,28 @@ int main(int argc, char **argv) return 0; } ''', - 'USE_DMAPI', - addmain=False, - execute=False, - lib=samba_dmapi_lib, - msg='Checking whether DMAPI lib '+samba_dmapi_lib+' can be used') - - if conf.CONFIG_SET('USE_DMAPI'): - conf.env['dmapi_lib'] = samba_dmapi_lib + 'USEABLE_DMAPI_LIBRARY', + addmain=False, + execute=False, + lib=samba_dmapi_lib, + msg='Checking whether DMAPI lib '+samba_dmapi_lib+' can be used') + if not conf.CONFIG_SET('USEABLE_DMAPI_LIBRARY'): + have_dmapi = False + broken_dmapi = "no usable DMAPI library found" + + if have_dmapi: + Logs.info("Building with DMAPI support.") + conf.env['dmapi_lib'] = samba_dmapi_lib + conf.DEFINE('USE_DMAPI', 1) + else: + if Options.options.with_dmapi == False: + Logs.info("Building without DMAPI support (--without-dmapi).") + elif Options.options.with_dmapi == True: + Logs.error("DMAPI support not available: " + broken_dmapi) + conf.fatal('DMAPI support requested but not found.'); else: - if Options.options.with_dmapi == True: - conf.fatal('DMAPI support requested but not found'); + Logs.warn("Building without DMAPI support: " + broken_dmapi) + conf.env['dmapi_lib'] = '' # Check for various members of the stat structure conf.CHECK_STRUCTURE_MEMBER('struct stat', 'st_blocks', define='HAVE_STAT_ST_BLOCKS', diff --git a/source3/wscript_build b/source3/wscript_build index d4f999a0c61..9461b05d32b 100755 --- a/source3/wscript_build +++ b/source3/wscript_build @@ -1108,7 +1108,7 @@ bld.SAMBA3_SUBSYSTEM('tdb-wrap3', vars=locals()) bld.SAMBA3_LIBRARY('samba3-util', - source='''lib/util_sec.c lib/util_str.c lib/adt_tree.c lib/util_malloc.c lib/memcache.c lib/namearray.c lib/file_id.c''', + source='''lib/util_sec.c lib/util_str.c lib/adt_tree.c lib/util_malloc.c lib/namearray.c lib/file_id.c''', deps='samba-util charset', private_library=True) diff --git a/source4/dns_server/dlz_bind9.c b/source4/dns_server/dlz_bind9.c index c0ed8e11cbd..38b765aa0dc 100644 --- a/source4/dns_server/dlz_bind9.c +++ b/source4/dns_server/dlz_bind9.c @@ -37,7 +37,7 @@ #include "messaging/messaging.h" #include "lib/cmdline/popt_common.h" #include "dlz_minimal.h" - +#include "dns_server/dnsserver_common.h" struct b9_options { const char *url; @@ -459,7 +459,7 @@ static isc_result_t b9_putnamedrr(struct dlz_bind9_data *state, parse options */ static isc_result_t parse_options(struct dlz_bind9_data *state, - unsigned int argc, char *argv[], + unsigned int argc, const char **argv, struct b9_options *options) { int opt; @@ -470,7 +470,7 @@ static isc_result_t parse_options(struct dlz_bind9_data *state, { NULL } }; - pc = poptGetContext("dlz_bind9", argc, (const char **)argv, long_options, + pc = poptGetContext("dlz_bind9", argc, argv, long_options, POPT_CONTEXT_KEEP_FIRST); while ((opt = poptGetNextOpt(pc)) != -1) { switch (opt) { @@ -564,7 +564,7 @@ static int dlz_state_debug_unregister(struct dlz_bind9_data *state) called to initialise the driver */ _PUBLIC_ isc_result_t dlz_create(const char *dlzname, - unsigned int argc, char *argv[], + unsigned int argc, const char **argv, void **dbdata, ...) { struct dlz_bind9_data *state; @@ -801,11 +801,10 @@ static isc_result_t dlz_lookup_types(struct dlz_bind9_data *state, const char **types) { TALLOC_CTX *tmp_ctx = talloc_new(state); - const char *attrs[] = { "dnsRecord", NULL }; - int ret = LDB_SUCCESS, i; - struct ldb_result *res; - struct ldb_message_element *el; struct ldb_dn *dn; + WERROR werr = WERR_DNS_ERROR_NAME_DOES_NOT_EXIST; + struct dnsp_DnssrvRpcRecord *records = NULL; + uint16_t num_records = 0, i; for (i=0; zone_prefixes[i]; i++) { dn = ldb_dn_copy(tmp_ctx, ldb_get_default_basedn(state->samdb)); @@ -819,38 +818,21 @@ static isc_result_t dlz_lookup_types(struct dlz_bind9_data *state, return ISC_R_NOMEMORY; } - ret = ldb_search(state->samdb, tmp_ctx, &res, dn, LDB_SCOPE_BASE, - attrs, "objectClass=dnsNode"); - if (ret == LDB_SUCCESS) { + werr = dns_common_lookup(state->samdb, tmp_ctx, dn, + &records, &num_records, NULL); + if (W_ERROR_IS_OK(werr)) { break; } } - if (ret != LDB_SUCCESS || res->count == 0) { - talloc_free(tmp_ctx); - return ISC_R_NOTFOUND; - } - - el = ldb_msg_find_element(res->msgs[0], "dnsRecord"); - if (el == NULL || el->num_values == 0) { + if (!W_ERROR_IS_OK(werr)) { talloc_free(tmp_ctx); return ISC_R_NOTFOUND; } - for (i=0; i<el->num_values; i++) { - struct dnsp_DnssrvRpcRecord rec; - enum ndr_err_code ndr_err; + for (i=0; i < num_records; i++) { isc_result_t result; - ndr_err = ndr_pull_struct_blob(&el->values[i], tmp_ctx, &rec, - (ndr_pull_flags_fn_t)ndr_pull_dnsp_DnssrvRpcRecord); - if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { - state->log(ISC_LOG_ERROR, "samba_dlz: failed to parse dnsRecord for %s", - ldb_dn_get_linearized(dn)); - talloc_free(tmp_ctx); - return ISC_R_FAILURE; - } - - result = b9_putrr(state, lookup, &rec, types); + result = b9_putrr(state, lookup, &records[i], types); if (result != ISC_R_SUCCESS) { talloc_free(tmp_ctx); return result; @@ -929,6 +911,9 @@ _PUBLIC_ isc_result_t dlz_allnodes(const char *zone, void *dbdata, TALLOC_CTX *el_ctx = talloc_new(tmp_ctx); const char *rdn, *name; const struct ldb_val *v; + WERROR werr; + struct dnsp_DnssrvRpcRecord *recs = NULL; + uint16_t num_recs = 0; el = ldb_msg_find_element(res->msgs[i], "dnsRecord"); if (el == NULL || el->num_values == 0) { @@ -962,24 +947,24 @@ _PUBLIC_ isc_result_t dlz_allnodes(const char *zone, void *dbdata, return ISC_R_NOMEMORY; } - for (j=0; j<el->num_values; j++) { - struct dnsp_DnssrvRpcRecord rec; - enum ndr_err_code ndr_err; - isc_result_t result; + werr = dns_common_extract(el, el_ctx, &recs, &num_recs); + if (!W_ERROR_IS_OK(werr)) { + state->log(ISC_LOG_ERROR, "samba_dlz: failed to parse dnsRecord for %s, %s", + ldb_dn_get_linearized(dn), win_errstr(werr)); + talloc_free(el_ctx); + continue; + } - ndr_err = ndr_pull_struct_blob(&el->values[j], el_ctx, &rec, - (ndr_pull_flags_fn_t)ndr_pull_dnsp_DnssrvRpcRecord); - if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { - state->log(ISC_LOG_ERROR, "samba_dlz: failed to parse dnsRecord for %s", - ldb_dn_get_linearized(dn)); - continue; - } + for (j=0; j < num_recs; j++) { + isc_result_t result; - result = b9_putnamedrr(state, allnodes, name, &rec); + result = b9_putnamedrr(state, allnodes, name, &recs[j]); if (result != ISC_R_SUCCESS) { continue; } } + + talloc_free(el_ctx); } talloc_free(tmp_ctx); @@ -1057,39 +1042,25 @@ _PUBLIC_ void dlz_closeversion(const char *zone, isc_boolean_t commit, */ static bool b9_has_soa(struct dlz_bind9_data *state, struct ldb_dn *dn, const char *zone) { - const char *attrs[] = { "dnsRecord", NULL }; - struct ldb_result *res; - struct ldb_message_element *el; TALLOC_CTX *tmp_ctx = talloc_new(state); - int ret, i; + WERROR werr; + struct dnsp_DnssrvRpcRecord *records = NULL; + uint16_t num_records = 0, i; if (!ldb_dn_add_child_fmt(dn, "DC=@,DC=%s", zone)) { talloc_free(tmp_ctx); return false; } - ret = ldb_search(state->samdb, tmp_ctx, &res, dn, LDB_SCOPE_BASE, - attrs, "objectClass=dnsNode"); - if (ret != LDB_SUCCESS) { - talloc_free(tmp_ctx); - return false; - } - - el = ldb_msg_find_element(res->msgs[0], "dnsRecord"); - if (el == NULL) { + werr = dns_common_lookup(state->samdb, tmp_ctx, dn, + &records, &num_records, NULL); + if (!W_ERROR_IS_OK(werr)) { talloc_free(tmp_ctx); return false; } - for (i=0; i<el->num_values; i++) { - struct dnsp_DnssrvRpcRecord rec; - enum ndr_err_code ndr_err; - ndr_err = ndr_pull_struct_blob(&el->values[i], tmp_ctx, &rec, - (ndr_pull_flags_fn_t)ndr_pull_dnsp_DnssrvRpcRecord); - if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { - continue; - } - if (rec.wType == DNS_TYPE_SOA) { + for (i=0; i < num_records; i++) { + if (records[i].wType == DNS_TYPE_SOA) { talloc_free(tmp_ctx); return true; } @@ -1321,46 +1292,6 @@ _PUBLIC_ isc_boolean_t dlz_ssumatch(const char *signer, const char *name, const return ISC_TRUE; } - -/* - add a new record - */ -static isc_result_t b9_add_record(struct dlz_bind9_data *state, const char *name, - struct ldb_dn *dn, - struct dnsp_DnssrvRpcRecord *rec) -{ - struct ldb_message *msg; - enum ndr_err_code ndr_err; - struct ldb_val v; - int ret; - - msg = ldb_msg_new(rec); - if (msg == NULL) { - return ISC_R_NOMEMORY; - } - msg->dn = dn; - ret = ldb_msg_add_string(msg, "objectClass", "dnsNode"); - if (ret != LDB_SUCCESS) { - return ISC_R_FAILURE; - } - - ndr_err = ndr_push_struct_blob(&v, rec, rec, (ndr_push_flags_fn_t)ndr_push_dnsp_DnssrvRpcRecord); - if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { - return ISC_R_FAILURE; - } - ret = ldb_msg_add_value(msg, "dnsRecord", &v, NULL); - if (ret != LDB_SUCCESS) { - return ISC_R_FAILURE; - } - - ret = ldb_add(state->samdb, msg); - if (ret != LDB_SUCCESS) { - return ISC_R_FAILURE; - } - - return ISC_R_SUCCESS; -} - /* see if two DNS names are the same */ @@ -1488,12 +1419,14 @@ _PUBLIC_ isc_result_t dlz_addrdataset(const char *name, const char *rdatastr, vo struct dnsp_DnssrvRpcRecord *rec; struct ldb_dn *dn; isc_result_t result; - struct ldb_result *res; - const char *attrs[] = { "dnsRecord", NULL }; - int ret, i; - struct ldb_message_element *el; - enum ndr_err_code ndr_err; + bool tombstoned = false; + bool needs_add = false; + struct dnsp_DnssrvRpcRecord *recs = NULL; + uint16_t num_recs = 0; + uint16_t first = 0; + uint16_t i; NTTIME t; + WERROR werr; if (state->transaction_token != (void*)version) { state->log(ISC_LOG_INFO, "samba_dlz: bad transaction version"); @@ -1510,7 +1443,6 @@ _PUBLIC_ isc_result_t dlz_addrdataset(const char *name, const char *rdatastr, vo t /= 3600; /* convert to hours */ rec->rank = DNS_RANK_ZONE; - rec->dwSerial = state->soa_serial; rec->dwTimeStamp = (uint32_t)t; if (!b9_parse(state, rdatastr, rec)) { @@ -1527,70 +1459,55 @@ _PUBLIC_ isc_result_t dlz_addrdataset(const char *name, const char *rdatastr, vo } /* get any existing records */ - ret = ldb_search(state->samdb, rec, &res, dn, LDB_SCOPE_BASE, attrs, "objectClass=dnsNode"); - if (ret == LDB_ERR_NO_SUCH_OBJECT) { - if (!b9_set_session_info(state, name)) { - talloc_free(rec); - return ISC_R_FAILURE; - } - result = b9_add_record(state, name, dn, rec); - b9_reset_session_info(state); + werr = dns_common_lookup(state->samdb, rec, dn, + &recs, &num_recs, &tombstoned); + if (W_ERROR_EQUAL(werr, WERR_DNS_ERROR_NAME_DOES_NOT_EXIST)) { + needs_add = true; + werr = WERR_OK; + } + if (!W_ERROR_IS_OK(werr)) { + state->log(ISC_LOG_ERROR, "samba_dlz: failed to parse dnsRecord for %s, %s", + ldb_dn_get_linearized(dn), win_errstr(werr)); talloc_free(rec); - if (result == ISC_R_SUCCESS) { - state->log(ISC_LOG_INFO, "samba_dlz: added %s %s", name, rdatastr); - } - return result; + return ISC_R_FAILURE; } - el = ldb_msg_find_element(res->msgs[0], "dnsRecord"); - if (el == NULL) { - ret = ldb_msg_add_empty(res->msgs[0], "dnsRecord", LDB_FLAG_MOD_ADD, &el); - if (ret != LDB_SUCCESS) { - state->log(ISC_LOG_ERROR, "samba_dlz: failed to add dnsRecord for %s", - ldb_dn_get_linearized(dn)); - talloc_free(rec); - return ISC_R_FAILURE; - } + if (tombstoned) { + /* + * we need to keep the existing tombstone record + * and ignore it + */ + first = num_recs; } /* there are existing records. We need to see if this will * replace a record or add to it */ - for (i=0; i<el->num_values; i++) { - struct dnsp_DnssrvRpcRecord rec2; - - ndr_err = ndr_pull_struct_blob(&el->values[i], rec, &rec2, - (ndr_pull_flags_fn_t)ndr_pull_dnsp_DnssrvRpcRecord); - if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { - state->log(ISC_LOG_ERROR, "samba_dlz: failed to parse dnsRecord for %s", - ldb_dn_get_linearized(dn)); - talloc_free(rec); - return ISC_R_FAILURE; - } - - if (b9_record_match(state, rec, &rec2)) { + for (i=first; i < num_recs; i++) { + if (b9_record_match(state, rec, &recs[i])) { break; } } - if (i == el->num_values) { + if (i == UINT16_MAX) { + state->log(ISC_LOG_ERROR, "samba_dlz: failed to already %u dnsRecord values for %s", + i, ldb_dn_get_linearized(dn)); + talloc_free(rec); + return ISC_R_FAILURE; + } + + if (i == num_recs) { /* adding a new value */ - el->values = talloc_realloc(el, el->values, struct ldb_val, el->num_values+1); - if (el->values == NULL) { + recs = talloc_realloc(rec, recs, + struct dnsp_DnssrvRpcRecord, + num_recs + 1); + if (recs == NULL) { talloc_free(rec); return ISC_R_NOMEMORY; } - el->num_values++; - } - - ndr_err = ndr_push_struct_blob(&el->values[i], rec, rec, - (ndr_push_flags_fn_t)ndr_push_dnsp_DnssrvRpcRecord); - if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { - state->log(ISC_LOG_ERROR, "samba_dlz: failed to push dnsRecord for %s", - ldb_dn_get_linearized(dn)); - talloc_free(rec); - return ISC_R_FAILURE; + num_recs++; } + recs[i] = *rec; if (!b9_set_session_info(state, name)) { talloc_free(rec); @@ -1598,12 +1515,15 @@ _PUBLIC_ isc_result_t dlz_addrdataset(const char *name, const char *rdatastr, vo } /* modify the record */ - el->flags = LDB_FLAG_MOD_REPLACE; - ret = ldb_modify(state->samdb, res->msgs[0]); + werr = dns_common_replace(state->samdb, rec, dn, + needs_add, + state->soa_serial, + recs, num_recs); b9_reset_session_info(state); - if (ret != LDB_SUCCESS) { - state->log(ISC_LOG_ERROR, "samba_dlz: failed to modify %s - %s", - ldb_dn_get_linearized(dn), ldb_errstring(state->samdb)); + if (!W_ERROR_IS_OK(werr)) { + state->log(ISC_LOG_ERROR, "samba_dlz: failed to %s %s - %s", + needs_add ? "add" : "modify", + ldb_dn_get_linearized(dn), win_errstr(werr)); talloc_free(rec); return ISC_R_FAILURE; } @@ -1623,11 +1543,10 @@ _PUBLIC_ isc_result_t dlz_subrdataset(const char *name, const char *rdatastr, vo struct dnsp_DnssrvRpcRecord *rec; struct ldb_dn *dn; isc_result_t result; - struct ldb_result *res; - const char *attrs[] = { "dnsRecord", NULL }; - int ret, i; - struct ldb_message_element *el; - enum ndr_err_code ndr_err; + struct dnsp_DnssrvRpcRecord *recs = NULL; + uint16_t num_recs = 0; + uint16_t i; + WERROR werr; if (state->transaction_token != (void*)version) { state->log(ISC_LOG_ERROR, "samba_dlz: bad transaction version"); @@ -1653,64 +1572,40 @@ _PUBLIC_ isc_result_t dlz_subrdataset(const char *name, const char *rdatastr, vo } /* get the existing records */ - ret = ldb_search(state->samdb, rec, &res, dn, LDB_SCOPE_BASE, attrs, "objectClass=dnsNode"); - if (ret == LDB_ERR_NO_SUCH_OBJECT) { + werr = dns_common_lookup(state->samdb, rec, dn, + &recs, &num_recs, NULL); + if (!W_ERROR_IS_OK(werr)) { talloc_free(rec); return ISC_R_NOTFOUND; } - /* there are existing records. We need to see if any match - */ - el = ldb_msg_find_element(res->msgs[0], "dnsRecord"); - if (el == NULL || el->num_values == 0) { - state->log(ISC_LOG_ERROR, "samba_dlz: no dnsRecord attribute for %s", - ldb_dn_get_linearized(dn)); - talloc_free(rec); - return ISC_R_FAILURE; - } - - for (i=0; i<el->num_values; i++) { - struct dnsp_DnssrvRpcRecord rec2; - - ndr_err = ndr_pull_struct_blob(&el->values[i], rec, &rec2, - (ndr_pull_flags_fn_t)ndr_pull_dnsp_DnssrvRpcRecord); - if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { - state->log(ISC_LOG_ERROR, "samba_dlz: failed to parse dnsRecord for %s", - ldb_dn_get_linearized(dn)); - talloc_free(rec); - return ISC_R_FAILURE; - } - - if (b9_record_match(state, rec, &rec2)) { + for (i=0; i < num_recs; i++) { + if (b9_record_match(state, rec, &recs[i])) { + recs[i] = (struct dnsp_DnssrvRpcRecord) { + .wType = DNS_TYPE_TOMBSTONE, + }; break; } } - if (i == el->num_values) { + if (i == num_recs) { talloc_free(rec); return ISC_R_NOTFOUND; } - if (i < el->num_values-1) { - memmove(&el->values[i], &el->values[i+1], sizeof(el->values[0])*((el->num_values-1)-i)); - } - el->num_values--; - if (!b9_set_session_info(state, name)) { talloc_free(rec); return ISC_R_FAILURE; } - if (el->num_values == 0) { - el->flags = LDB_FLAG_MOD_DELETE; - } else { - el->flags = LDB_FLAG_MOD_REPLACE; - } - ret = ldb_modify(state->samdb, res->msgs[0]); - + /* modify the record */ + werr = dns_common_replace(state->samdb, rec, dn, + false,/* needs_add */ + state->soa_serial, + recs, num_recs); b9_reset_session_info(state); - if (ret != LDB_SUCCESS) { + if (!W_ERROR_IS_OK(werr)) { state->log(ISC_LOG_ERROR, "samba_dlz: failed to modify %s - %s", - ldb_dn_get_linearized(dn), ldb_errstring(state->samdb)); + ldb_dn_get_linearized(dn), win_errstr(werr)); talloc_free(rec); return ISC_R_FAILURE; } @@ -1731,13 +1626,12 @@ _PUBLIC_ isc_result_t dlz_delrdataset(const char *name, const char *type, void * TALLOC_CTX *tmp_ctx; struct ldb_dn *dn; isc_result_t result; - struct ldb_result *res; - const char *attrs[] = { "dnsRecord", NULL }; - int ret, i; - struct ldb_message_element *el; - enum ndr_err_code ndr_err; enum dns_record_type dns_type; bool found = false; + struct dnsp_DnssrvRpcRecord *recs = NULL; + uint16_t num_recs = 0; + uint16_t ri = 0; + WERROR werr; if (state->transaction_token != (void*)version) { state->log(ISC_LOG_ERROR, "samba_dlz: bad transaction version"); @@ -1759,41 +1653,22 @@ _PUBLIC_ isc_result_t dlz_delrdataset(const char *name, const char *type, void * } /* get the existing records */ - ret = ldb_search(state->samdb, tmp_ctx, &res, dn, LDB_SCOPE_BASE, attrs, "objectClass=dnsNode"); - if (ret == LDB_ERR_NO_SUCH_OBJECT) { - talloc_free(tmp_ctx); - return ISC_R_NOTFOUND; - } - - /* there are existing records. We need to see if any match the type - */ - el = ldb_msg_find_element(res->msgs[0], "dnsRecord"); - if (el == NULL || el->num_values == 0) { + werr = dns_common_lookup(state->samdb, tmp_ctx, dn, + &recs, &num_recs, NULL); + if (!W_ERROR_IS_OK(werr)) { talloc_free(tmp_ctx); return ISC_R_NOTFOUND; } - for (i=0; i<el->num_values; i++) { - struct dnsp_DnssrvRpcRecord rec2; - - ndr_err = ndr_pull_struct_blob(&el->values[i], tmp_ctx, &rec2, - (ndr_pull_flags_fn_t)ndr_pull_dnsp_DnssrvRpcRecord); - if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { - state->log(ISC_LOG_ERROR, "samba_dlz: failed to parse dnsRecord for %s", - ldb_dn_get_linearized(dn)); - talloc_free(tmp_ctx); - return ISC_R_FAILURE; + for (ri=0; ri < num_recs; ri++) { + if (dns_type != recs[ri].wType) { + continue; } - if (dns_type == rec2.wType) { - if (i < el->num_values-1) { - memmove(&el->values[i], &el->values[i+1], - sizeof(el->values[0])*((el->num_values-1)-i)); - } - el->num_values--; - i--; - found = true; - } + found = true; + recs[ri] = (struct dnsp_DnssrvRpcRecord) { + .wType = DNS_TYPE_TOMBSTONE, + }; } if (!found) { @@ -1806,17 +1681,15 @@ _PUBLIC_ isc_result_t dlz_delrdataset(const char *name, const char *type, void * return ISC_R_FAILURE; } - if (el->num_values == 0) { - el->flags = LDB_FLAG_MOD_DELETE; - } else { - el->flags = LDB_FLAG_MOD_REPLACE; - } - ret = ldb_modify(state->samdb, res->msgs[0]); - + /* modify the record */ + werr = dns_common_replace(state->samdb, tmp_ctx, dn, + false,/* needs_add */ + state->soa_serial, + recs, num_recs); b9_reset_session_info(state); - if (ret != LDB_SUCCESS) { - state->log(ISC_LOG_ERROR, "samba_dlz: failed to delete type %s in %s - %s", - type, ldb_dn_get_linearized(dn), ldb_errstring(state->samdb)); + if (!W_ERROR_IS_OK(werr)) { + state->log(ISC_LOG_ERROR, "samba_dlz: failed to modify %s - %s", + ldb_dn_get_linearized(dn), win_errstr(werr)); talloc_free(tmp_ctx); return ISC_R_FAILURE; } diff --git a/source4/dns_server/dlz_minimal.h b/source4/dns_server/dlz_minimal.h index 5262cbdaa6a..98fb34e9745 100644 --- a/source4/dns_server/dlz_minimal.h +++ b/source4/dns_server/dlz_minimal.h @@ -100,7 +100,7 @@ int dlz_version(unsigned int *flags); /* * dlz_create() is required for all DLZ external drivers. */ -isc_result_t dlz_create(const char *dlzname, unsigned int argc, char *argv[], void **dbdata, ...); +isc_result_t dlz_create(const char *dlzname, unsigned int argc, const char **argv, void **dbdata, ...); /* * dlz_destroy() is optional, and will be called when the driver is diff --git a/source4/dns_server/dns_server.h b/source4/dns_server/dns_server.h index efe4db822fc..12ccc9b8999 100644 --- a/source4/dns_server/dns_server.h +++ b/source4/dns_server/dns_server.h @@ -86,7 +86,6 @@ WERROR dns_server_process_update(struct dns_server *dns, struct dns_res_rec **updates, uint16_t *update_count, struct dns_res_rec **additional, uint16_t *arcount); -uint8_t werr_to_dns_err(WERROR werror); bool dns_name_match(const char *zone, const char *name, size_t *host_part_len); bool dns_name_equal(const char *name1, const char *name2); bool dns_records_match(struct dnsp_DnssrvRpcRecord *rec1, @@ -102,7 +101,7 @@ WERROR dns_replace_records(struct dns_server *dns, TALLOC_CTX *mem_ctx, struct ldb_dn *dn, bool needs_add, - const struct dnsp_DnssrvRpcRecord *records, + struct dnsp_DnssrvRpcRecord *records, uint16_t rec_count); WERROR dns_name2dn(struct dns_server *dns, TALLOC_CTX *mem_ctx, @@ -124,5 +123,6 @@ WERROR dns_sign_tsig(struct dns_server *dns, struct dns_name_packet *packet, uint16_t error); -#define DNS_ERR(err_str) WERR_DNS_ERROR_RCODE_##err_str +#include "source4/dns_server/dnsserver_common.h" + #endif /* __DNS_SERVER_H__ */ diff --git a/source4/dns_server/dns_update.c b/source4/dns_server/dns_update.c index 9edc40bc341..04e7d9adff7 100644 --- a/source4/dns_server/dns_update.c +++ b/source4/dns_server/dns_update.c @@ -82,6 +82,9 @@ static WERROR check_one_prerequisite(struct dns_server *dns, /* */ werror = dns_lookup_records(dns, mem_ctx, dn, &ans, &acount); + if (W_ERROR_EQUAL(werror, WERR_DNS_ERROR_NAME_DOES_NOT_EXIST)) { + return DNS_ERR(NAME_ERROR); + } W_ERROR_NOT_OK_RETURN(werror); if (acount == 0) { @@ -91,6 +94,9 @@ static WERROR check_one_prerequisite(struct dns_server *dns, /* */ werror = dns_lookup_records(dns, mem_ctx, dn, &ans, &acount); + if (W_ERROR_EQUAL(werror, WERR_DNS_ERROR_NAME_DOES_NOT_EXIST)) { + return DNS_ERR(NXRRSET); + } if (W_ERROR_EQUAL(werror, DNS_ERR(NAME_ERROR))) { return DNS_ERR(NXRRSET); } @@ -131,10 +137,11 @@ static WERROR check_one_prerequisite(struct dns_server *dns, /* */ werror = dns_lookup_records(dns, mem_ctx, dn, &ans, &acount); + if (W_ERROR_EQUAL(werror, WERR_DNS_ERROR_NAME_DOES_NOT_EXIST)) { + werror = WERR_OK; + } if (W_ERROR_EQUAL(werror, DNS_ERR(NAME_ERROR))) { werror = WERR_OK; - ans = NULL; - acount = 0; } for (i = 0; i < acount; i++) { @@ -163,6 +170,9 @@ static WERROR check_one_prerequisite(struct dns_server *dns, *final_result = false; werror = dns_lookup_records(dns, mem_ctx, dn, &ans, &acount); + if (W_ERROR_EQUAL(werror, WERR_DNS_ERROR_NAME_DOES_NOT_EXIST)) { + return DNS_ERR(NXRRSET); + } if (W_ERROR_EQUAL(werror, DNS_ERR(NAME_ERROR))) { return DNS_ERR(NXRRSET); } @@ -302,8 +312,6 @@ static WERROR dns_rr_to_dnsp(TALLOC_CTX *mem_ctx, r->wType = rrec->rr_type; r->dwTtlSeconds = rrec->ttl; r->rank = DNS_RANK_ZONE; - /* TODO: Autogenerate this somehow */ - r->dwSerial = 110; /* If we get QCLASS_ANY, we're done here */ if (rrec->rr_class == DNS_QCLASS_ANY) { @@ -392,7 +400,9 @@ static WERROR handle_one_update(struct dns_server *dns, uint16_t rcount = 0; struct ldb_dn *dn; uint16_t i; + uint16_t first = 0; WERROR werror; + bool tombstoned = false; bool needs_add = false; DEBUG(2, ("Looking at record: \n")); @@ -420,22 +430,29 @@ static WERROR handle_one_update(struct dns_server *dns, werror = dns_name2dn(dns, mem_ctx, update->name, &dn); W_ERROR_NOT_OK_RETURN(werror); - werror = dns_lookup_records(dns, mem_ctx, dn, &recs, &rcount); - if (W_ERROR_EQUAL(werror, DNS_ERR(NAME_ERROR))) { - recs = NULL; - rcount = 0; + werror = dns_common_lookup(dns->samdb, mem_ctx, dn, + &recs, &rcount, &tombstoned); + if (W_ERROR_EQUAL(werror, WERR_DNS_ERROR_NAME_DOES_NOT_EXIST)) { needs_add = true; werror = WERR_OK; } W_ERROR_NOT_OK_RETURN(werror); + if (tombstoned) { + /* + * we need to keep the existing tombstone record + * and ignore it + */ + first = rcount; + } + if (update->rr_class == zone->question_class) { if (update->rr_type == DNS_QTYPE_CNAME) { /* * If there is a record in the directory * that's not a CNAME, ignore update */ - for (i = 0; i < rcount; i++) { + for (i = first; i < rcount; i++) { if (recs[i].wType != DNS_TYPE_CNAME) { DEBUG(5, ("Skipping update\n")); return WERR_OK; @@ -448,13 +465,14 @@ static WERROR handle_one_update(struct dns_server *dns, * per name, so replace everything with the new CNAME */ - rcount = 1; + rcount = first; recs = talloc_realloc(mem_ctx, recs, - struct dnsp_DnssrvRpcRecord, rcount); + struct dnsp_DnssrvRpcRecord, rcount + 1); W_ERROR_HAVE_NO_MEMORY(recs); - werror = dns_rr_to_dnsp(recs, update, &recs[0]); + werror = dns_rr_to_dnsp(recs, update, &recs[rcount]); W_ERROR_NOT_OK_RETURN(werror); + rcount += 1; werror = dns_replace_records(dns, mem_ctx, dn, needs_add, recs, rcount); @@ -466,7 +484,7 @@ static WERROR handle_one_update(struct dns_server *dns, * If there is a CNAME record for this name, * ignore update */ - for (i = 0; i < rcount; i++) { + for (i = first; i < rcount; i++) { if (recs[i].wType == DNS_TYPE_CNAME) { DEBUG(5, ("Skipping update\n")); return WERR_OK; @@ -481,7 +499,7 @@ static WERROR handle_one_update(struct dns_server *dns, * serial number is smaller than existing SOA's, * ignore update */ - for (i = 0; i < rcount; i++) { + for (i = first; i < rcount; i++) { if (recs[i].wType == DNS_TYPE_SOA) { uint16_t n, o; @@ -512,7 +530,9 @@ static WERROR handle_one_update(struct dns_server *dns, continue; } - ZERO_STRUCT(recs[i]); + recs[i] = (struct dnsp_DnssrvRpcRecord) { + .wType = DNS_TYPE_TOMBSTONE, + }; } werror = dns_replace_records(dns, mem_ctx, dn, @@ -529,7 +549,7 @@ static WERROR handle_one_update(struct dns_server *dns, werror = dns_rr_to_dnsp(recs, update, &recs[rcount]); W_ERROR_NOT_OK_RETURN(werror); - for (i = 0; i < rcount; i++) { + for (i = first; i < rcount; i++) { if (!dns_records_match(&recs[i], &recs[rcount])) { continue; } @@ -551,7 +571,7 @@ static WERROR handle_one_update(struct dns_server *dns, } else if (update->rr_class == DNS_QCLASS_ANY) { if (update->rr_type == DNS_QTYPE_ALL) { if (dns_name_equal(update->name, zone->name)) { - for (i = 0; i < rcount; i++) { + for (i = first; i < rcount; i++) { if (recs[i].wType == DNS_TYPE_SOA) { continue; @@ -561,12 +581,16 @@ static WERROR handle_one_update(struct dns_server *dns, continue; } - ZERO_STRUCT(recs[i]); + recs[i] = (struct dnsp_DnssrvRpcRecord) { + .wType = DNS_TYPE_TOMBSTONE, + }; } } else { - for (i = 0; i < rcount; i++) { - ZERO_STRUCT(recs[i]); + for (i = first; i < rcount; i++) { + recs[i] = (struct dnsp_DnssrvRpcRecord) { + .wType = DNS_TYPE_TOMBSTONE, + }; } } @@ -580,9 +604,11 @@ static WERROR handle_one_update(struct dns_server *dns, return WERR_OK; } } - for (i = 0; i < rcount; i++) { + for (i = first; i < rcount; i++) { if (recs[i].wType == update->rr_type) { - ZERO_STRUCT(recs[i]); + recs[i] = (struct dnsp_DnssrvRpcRecord) { + .wType = DNS_TYPE_TOMBSTONE, + }; } } @@ -607,7 +633,7 @@ static WERROR handle_one_update(struct dns_server *dns, werror = dns_rr_to_dnsp(ns_rec, update, ns_rec); W_ERROR_NOT_OK_RETURN(werror); - for (i = 0; i < rcount; i++) { + for (i = first; i < rcount; i++) { if (dns_records_match(ns_rec, &recs[i])) { found = true; break; @@ -624,9 +650,11 @@ static WERROR handle_one_update(struct dns_server *dns, werror = dns_rr_to_dnsp(del_rec, update, del_rec); W_ERROR_NOT_OK_RETURN(werror); - for (i = 0; i < rcount; i++) { + for (i = first; i < rcount; i++) { if (dns_records_match(del_rec, &recs[i])) { - ZERO_STRUCT(recs[i]); + recs[i] = (struct dnsp_DnssrvRpcRecord) { + .wType = DNS_TYPE_TOMBSTONE, + }; } } diff --git a/source4/dns_server/dns_utils.c b/source4/dns_server/dns_utils.c index 72782cf4500..c757c157626 100644 --- a/source4/dns_server/dns_utils.c +++ b/source4/dns_server/dns_utils.c @@ -33,37 +33,6 @@ #undef DBGC_CLASS #define DBGC_CLASS DBGC_DNS -uint8_t werr_to_dns_err(WERROR werr) -{ - if (W_ERROR_EQUAL(WERR_OK, werr)) { - return DNS_RCODE_OK; - } else if (W_ERROR_EQUAL(DNS_ERR(FORMAT_ERROR), werr)) { - return DNS_RCODE_FORMERR; - } else if (W_ERROR_EQUAL(DNS_ERR(SERVER_FAILURE), werr)) { - return DNS_RCODE_SERVFAIL; - } else if (W_ERROR_EQUAL(DNS_ERR(NAME_ERROR), werr)) { - return DNS_RCODE_NXDOMAIN; - } else if (W_ERROR_EQUAL(DNS_ERR(NOT_IMPLEMENTED), werr)) { - return DNS_RCODE_NOTIMP; - } else if (W_ERROR_EQUAL(DNS_ERR(REFUSED), werr)) { - return DNS_RCODE_REFUSED; - } else if (W_ERROR_EQUAL(DNS_ERR(YXDOMAIN), werr)) { - return DNS_RCODE_YXDOMAIN; - } else if (W_ERROR_EQUAL(DNS_ERR(YXRRSET), werr)) { - return DNS_RCODE_YXRRSET; - } else if (W_ERROR_EQUAL(DNS_ERR(NXRRSET), werr)) { - return DNS_RCODE_NXRRSET; - } else if (W_ERROR_EQUAL(DNS_ERR(NOTAUTH), werr)) { - return DNS_RCODE_NOTAUTH; - } else if (W_ERROR_EQUAL(DNS_ERR(NOTZONE), werr)) { - return DNS_RCODE_NOTZONE; - } else if (W_ERROR_EQUAL(DNS_ERR(BADKEY), werr)) { - return DNS_RCODE_BADKEY; - } - DEBUG(5, ("No mapping exists for %s\n", win_errstr(werr))); - return DNS_RCODE_SERVFAIL; -} - bool dns_name_match(const char *zone, const char *name, size_t *host_part_len) { size_t zl = strlen(zone); @@ -185,126 +154,21 @@ WERROR dns_lookup_records(struct dns_server *dns, struct dnsp_DnssrvRpcRecord **records, uint16_t *rec_count) { - static const char * const attrs[] = { "dnsRecord", NULL}; - struct ldb_message_element *el; - uint16_t ri; - int ret; - struct ldb_message *msg = NULL; - struct dnsp_DnssrvRpcRecord *recs; - - ret = dsdb_search_one(dns->samdb, mem_ctx, &msg, dn, - LDB_SCOPE_BASE, attrs, 0, "%s", "(objectClass=dnsNode)"); - if (ret != LDB_SUCCESS) { - /* TODO: we need to check if there's a glue record we need to - * create a referral to */ - return DNS_ERR(NAME_ERROR); - } - - el = ldb_msg_find_element(msg, attrs[0]); - if (el == NULL) { - *records = NULL; - *rec_count = 0; - return DNS_ERR(NAME_ERROR); - } - - recs = talloc_zero_array(mem_ctx, struct dnsp_DnssrvRpcRecord, el->num_values); - if (recs == NULL) { - return WERR_NOMEM; - } - for (ri = 0; ri < el->num_values; ri++) { - struct ldb_val *v = &el->values[ri]; - enum ndr_err_code ndr_err; - - ndr_err = ndr_pull_struct_blob(v, recs, &recs[ri], - (ndr_pull_flags_fn_t)ndr_pull_dnsp_DnssrvRpcRecord); - if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { - DEBUG(0, ("Failed to grab dnsp_DnssrvRpcRecord\n")); - return DNS_ERR(SERVER_FAILURE); - } - } - *records = recs; - *rec_count = el->num_values; - return WERR_OK; + return dns_common_lookup(dns->samdb, mem_ctx, dn, + records, rec_count, NULL); } WERROR dns_replace_records(struct dns_server *dns, TALLOC_CTX *mem_ctx, struct ldb_dn *dn, bool needs_add, - const struct dnsp_DnssrvRpcRecord *records, + struct dnsp_DnssrvRpcRecord *records, uint16_t rec_count) { - struct ldb_message_element *el; - uint16_t i; - int ret; - struct ldb_message *msg = NULL; - - msg = ldb_msg_new(mem_ctx); - W_ERROR_HAVE_NO_MEMORY(msg); - - msg->dn = dn; - - ret = ldb_msg_add_empty(msg, "dnsRecord", LDB_FLAG_MOD_REPLACE, &el); - if (ret != LDB_SUCCESS) { - return DNS_ERR(SERVER_FAILURE); - } - - el->values = talloc_zero_array(el, struct ldb_val, rec_count); - if (rec_count > 0) { - W_ERROR_HAVE_NO_MEMORY(el->values); - } - - for (i = 0; i < rec_count; i++) { - static const struct dnsp_DnssrvRpcRecord zero; - struct ldb_val *v = &el->values[el->num_values]; - enum ndr_err_code ndr_err; - - if (memcmp(&records[i], &zero, sizeof(zero)) == 0) { - continue; - } - ndr_err = ndr_push_struct_blob(v, el->values, &records[i], - (ndr_push_flags_fn_t)ndr_push_dnsp_DnssrvRpcRecord); - if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { - DEBUG(0, ("Failed to grab dnsp_DnssrvRpcRecord\n")); - return DNS_ERR(SERVER_FAILURE); - } - el->num_values++; - } - - - if (el->num_values == 0) { - if (needs_add) { - return WERR_OK; - } - /* No entries left, delete the dnsNode object */ - ret = ldb_delete(dns->samdb, msg->dn); - if (ret != LDB_SUCCESS) { - DEBUG(0, ("Deleting record failed; %d\n", ret)); - return DNS_ERR(SERVER_FAILURE); - } - return WERR_OK; - } - - if (needs_add) { - ret = ldb_msg_add_string(msg, "objectClass", "dnsNode"); - if (ret != LDB_SUCCESS) { - return DNS_ERR(SERVER_FAILURE); - } - - ret = ldb_add(dns->samdb, msg); - if (ret != LDB_SUCCESS) { - return DNS_ERR(SERVER_FAILURE); - } - - return WERR_OK; - } - - ret = ldb_modify(dns->samdb, msg); - if (ret != LDB_SUCCESS) { - return DNS_ERR(SERVER_FAILURE); - } - - return WERR_OK; + /* TODO: Autogenerate this somehow */ + uint32_t dwSerial = 110; + return dns_common_replace(dns->samdb, mem_ctx, dn, + needs_add, dwSerial, records, rec_count); } bool dns_authorative_for_zone(struct dns_server *dns, diff --git a/source4/dns_server/dnsserver_common.c b/source4/dns_server/dnsserver_common.c new file mode 100644 index 00000000000..c49d6ec87a6 --- /dev/null +++ b/source4/dns_server/dnsserver_common.c @@ -0,0 +1,342 @@ +/* + Unix SMB/CIFS implementation. + + DNS server utils + + Copyright (C) 2010 Kai Blin + Copyright (C) 2014 Stefan Metzmacher + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "libcli/util/ntstatus.h" +#include "libcli/util/werror.h" +#include "librpc/ndr/libndr.h" +#include "librpc/gen_ndr/ndr_dns.h" +#include "librpc/gen_ndr/ndr_dnsp.h" +#include <ldb.h> +#include "dsdb/samdb/samdb.h" +#include "dsdb/common/util.h" +#include "dns_server/dnsserver_common.h" + +#undef DBGC_CLASS +#define DBGC_CLASS DBGC_DNS + +uint8_t werr_to_dns_err(WERROR werr) +{ + if (W_ERROR_EQUAL(WERR_OK, werr)) { + return DNS_RCODE_OK; + } else if (W_ERROR_EQUAL(DNS_ERR(FORMAT_ERROR), werr)) { + return DNS_RCODE_FORMERR; + } else if (W_ERROR_EQUAL(DNS_ERR(SERVER_FAILURE), werr)) { + return DNS_RCODE_SERVFAIL; + } else if (W_ERROR_EQUAL(DNS_ERR(NAME_ERROR), werr)) { + return DNS_RCODE_NXDOMAIN; + } else if (W_ERROR_EQUAL(WERR_DNS_ERROR_NAME_DOES_NOT_EXIST, werr)) { + return DNS_RCODE_NXDOMAIN; + } else if (W_ERROR_EQUAL(DNS_ERR(NOT_IMPLEMENTED), werr)) { + return DNS_RCODE_NOTIMP; + } else if (W_ERROR_EQUAL(DNS_ERR(REFUSED), werr)) { + return DNS_RCODE_REFUSED; + } else if (W_ERROR_EQUAL(DNS_ERR(YXDOMAIN), werr)) { + return DNS_RCODE_YXDOMAIN; + } else if (W_ERROR_EQUAL(DNS_ERR(YXRRSET), werr)) { + return DNS_RCODE_YXRRSET; + } else if (W_ERROR_EQUAL(DNS_ERR(NXRRSET), werr)) { + return DNS_RCODE_NXRRSET; + } else if (W_ERROR_EQUAL(DNS_ERR(NOTAUTH), werr)) { + return DNS_RCODE_NOTAUTH; + } else if (W_ERROR_EQUAL(DNS_ERR(NOTZONE), werr)) { + return DNS_RCODE_NOTZONE; + } else if (W_ERROR_EQUAL(DNS_ERR(BADKEY), werr)) { + return DNS_RCODE_BADKEY; + } + DEBUG(5, ("No mapping exists for %s\n", win_errstr(werr))); + return DNS_RCODE_SERVFAIL; +} + +WERROR dns_common_extract(const struct ldb_message_element *el, + TALLOC_CTX *mem_ctx, + struct dnsp_DnssrvRpcRecord **records, + uint16_t *num_records) +{ + uint16_t ri; + struct dnsp_DnssrvRpcRecord *recs; + + *records = NULL; + *num_records = 0; + + recs = talloc_zero_array(mem_ctx, struct dnsp_DnssrvRpcRecord, + el->num_values); + if (recs == NULL) { + return WERR_NOMEM; + } + for (ri = 0; ri < el->num_values; ri++) { + struct ldb_val *v = &el->values[ri]; + enum ndr_err_code ndr_err; + + ndr_err = ndr_pull_struct_blob(v, recs, &recs[ri], + (ndr_pull_flags_fn_t)ndr_pull_dnsp_DnssrvRpcRecord); + if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { + TALLOC_FREE(recs); + DEBUG(0, ("Failed to grab dnsp_DnssrvRpcRecord\n")); + return DNS_ERR(SERVER_FAILURE); + } + } + *records = recs; + *num_records = el->num_values; + return WERR_OK; +} + +WERROR dns_common_lookup(struct ldb_context *samdb, + TALLOC_CTX *mem_ctx, + struct ldb_dn *dn, + struct dnsp_DnssrvRpcRecord **records, + uint16_t *num_records, + bool *tombstoned) +{ + static const char * const attrs[] = { + "dnsRecord", + "dNSTombstoned", + NULL + }; + int ret; + WERROR werr; + struct ldb_message *msg = NULL; + struct ldb_message_element *el; + + *records = NULL; + *num_records = 0; + + if (tombstoned != NULL) { + *tombstoned = false; + ret = dsdb_search_one(samdb, mem_ctx, &msg, dn, + LDB_SCOPE_BASE, attrs, 0, + "(objectClass=dnsNode)"); + } else { + ret = dsdb_search_one(samdb, mem_ctx, &msg, dn, + LDB_SCOPE_BASE, attrs, 0, + "(&(objectClass=dnsNode)(!(dNSTombstoned=TRUE)))"); + } + if (ret == LDB_ERR_NO_SUCH_OBJECT) { + return WERR_DNS_ERROR_NAME_DOES_NOT_EXIST; + } + if (ret != LDB_SUCCESS) { + /* TODO: we need to check if there's a glue record we need to + * create a referral to */ + return DNS_ERR(NAME_ERROR); + } + + if (tombstoned != NULL) { + *tombstoned = ldb_msg_find_attr_as_bool(msg, + "dNSTombstoned", false); + } + + el = ldb_msg_find_element(msg, "dnsRecord"); + if (el == NULL) { + TALLOC_FREE(msg); + if (tombstoned != NULL) { + struct dnsp_DnssrvRpcRecord *recs; + /* + * records produced by older Samba releases + * keep dnsNode objects without dnsRecord and + * without setting dNSTombstoned=TRUE. + * + * We just pretend they're tombstones. + */ + recs = talloc_array(mem_ctx, + struct dnsp_DnssrvRpcRecord, + 1); + if (recs == NULL) { + return WERR_NOMEM; + } + recs[0] = (struct dnsp_DnssrvRpcRecord) { + .wType = DNS_TYPE_TOMBSTONE, + /* + * A value of timestamp != 0 + * indicated that the object was already + * a tombstone, this will be used + * in dns_common_replace() + */ + .data.timestamp = 1, + }; + + *tombstoned = true; + *records = recs; + *num_records = 1; + return WERR_OK; + } + return DNS_ERR(NAME_ERROR); + } + + werr = dns_common_extract(el, mem_ctx, records, num_records); + TALLOC_FREE(msg); + if (!W_ERROR_IS_OK(werr)) { + return werr; + } + + return WERR_OK; +} + +static int rec_cmp(const struct dnsp_DnssrvRpcRecord *r1, + const struct dnsp_DnssrvRpcRecord *r2) +{ + if (r1->wType != r2->wType) { + /* + * The records are sorted with higher types first + */ + return r2->wType - r1->wType; + } + + /* + * Then we need to sort from the oldest to newest timestamp + */ + return r1->dwTimeStamp - r2->dwTimeStamp; +} + +WERROR dns_common_replace(struct ldb_context *samdb, + TALLOC_CTX *mem_ctx, + struct ldb_dn *dn, + bool needs_add, + uint32_t serial, + struct dnsp_DnssrvRpcRecord *records, + uint16_t rec_count) +{ + struct ldb_message_element *el; + uint16_t i; + int ret; + struct ldb_message *msg = NULL; + bool was_tombstoned = false; + bool become_tombstoned = false; + + msg = ldb_msg_new(mem_ctx); + W_ERROR_HAVE_NO_MEMORY(msg); + + msg->dn = dn; + + ret = ldb_msg_add_empty(msg, "dnsRecord", LDB_FLAG_MOD_REPLACE, &el); + if (ret != LDB_SUCCESS) { + return DNS_ERR(SERVER_FAILURE); + } + + /* + * we have at least one value, + * which might be used for the tombstone marker + */ + el->values = talloc_zero_array(el, struct ldb_val, MAX(1, rec_count)); + if (rec_count > 0) { + W_ERROR_HAVE_NO_MEMORY(el->values); + + /* + * We store a sorted list with the high wType values first + * that's what windows does. It also simplifies the + * filtering of DNS_TYPE_TOMBSTONE records + */ + TYPESAFE_QSORT(records, rec_count, rec_cmp); + } + + for (i = 0; i < rec_count; i++) { + struct ldb_val *v = &el->values[el->num_values]; + enum ndr_err_code ndr_err; + + if (records[i].wType == DNS_TYPE_TOMBSTONE) { + if (records[i].data.timestamp != 0) { + was_tombstoned = true; + } + continue; + } + + records[i].dwSerial = serial; + ndr_err = ndr_push_struct_blob(v, el->values, &records[i], + (ndr_push_flags_fn_t)ndr_push_dnsp_DnssrvRpcRecord); + if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { + DEBUG(0, ("Failed to push dnsp_DnssrvRpcRecord\n")); + return DNS_ERR(SERVER_FAILURE); + } + el->num_values++; + } + + if (needs_add) { + if (el->num_values == 0) { + return WERR_OK; + } + + ret = ldb_msg_add_string(msg, "objectClass", "dnsNode"); + if (ret != LDB_SUCCESS) { + return DNS_ERR(SERVER_FAILURE); + } + + ret = ldb_add(samdb, msg); + if (ret != LDB_SUCCESS) { + return DNS_ERR(SERVER_FAILURE); + } + + return WERR_OK; + } + + if (el->num_values == 0) { + struct dnsp_DnssrvRpcRecord tbs; + struct ldb_val *v = &el->values[el->num_values]; + enum ndr_err_code ndr_err; + struct timeval tv; + + if (was_tombstoned) { + /* + * This is already a tombstoned object. + * Just leave it instead of updating the time stamp. + */ + return WERR_OK; + } + + tv = timeval_current(); + tbs = (struct dnsp_DnssrvRpcRecord) { + .wType = DNS_TYPE_TOMBSTONE, + .dwSerial = serial, + .data.timestamp = timeval_to_nttime(&tv), + }; + + ndr_err = ndr_push_struct_blob(v, el->values, &tbs, + (ndr_push_flags_fn_t)ndr_push_dnsp_DnssrvRpcRecord); + if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { + DEBUG(0, ("Failed to push dnsp_DnssrvRpcRecord\n")); + return DNS_ERR(SERVER_FAILURE); + } + el->num_values++; + + become_tombstoned = true; + } + + if (was_tombstoned || become_tombstoned) { + ret = ldb_msg_add_empty(msg, "dNSTombstoned", + LDB_FLAG_MOD_REPLACE, NULL); + if (ret != LDB_SUCCESS) { + return DNS_ERR(SERVER_FAILURE); + } + + ret = ldb_msg_add_fmt(msg, "dNSTombstoned", "%s", + become_tombstoned ? "TRUE" : "FALSE"); + if (ret != LDB_SUCCESS) { + return DNS_ERR(SERVER_FAILURE); + } + } + + ret = ldb_modify(samdb, msg); + if (ret != LDB_SUCCESS) { + NTSTATUS nt = dsdb_ldb_err_to_ntstatus(ret); + return ntstatus_to_werror(nt); + } + + return WERR_OK; +} diff --git a/source4/dns_server/dnsserver_common.h b/source4/dns_server/dnsserver_common.h new file mode 100644 index 00000000000..becd243f6a9 --- /dev/null +++ b/source4/dns_server/dnsserver_common.h @@ -0,0 +1,50 @@ +/* + Unix SMB/CIFS implementation. + + DNS server utils + + Copyright (C) 2014 Stefan Metzmacher + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef __DNSSERVER_COMMON_H__ +#define __DNSSERVER_COMMON_H__ + +uint8_t werr_to_dns_err(WERROR werr); +#define DNS_ERR(err_str) WERR_DNS_ERROR_RCODE_##err_str + +struct ldb_message_element; + +WERROR dns_common_extract(const struct ldb_message_element *el, + TALLOC_CTX *mem_ctx, + struct dnsp_DnssrvRpcRecord **records, + uint16_t *num_records); + +WERROR dns_common_lookup(struct ldb_context *samdb, + TALLOC_CTX *mem_ctx, + struct ldb_dn *dn, + struct dnsp_DnssrvRpcRecord **records, + uint16_t *num_records, + bool *tombstoned); + +WERROR dns_common_replace(struct ldb_context *samdb, + TALLOC_CTX *mem_ctx, + struct ldb_dn *dn, + bool needs_add, + uint32_t serial, + struct dnsp_DnssrvRpcRecord *records, + uint16_t rec_count); + +#endif /* __DNSSERVER_COMMON_H__ */ diff --git a/source4/dns_server/wscript_build b/source4/dns_server/wscript_build index 280f8de49c0..a92ab67a40f 100644 --- a/source4/dns_server/wscript_build +++ b/source4/dns_server/wscript_build @@ -1,10 +1,16 @@ #!/usr/bin/env python +bld.SAMBA_LIBRARY('dnsserver_common', + source='dnsserver_common.c', + deps='samba-util errors ldbsamba clidns', + private_library=True, + ) + bld.SAMBA_MODULE('service_dns', source='dns_server.c dns_query.c dns_update.c dns_utils.c dns_crypto.c', subsystem='service', init_function='server_service_dns_init', - deps='samba-hostconfig LIBTSOCKET LIBSAMBA_TSOCKET ldbsamba clidns gensec auth samba_server_gensec', + deps='samba-hostconfig LIBTSOCKET LIBSAMBA_TSOCKET ldbsamba clidns gensec auth samba_server_gensec dnsserver_common', local_include=False, internal_module=False, enabled=bld.AD_DC_BUILD_IS_ENABLED() @@ -18,7 +24,7 @@ bld.SAMBA_LIBRARY('dlz_bind9', link_name='modules/bind9/dlz_bind9.so', realname='dlz_bind9.so', install_path='${MODULESDIR}/bind9', - deps='samba-hostconfig samdb-common gensec popt', + deps='samba-hostconfig samdb-common gensec popt dnsserver_common', enabled=bld.AD_DC_BUILD_IS_ENABLED()) bld.SAMBA_LIBRARY('dlz_bind9_9', @@ -28,12 +34,12 @@ bld.SAMBA_LIBRARY('dlz_bind9_9', link_name='modules/bind9/dlz_bind9_9.so', realname='dlz_bind9_9.so', install_path='${MODULESDIR}/bind9', - deps='samba-hostconfig samdb-common gensec popt', + deps='samba-hostconfig samdb-common gensec popt dnsserver_common', enabled=bld.AD_DC_BUILD_IS_ENABLED()) bld.SAMBA_LIBRARY('dlz_bind9_for_torture', source='dlz_bind9.c', cflags='-DBIND_VERSION_9_8', private_library=True, - deps='samba-hostconfig samdb-common gensec popt', + deps='samba-hostconfig samdb-common gensec popt dnsserver_common', enabled=bld.AD_DC_BUILD_IS_ENABLED()) diff --git a/source4/dsdb/dns/dns_update.c b/source4/dsdb/dns/dns_update.c index 3e10447f0fc..aa617c6aeca 100644 --- a/source4/dsdb/dns/dns_update.c +++ b/source4/dsdb/dns/dns_update.c @@ -397,6 +397,7 @@ struct dnsupdate_RODC_state { struct irpc_message *msg; struct dnsupdate_RODC *r; char *tmp_path; + char *tmp_path2; int fd; }; @@ -406,6 +407,9 @@ static int dnsupdate_RODC_destructor(struct dnsupdate_RODC_state *st) close(st->fd); } unlink(st->tmp_path); + if (st->tmp_path2 != NULL) { + unlink(st->tmp_path2); + } return 0; } @@ -483,6 +487,13 @@ static NTSTATUS dnsupdate_dnsupdate_RODC(struct irpc_message *msg, talloc_set_destructor(st, dnsupdate_RODC_destructor); + st->tmp_path2 = talloc_asprintf(st, "%s.cache", st->tmp_path); + if (!st->tmp_path2) { + talloc_free(st); + r->out.result = NT_STATUS_NO_MEMORY; + return NT_STATUS_OK; + } + sid_dn = ldb_dn_new_fmt(st, s->samdb, "<SID=%s>", dom_sid_string(st, r->in.dom_sid)); if (!sid_dn) { talloc_free(st); @@ -575,6 +586,8 @@ static NTSTATUS dnsupdate_dnsupdate_RODC(struct irpc_message *msg, dns_update_command, "--update-list", st->tmp_path, + "--update-cache", + st->tmp_path2, NULL); NT_STATUS_HAVE_NO_MEMORY(req); diff --git a/source4/librpc/rpc/dcerpc_schannel.c b/source4/librpc/rpc/dcerpc_schannel.c index be1ab240bd7..1480486c320 100644 --- a/source4/librpc/rpc/dcerpc_schannel.c +++ b/source4/librpc/rpc/dcerpc_schannel.c @@ -187,6 +187,7 @@ static void continue_srv_challenge(struct tevent_req *subreq) s->creds = netlogon_creds_client_init(s, s->a.in.account_name, s->a.in.computer_name, + s->a.in.secure_channel_type, &s->credentials1, &s->credentials2, s->mach_pwd, &s->credentials3, s->local_negotiate_flags); diff --git a/source4/rpc_server/dnsserver/dcerpc_dnsserver.c b/source4/rpc_server/dnsserver/dcerpc_dnsserver.c index 5733a51177a..dee69fe2387 100644 --- a/source4/rpc_server/dnsserver/dcerpc_dnsserver.c +++ b/source4/rpc_server/dnsserver/dcerpc_dnsserver.c @@ -1625,7 +1625,8 @@ static WERROR dnsserver_enumerate_root_records(struct dnsserver_state *dsstate, } ret = ldb_search(dsstate->samdb, tmp_ctx, &res, z->zone_dn, - LDB_SCOPE_ONELEVEL, attrs, "(&(objectClass=dnsNode)(name=@))"); + LDB_SCOPE_ONELEVEL, attrs, + "(&(objectClass=dnsNode)(name=@)(!(dNSTombstoned=TRUE)))"); if (ret != LDB_SUCCESS) { talloc_free(tmp_ctx); return WERR_INTERNAL_DB_ERROR; @@ -1657,8 +1658,9 @@ static WERROR dnsserver_enumerate_root_records(struct dnsserver_state *dsstate, if (select_flag & DNS_RPC_VIEW_ADDITIONAL_DATA) { for (i=0; i<add_count; i++) { ret = ldb_search(dsstate->samdb, tmp_ctx, &res, z->zone_dn, - LDB_SCOPE_ONELEVEL, attrs, - "(&(objectClass=dnsNode)(name=%s))", add_names[i]); + LDB_SCOPE_ONELEVEL, attrs, + "(&(objectClass=dnsNode)(name=%s)(!(dNSTombstoned=TRUE)))", + add_names[i]); if (ret != LDB_SUCCESS || res->count == 0) { talloc_free(res); continue; @@ -1722,11 +1724,12 @@ static WERROR dnsserver_enumerate_records(struct dnsserver_state *dsstate, /* search all records under parent tree */ if (strcasecmp(name, z->name) == 0) { ret = ldb_search(dsstate->samdb, tmp_ctx, &res, z->zone_dn, - LDB_SCOPE_ONELEVEL, attrs, "(objectClass=dnsNode)"); + LDB_SCOPE_ONELEVEL, attrs, + "(&(objectClass=dnsNode)(!(dNSTombstoned=TRUE)))"); } else { ret = ldb_search(dsstate->samdb, tmp_ctx, &res, z->zone_dn, - LDB_SCOPE_ONELEVEL, attrs, - "(&(objectClass=dnsNode)(|(name=%s)(name=*.%s)))", + LDB_SCOPE_ONELEVEL, attrs, + "(&(objectClass=dnsNode)(|(name=%s)(name=*.%s))(!(dNSTombstoned=TRUE)))", name, name); } if (ret != LDB_SUCCESS) { @@ -1801,7 +1804,8 @@ static WERROR dnsserver_enumerate_records(struct dnsserver_state *dsstate, name = dns_split_node_name(tmp_ctx, add_names[i], z2->name); ret = ldb_search(dsstate->samdb, tmp_ctx, &res, z2->zone_dn, LDB_SCOPE_ONELEVEL, attrs, - "(&(objectClass=dnsNode)(name=%s))", name); + "(&(objectClass=dnsNode)(name=%s)(!(dNSTombstoned=TRUE)))", + name); talloc_free(name); if (ret != LDB_SUCCESS) { continue; @@ -1853,7 +1857,9 @@ static WERROR dnsserver_update_record(struct dnsserver_state *dsstate, W_ERROR_HAVE_NO_MEMORY(tmp_ctx); /* If node_name is @ or zone name, dns record is @ */ - if (strcmp(node_name, "@") == 0 || strcasecmp(node_name, z->name) == 0) { + if (strcmp(node_name, "@") == 0 || + strcmp(node_name, ".") == 0 || + strcasecmp(node_name, z->name) == 0) { name = talloc_strdup(tmp_ctx, "@"); } else { name = dns_split_node_name(tmp_ctx, node_name, z->name); diff --git a/source4/rpc_server/dnsserver/dnsdata.c b/source4/rpc_server/dnsserver/dnsdata.c index fb2547ff7ec..f752490101a 100644 --- a/source4/rpc_server/dnsserver/dnsdata.c +++ b/source4/rpc_server/dnsserver/dnsdata.c @@ -798,6 +798,15 @@ WERROR dns_fill_records_array(TALLOC_CTX *mem_ctx, if (select_flag & DNS_RPC_VIEW_AUTHORITY_DATA) { if (dnsp_rec.rank == DNS_RANK_ZONE) { found = true; + } else if (dnsp_rec.rank == DNS_RANK_NS_GLUE) { + /* + * If branch_name is NULL, we're + * explicitly asked to also return + * DNS_RANK_NS_GLUE records + */ + if (branch_name == NULL) { + found = true; + } } } if (select_flag & DNS_RPC_VIEW_CACHE_DATA) { @@ -806,7 +815,7 @@ WERROR dns_fill_records_array(TALLOC_CTX *mem_ctx, } } if (select_flag & DNS_RPC_VIEW_GLUE_DATA) { - if (dnsp_rec.rank == DNS_RANK_NS_GLUE) { + if (dnsp_rec.rank == DNS_RANK_GLUE) { found = true; } } diff --git a/source4/rpc_server/dnsserver/dnsdb.c b/source4/rpc_server/dnsserver/dnsdb.c index 8cdeae41f52..e567f5a4f80 100644 --- a/source4/rpc_server/dnsserver/dnsdb.c +++ b/source4/rpc_server/dnsserver/dnsdb.c @@ -395,7 +395,7 @@ WERROR dnsserver_db_add_record(TALLOC_CTX *mem_ctx, const char *name, struct DNS_RPC_RECORD *add_record) { - const char * const attrs[] = { "dnsRecord", NULL }; + const char * const attrs[] = { "dnsRecord", "dNSTombstoned", NULL }; struct ldb_result *res; struct dnsp_DnssrvRpcRecord *rec; struct ldb_message_element *el; @@ -404,14 +404,18 @@ WERROR dnsserver_db_add_record(TALLOC_CTX *mem_ctx, NTTIME t; int ret, i; int serial; + bool was_tombstoned = false; rec = dns_to_dnsp_copy(mem_ctx, add_record); W_ERROR_HAVE_NO_MEMORY(rec); - /* Set the correct rank for the record. - * FIXME: add logic to check for glue records */ + /* Set the correct rank for the record. */ if (z->zoneinfo->dwZoneType == DNS_ZONE_TYPE_PRIMARY) { - rec->rank |= DNS_RANK_ZONE; + if (strcmp(name, "@") != 0 && rec->wType == DNS_TYPE_NS) { + rec->rank = DNS_RANK_NS_GLUE; + } else { + rec->rank |= DNS_RANK_ZONE; + } } else if (strcmp(z->name, ".") == 0) { rec->rank |= DNS_RANK_ROOT_HINT; } @@ -449,6 +453,12 @@ WERROR dnsserver_db_add_record(TALLOC_CTX *mem_ctx, } } + was_tombstoned = ldb_msg_find_attr_as_bool(res->msgs[0], + "dNSTombstoned", false); + if (was_tombstoned) { + el->num_values = 0; + } + for (i=0; i<el->num_values; i++) { struct dnsp_DnssrvRpcRecord rec2; @@ -479,6 +489,12 @@ WERROR dnsserver_db_add_record(TALLOC_CTX *mem_ctx, } el->flags = LDB_FLAG_MOD_REPLACE; + + el = ldb_msg_find_element(res->msgs[0], "dNSTombstoned"); + if (el != NULL) { + el->flags = LDB_FLAG_MOD_DELETE; + } + ret = ldb_modify(samdb, res->msgs[0]); if (ret != LDB_SUCCESS) { return WERR_INTERNAL_DB_ERROR; @@ -517,7 +533,7 @@ WERROR dnsserver_db_update_record(TALLOC_CTX *mem_ctx, arec->dwTimeStamp = t; ret = ldb_search(samdb, mem_ctx, &res, z->zone_dn, LDB_SCOPE_ONELEVEL, attrs, - "(&(objectClass=dnsNode)(name=%s))", name); + "(&(objectClass=dnsNode)(name=%s)(!(dNSTombstoned=TRUE)))", name); if (ret != LDB_SUCCESS) { return WERR_INTERNAL_DB_ERROR; } diff --git a/source4/rpc_server/netlogon/dcerpc_netlogon.c b/source4/rpc_server/netlogon/dcerpc_netlogon.c index b3b9989f9b7..70239a4c834 100644 --- a/source4/rpc_server/netlogon/dcerpc_netlogon.c +++ b/source4/rpc_server/netlogon/dcerpc_netlogon.c @@ -27,6 +27,7 @@ #include "auth/auth_sam_reply.h" #include "dsdb/samdb/samdb.h" #include "../lib/util/util_ldb.h" +#include "../lib/util/memcache.h" #include "../libcli/auth/schannel.h" #include "libcli/security/security.h" #include "param/param.h" @@ -39,6 +40,8 @@ #include "librpc/gen_ndr/ndr_irpc.h" #include "lib/socket/netif.h" +static struct memcache *global_challenge_table; + struct netlogon_server_pipe_state { struct netr_Credential client_challenge; struct netr_Credential server_challenge; @@ -49,9 +52,27 @@ static NTSTATUS dcesrv_netr_ServerReqChallenge(struct dcesrv_call_state *dce_cal { struct netlogon_server_pipe_state *pipe_state = talloc_get_type(dce_call->context->private_data, struct netlogon_server_pipe_state); + DATA_BLOB key, val; ZERO_STRUCTP(r->out.return_credentials); + if (global_challenge_table == NULL) { + /* + * We maintain a global challenge table + * with a fixed size (8k) + * + * This is required for the strange clients + * which use different connections for + * netr_ServerReqChallenge() and netr_ServerAuthenticate3() + * + */ + global_challenge_table = memcache_init(talloc_autofree_context(), + 8192); + if (global_challenge_table == NULL) { + return NT_STATUS_NO_MEMORY; + } + } + /* destroyed on pipe shutdown */ if (pipe_state) { @@ -71,6 +92,11 @@ static NTSTATUS dcesrv_netr_ServerReqChallenge(struct dcesrv_call_state *dce_cal dce_call->context->private_data = pipe_state; + key = data_blob_string_const(r->in.computer_name); + val = data_blob_const(pipe_state, sizeof(*pipe_state)); + + memcache_add(global_challenge_table, SINGLETON_CACHE, key, val); + return NT_STATUS_OK; } @@ -79,6 +105,9 @@ static NTSTATUS dcesrv_netr_ServerAuthenticate3(struct dcesrv_call_state *dce_ca { struct netlogon_server_pipe_state *pipe_state = talloc_get_type(dce_call->context->private_data, struct netlogon_server_pipe_state); + DATA_BLOB challenge_key; + bool challenge_valid = false; + struct netlogon_server_pipe_state challenge; struct netlogon_creds_CredentialState *creds; struct ldb_context *sam_ctx; struct samr_Password *mach_pwd; @@ -96,6 +125,57 @@ static NTSTATUS dcesrv_netr_ServerAuthenticate3(struct dcesrv_call_state *dce_ca ZERO_STRUCTP(r->out.return_credentials); *r->out.rid = 0; + challenge_key = data_blob_string_const(r->in.computer_name); + if (pipe_state != NULL) { + dce_call->context->private_data = NULL; + + /* + * If we had a challenge remembered on the connection + * consider this for usage. This can't be cleanup + * by other clients. + * + * This is the default code path for typical clients + * which call netr_ServerReqChallenge() and + * netr_ServerAuthenticate3() on the same dcerpc connection. + */ + challenge = *pipe_state; + TALLOC_FREE(pipe_state); + challenge_valid = true; + } else { + DATA_BLOB val; + bool ok; + + /* + * Fallback and try to get the challenge from + * the global cache. + * + * If too many clients are using this code path, + * they may destroy their cache entries as the + * global_challenge_table memcache has a fixed size. + * + * Note: this handles global_challenge_table == NULL fine + */ + ok = memcache_lookup(global_challenge_table, SINGLETON_CACHE, + challenge_key, &val); + if (ok && val.length == sizeof(challenge)) { + memcpy(&challenge, val.data, sizeof(challenge)); + challenge_valid = true; + } else { + ZERO_STRUCT(challenge); + } + } + + /* + * At this point we can cleanup the cache entry, + * if we fail the client needs to call netr_ServerReqChallenge + * again. + * + * Note: this handles global_challenge_table == NULL + * and also a non existing record just fine. + */ + memcache_delete(global_challenge_table, + SINGLETON_CACHE, challenge_key); + negotiate_flags = NETLOGON_NEG_ACCOUNT_LOCKOUT | NETLOGON_NEG_PERSISTENT_SAMREPL | NETLOGON_NEG_ARCFOUR | @@ -257,8 +337,11 @@ static NTSTATUS dcesrv_netr_ServerAuthenticate3(struct dcesrv_call_state *dce_ca return NT_STATUS_ACCESS_DENIED; } - if (!pipe_state) { - DEBUG(1, ("No challenge requested by client, cannot authenticate\n")); + if (!challenge_valid) { + DEBUG(1, ("No challenge requested by client [%s/%s], " + "cannot authenticate\n", + r->in.computer_name, + r->in.account_name)); return NT_STATUS_ACCESS_DENIED; } @@ -266,8 +349,8 @@ static NTSTATUS dcesrv_netr_ServerAuthenticate3(struct dcesrv_call_state *dce_ca r->in.account_name, r->in.computer_name, r->in.secure_channel_type, - &pipe_state->client_challenge, - &pipe_state->server_challenge, + &challenge.client_challenge, + &challenge.server_challenge, mach_pwd, r->in.credentials, r->out.return_credentials, diff --git a/source4/scripting/bin/samba_dnsupdate b/source4/scripting/bin/samba_dnsupdate index 68b0f72151f..30d5608d50a 100755 --- a/source4/scripting/bin/samba_dnsupdate +++ b/source4/scripting/bin/samba_dnsupdate @@ -42,6 +42,7 @@ import samba import optparse from samba import getopt as options from ldb import SCOPE_BASE +from samba import dsdb from samba.auth import system_session from samba.samdb import SamDB from samba.dcerpc import netlogon, winbind @@ -63,6 +64,7 @@ parser.add_option("--all-names", action="store_true") parser.add_option("--all-interfaces", action="store_true") parser.add_option("--use-file", type="string", help="Use a file, rather than real DNS calls") parser.add_option("--update-list", type="string", help="Add DNS names from the given file") +parser.add_option("--update-cache", type="string", help="Cache database of already registered records") parser.add_option("--fail-immediately", action='store_true', help="Exit on first failure") parser.add_option("--no-credentials", dest='nocreds', action='store_true', help="don't try and get credentials") parser.add_option("--no-substiutions", dest='nosubs', action='store_true', help="don't try and expands variables in file specified by --update-list") @@ -136,41 +138,44 @@ class dnsobj(object): self.existing_port = None self.existing_weight = None self.type = list[0] - self.name = list[1].lower() + self.name = list[1] if self.type == 'SRV': if len(list) < 4: raise Exception("Invalid DNS entry %r" % string_form) - self.dest = list[2].lower() + self.dest = list[2] self.port = list[3] elif self.type in ['A', 'AAAA']: self.ip = list[2] # usually $IP, which gets replaced elif self.type == 'CNAME': - self.dest = list[2].lower() + self.dest = list[2] elif self.type == 'NS': - self.dest = list[2].lower() + self.dest = list[2] else: - raise Exception("Received unexpected DNS reply of type %s" % self.type) + raise Exception("Received unexpected DNS reply of type %s: %s" % (self.type, string_form)) def __str__(self): - if d.type == "A": + if self.type == "A": return "%s %s %s" % (self.type, self.name, self.ip) - if d.type == "AAAA": + if self.type == "AAAA": return "%s %s %s" % (self.type, self.name, self.ip) - if d.type == "SRV": + if self.type == "SRV": return "%s %s %s %s" % (self.type, self.name, self.dest, self.port) - if d.type == "CNAME": + if self.type == "CNAME": return "%s %s %s" % (self.type, self.name, self.dest) - if d.type == "NS": + if self.type == "NS": return "%s %s %s" % (self.type, self.name, self.dest) def parse_dns_line(line, sub_vars): """parse a DNS line from.""" if line.startswith("SRV _ldap._tcp.pdc._msdcs.") and not samdb.am_pdc(): + # We keep this as compat to the dns_update_list of 4.0/4.1 if opts.verbose: print "Skipping PDC entry (%s) as we are not a PDC" % line return None subline = samba.substitute_var(line, sub_vars) + if subline == '' or subline[0] == "#": + return None return dnsobj(subline) @@ -202,25 +207,6 @@ def check_dns_name(d): return False resolver = dns.resolver.Resolver() - if d.type == "NS": - # we need to lookup the nameserver for the parent domain, - # and use that to check the NS record - parent_domain = '.'.join(normalised_name.split('.')[1:]) - try: - ans = resolver.query(parent_domain, 'NS') - except dns.exception.DNSException: - if opts.verbose: - print "Failed to find parent NS for %s" % d - return False - nameservers = set() - for i in range(len(ans)): - try: - ns = resolver.query(str(ans[i]), 'A') - except dns.exception.DNSException: - continue - for j in range(len(ns)): - nameservers.add(str(ns[j])) - d.nameservers = list(nameservers) try: if getattr(d, 'nameservers', None): @@ -273,17 +259,72 @@ def get_subst_vars(samdb): res = samdb.search(base=samdb.get_default_basedn(), scope=SCOPE_BASE, attrs=["objectGUID"]) guid = samdb.schema_format_value("objectGUID", res[0]['objectGUID'][0]) vars['DOMAINGUID'] = guid + + vars['IF_DC'] = "" + vars['IF_RWDC'] = "# " + vars['IF_RODC'] = "# " + vars['IF_PDC'] = "# " + vars['IF_GC'] = "# " + vars['IF_RWGC'] = "# " + vars['IF_ROGC'] = "# " + vars['IF_DNS_DOMAIN'] = "# " + vars['IF_RWDNS_DOMAIN'] = "# " + vars['IF_RODNS_DOMAIN'] = "# " + vars['IF_DNS_FOREST'] = "# " + vars['IF_RWDNS_FOREST'] = "# " + vars['IF_R0DNS_FOREST'] = "# " + am_rodc = samdb.am_rodc() + if am_rodc: + vars['IF_RODC'] = "" + else: + vars['IF_RWDC'] = "" + + if samdb.am_pdc(): + vars['IF_PDC'] = "" + + # check if we "are DNS server" + res = samdb.search(base=samdb.get_config_basedn(), + expression='(objectguid=%s)' % vars['NTDSGUID'], + attrs=["options", "msDS-hasMasterNCs"]) + + if len(res) == 1: + if "options" in res[0]: + options = int(res[0]["options"][0]) + if (options & dsdb.DS_NTDSDSA_OPT_IS_GC) != 0: + vars['IF_GC'] = "" + if am_rodc: + vars['IF_ROGC'] = "" + else: + vars['IF_RWGC'] = "" + + basedn = str(samdb.get_default_basedn()) + if "msDS-hasMasterNCs" in res[0]: + for e in res[0]["msDS-hasMasterNCs"]: + if str(e) == "DC=DomainDnsZones,%s" % basedn: + vars['IF_DNS_DOMAIN'] = "" + if am_rodc: + vars['IF_RODNS_DOMAIN'] = "" + else: + vars['IF_RWDNS_DOMAIN'] = "" + if str(e) == "DC=ForestDnsZones,%s" % basedn: + vars['IF_DNS_FOREST'] = "" + if am_rodc: + vars['IF_RODNS_FOREST'] = "" + else: + vars['IF_RWDNS_FOREST'] = "" return vars -def call_nsupdate(d): +def call_nsupdate(d, op="add"): """call nsupdate for an entry.""" global ccachename, nsupdate_cmd, krb5conf + assert(op in ["add", "delete"]) + if opts.verbose: - print "Calling nsupdate for %s" % d + print "Calling nsupdate for %s (%s)" % (d, op) if opts.use_file is not None: try: @@ -299,8 +340,13 @@ def call_nsupdate(d): wfile = os.fdopen(tmp_fd, 'a') rfile.seek(0) for line in rfile: + if op == "delete": + l = parse_dns_line(line, {}) + if str(l).lower() == str(d).lower(): + continue wfile.write(line) - wfile.write(str(d)+"\n") + if op == "add": + wfile.write(str(d)+"\n") os.rename(tmpfile, opts.use_file) fcntl.lockf(rfile, fcntl.LOCK_UN) return @@ -312,18 +358,18 @@ def call_nsupdate(d): if getattr(d, 'nameservers', None): f.write('server %s\n' % d.nameservers[0]) if d.type == "A": - f.write("update add %s %u A %s\n" % (normalised_name, default_ttl, d.ip)) + f.write("update %s %s %u A %s\n" % (op, normalised_name, default_ttl, d.ip)) if d.type == "AAAA": - f.write("update add %s %u AAAA %s\n" % (normalised_name, default_ttl, d.ip)) + f.write("update %s %s %u AAAA %s\n" % (op, normalised_name, default_ttl, d.ip)) if d.type == "SRV": - if d.existing_port is not None: + if op == "add" and d.existing_port is not None: f.write("update delete %s SRV 0 %s %s %s\n" % (normalised_name, d.existing_weight, d.existing_port, d.dest)) - f.write("update add %s %u SRV 0 100 %s %s\n" % (normalised_name, default_ttl, d.port, d.dest)) + f.write("update %s %s %u SRV 0 100 %s %s\n" % (op, normalised_name, default_ttl, d.port, d.dest)) if d.type == "CNAME": - f.write("update add %s %u CNAME %s\n" % (normalised_name, default_ttl, d.dest)) + f.write("update %s %s %u CNAME %s\n" % (op, normalised_name, default_ttl, d.dest)) if d.type == "NS": - f.write("update add %s %u NS %s\n" % (normalised_name, default_ttl, d.dest)) + f.write("update %s %s %u NS %s\n" % (op, normalised_name, default_ttl, d.dest)) if opts.verbose: f.write("show\n") f.write("send\n") @@ -359,10 +405,12 @@ def call_nsupdate(d): -def rodc_dns_update(d, t): +def rodc_dns_update(d, t, op): '''a single DNS update via the RODC netlogon call''' global sub_vars + assert(op in ["add", "delete"]) + if opts.verbose: print "Calling netlogon RODC update for %s" % d @@ -386,7 +434,10 @@ def rodc_dns_update(d, t): name.weight = 0 if d.port is not None: name.port = int(d.port) - name.dns_register = True + if op == "add": + name.dns_register = True + else: + name.dns_register = False dns_names.names = [ name ] site_name = sub_vars['SITE'].decode('utf-8') @@ -405,10 +456,12 @@ def rodc_dns_update(d, t): sys.exit(1) -def call_rodc_update(d): +def call_rodc_update(d, op="add"): '''RODCs need to use the netlogon API for nsupdate''' global lp, sub_vars + assert(op in ["add", "delete"]) + # we expect failure for 3268 if we aren't a GC if d.port is not None and int(d.port) == 3268: return @@ -428,7 +481,7 @@ def call_rodc_update(d): subname = samba.substitute_var(map[t], sub_vars) if subname.lower() == d.name.lower(): # found a match - do the update - rodc_dns_update(d, t) + rodc_dns_update(d, t, op) return if opts.verbose: print("Unable to map to netlogon DNS update: %s" % d) @@ -440,6 +493,11 @@ if opts.update_list: else: dns_update_list = lp.private_path('dns_update_list') +if opts.update_cache: + dns_update_cache = opts.update_cache +else: + dns_update_cache = lp.private_path('dns_update_cache') + # use our private krb5.conf to avoid problems with the wrong domain # bind9 nsupdate wants the default domain set krb5conf = lp.private_path('krb5.conf') @@ -458,8 +516,31 @@ else: # build up a list of update commands to pass to nsupdate update_list = [] dns_list = [] +cache_list = [] +delete_list = [] dup_set = set() +cache_set = set() + +rebuild_cache = False +try: + cfile = open(dns_update_cache, 'r+') +except IOError: + # Perhaps create it + cfile = open(dns_update_cache, 'w+') + # Open it for reading again, in case someone else got to it first + cfile = open(dns_update_cache, 'r+') +fcntl.lockf(cfile, fcntl.LOCK_EX) +for line in cfile: + line = line.strip() + if line == '' or line[0] == "#": + continue + c = parse_dns_line(line, {}) + if c is None: + continue + if str(c) not in cache_set: + cache_list.append(c) + cache_set.add(str(c)) # read each line, and check that the DNS name exists for line in file: @@ -497,17 +578,50 @@ for d in dns_list: # now check if the entries already exist on the DNS server for d in dns_list: + found = False + for c in cache_list: + if str(c).lower() == str(d).lower(): + found = True + break + if not found: + rebuild_cache = True if opts.all_names or not check_dns_name(d): update_list.append(d) -if len(update_list) == 0: +for c in cache_list: + found = False + for d in dns_list: + if str(c).lower() == str(d).lower(): + found = True + break + if found: + continue + rebuild_cache = True + if not opts.all_names and not check_dns_name(c): + continue + delete_list.append(c) + +if len(delete_list) == 0 and len(update_list) == 0 and not rebuild_cache: if opts.verbose: print "No DNS updates needed" sys.exit(0) # get our krb5 creds -if not opts.nocreds: - get_credentials(lp) +if len(delete_list) != 0 or len(update_list) != 0: + if not opts.nocreds: + get_credentials(lp) + +# ask nsupdate to delete entries as needed +for d in delete_list: + if am_rodc: + if d.name.lower() == domain.lower(): + continue + if not d.type in [ 'A', 'AAAA' ]: + call_rodc_update(d, op="delete") + else: + call_nsupdate(d, op="delete") + else: + call_nsupdate(d, op="delete") # ask nsupdate to add entries as needed for d in update_list: @@ -521,6 +635,15 @@ for d in update_list: else: call_nsupdate(d) +if rebuild_cache: + (file_dir, file_name) = os.path.split(dns_update_cache) + (tmp_fd, tmpfile) = tempfile.mkstemp(dir=file_dir, prefix=file_name, suffix="XXXXXX") + wfile = os.fdopen(tmp_fd, 'a') + for d in dns_list: + wfile.write(str(d)+"\n") + os.rename(tmpfile, dns_update_cache) +fcntl.lockf(cfile, fcntl.LOCK_UN) + # delete the ccache if we created it if ccachename is not None: os.unlink(ccachename) diff --git a/source4/setup/dns_update_list b/source4/setup/dns_update_list index 4da0398196e..deac459d7aa 100644 --- a/source4/setup/dns_update_list +++ b/source4/setup/dns_update_list @@ -1,39 +1,49 @@ # this is a list of DNS entries which will be put into DNS using # dynamic DNS update. It is processed by the samba_dnsupdate script -A ${DNSDOMAIN} $IP -A ${HOSTNAME} $IP -AAAA ${DNSDOMAIN} $IP -AAAA ${HOSTNAME} $IP +A ${HOSTNAME} $IP +AAAA ${HOSTNAME} $IP + +# RW domain controller +${IF_RWDC}A ${DNSDOMAIN} $IP +${IF_RWDC}AAAA ${DNSDOMAIN} $IP +${IF_RWDC}SRV _ldap._tcp.${DNSDOMAIN} ${HOSTNAME} 389 +${IF_RWDC}SRV _ldap._tcp.dc._msdcs.${DNSDOMAIN} ${HOSTNAME} 389 +${IF_RWDC}SRV _ldap._tcp.${DOMAINGUID}.domains._msdcs.${DNSFOREST} ${HOSTNAME} 389 +${IF_RWDC}SRV _kerberos._tcp.${DNSDOMAIN} ${HOSTNAME} 88 +${IF_RWDC}SRV _kerberos._udp.${DNSDOMAIN} ${HOSTNAME} 88 +${IF_RWDC}SRV _kerberos._tcp.dc._msdcs.${DNSDOMAIN} ${HOSTNAME} 88 +${IF_RWDC}SRV _kpasswd._tcp.${DNSDOMAIN} ${HOSTNAME} 464 +${IF_RWDC}SRV _kpasswd._udp.${DNSDOMAIN} ${HOSTNAME} 464 +# RW and RO domain controller +${IF_DC}CNAME ${NTDSGUID}._msdcs.${DNSFOREST} ${HOSTNAME} +${IF_DC}SRV _ldap._tcp.${SITE}._sites.${DNSDOMAIN} ${HOSTNAME} 389 +${IF_DC}SRV _ldap._tcp.${SITE}._sites.dc._msdcs.${DNSDOMAIN} ${HOSTNAME} 389 +${IF_DC}SRV _kerberos._tcp.${SITE}._sites.${DNSDOMAIN} ${HOSTNAME} 88 +${IF_DC}SRV _kerberos._tcp.${SITE}._sites.dc._msdcs.${DNSDOMAIN} ${HOSTNAME} 88 + +# The PDC emulator +${IF_PDC}SRV _ldap._tcp.pdc._msdcs.${DNSDOMAIN} ${HOSTNAME} 389 + +# RW GC servers +${IF_RWGC}A gc._msdcs.${DNSFOREST} $IP +${IF_RWGC}AAAA gc._msdcs.${DNSFOREST} $IP +${IF_RWGC}SRV _gc._tcp.${DNSFOREST} ${HOSTNAME} 3268 +${IF_RWGC}SRV _ldap._tcp.gc._msdcs.${DNSFOREST} ${HOSTNAME} 3268 +# RW and RO GC servers +${IF_GC}SRV _gc._tcp.${SITE}._sites.${DNSFOREST} ${HOSTNAME} 3268 +${IF_GC}SRV _ldap._tcp.${SITE}._sites.gc._msdcs.${DNSFOREST} ${HOSTNAME} 3268 + +# RW DNS servers +${IF_RWDNS_DOMAIN}A DomainDnsZones.${DNSDOMAIN} $IP +${IF_RWDNS_DOMAIN}AAAA DomainDnsZones.${DNSDOMAIN} $IP +${IF_RWDNS_DOMAIN}SRV _ldap._tcp.DomainDnsZones.${DNSDOMAIN} ${HOSTNAME} 389 +# RW and RO DNS servers +${IF_DNS_DOMAIN}SRV _ldap._tcp.${SITE}._sites.DomainDnsZones.${DNSDOMAIN} ${HOSTNAME} 389 + +# RW DNS servers +${IF_RWDNS_FOREST}A ForestDnsZones.${DNSFOREST} $IP +${IF_RWDNS_FOREST}AAAA ForestDnsZones.${DNSFOREST} $IP +${IF_RWDNS_FOREST}SRV _ldap._tcp.ForestDnsZones.${DNSFOREST} ${HOSTNAME} 389 +# RW and RO DNS servers +${IF_DNS_FOREST}SRV _ldap._tcp.${SITE}._sites.ForestDnsZones.${DNSFOREST} ${HOSTNAME} 389 -A gc._msdcs.${DNSFOREST} $IP -AAAA gc._msdcs.${DNSFOREST} $IP - -CNAME ${NTDSGUID}._msdcs.${DNSFOREST} ${HOSTNAME} - -SRV _kpasswd._tcp.${DNSDOMAIN} ${HOSTNAME} 464 -SRV _kpasswd._udp.${DNSDOMAIN} ${HOSTNAME} 464 - -SRV _kerberos._tcp.${DNSDOMAIN} ${HOSTNAME} 88 -SRV _kerberos._tcp.dc._msdcs.${DNSDOMAIN} ${HOSTNAME} 88 -SRV _kerberos._tcp.dc._msdcs.${DNSFOREST} ${HOSTNAME} 88 -SRV _kerberos._tcp.${SITE}._sites.${DNSDOMAIN} ${HOSTNAME} 88 -SRV _kerberos._tcp.${SITE}._sites.dc._msdcs.${DNSDOMAIN} ${HOSTNAME} 88 -SRV _kerberos._tcp.${SITE}._sites.dc._msdcs.${DNSFOREST} ${HOSTNAME} 88 - -SRV _kerberos._udp.${DNSDOMAIN} ${HOSTNAME} 88 - -SRV _ldap._tcp.${DNSDOMAIN} ${HOSTNAME} 389 -SRV _ldap._tcp.dc._msdcs.${DNSDOMAIN} ${HOSTNAME} 389 -SRV _ldap._tcp.dc._msdcs.${DNSFOREST} ${HOSTNAME} 389 -SRV _ldap._tcp.gc._msdcs.${DNSFOREST} ${HOSTNAME} 3268 -SRV _ldap._tcp.pdc._msdcs.${DNSDOMAIN} ${HOSTNAME} 389 -SRV _ldap._tcp.pdc._msdcs.${DNSFOREST} ${HOSTNAME} 389 -SRV _ldap._tcp.${SITE}._sites.${DNSDOMAIN} ${HOSTNAME} 389 -SRV _ldap._tcp.${SITE}._sites.dc._msdcs.${DNSDOMAIN} ${HOSTNAME} 389 -SRV _ldap._tcp.${SITE}._sites.dc._msdcs.${DNSFOREST} ${HOSTNAME} 389 -SRV _ldap._tcp.${SITE}._sites.gc._msdcs.${DNSFOREST} ${HOSTNAME} 3268 -SRV _ldap._tcp.${DOMAINGUID}.domains._msdcs.${DNSFOREST} ${HOSTNAME} 389 - - -SRV _gc._tcp.${DNSFOREST} ${HOSTNAME} 3268 -SRV _gc._tcp.${SITE}._sites.${DNSFOREST} ${HOSTNAME} 3268 diff --git a/source4/torture/dns/dlz_bind9.c b/source4/torture/dns/dlz_bind9.c index d7d1736a6fa..fa6967dc761 100644 --- a/source4/torture/dns/dlz_bind9.c +++ b/source4/torture/dns/dlz_bind9.c @@ -61,7 +61,7 @@ static bool test_dlz_bind9_create(struct torture_context *tctx) NULL }; tctx_static = tctx; - torture_assert_int_equal(tctx, dlz_create("samba_dlz", 3, discard_const_p(char *, argv), &dbdata, + torture_assert_int_equal(tctx, dlz_create("samba_dlz", 3, argv, &dbdata, "log", dlz_bind9_log_wrapper, NULL), ISC_R_SUCCESS, "Failed to create samba_dlz"); @@ -109,7 +109,7 @@ static bool test_dlz_bind9_configure(struct torture_context *tctx) NULL }; tctx_static = tctx; - torture_assert_int_equal(tctx, dlz_create("samba_dlz", 3, discard_const_p(char *, argv), &dbdata, + torture_assert_int_equal(tctx, dlz_create("samba_dlz", 3, argv, &dbdata, "log", dlz_bind9_log_wrapper, "writeable_zone", dlz_bind9_writeable_zone_hook, NULL), ISC_R_SUCCESS, @@ -144,7 +144,7 @@ static bool test_dlz_bind9_gensec(struct torture_context *tctx, const char *mech NULL }; tctx_static = tctx; - torture_assert_int_equal(tctx, dlz_create("samba_dlz", 3, discard_const_p(char *, argv), &dbdata, + torture_assert_int_equal(tctx, dlz_create("samba_dlz", 3, argv, &dbdata, "log", dlz_bind9_log_wrapper, "writeable_zone", dlz_bind9_writeable_zone_hook, NULL), ISC_R_SUCCESS, @@ -158,9 +158,19 @@ static bool test_dlz_bind9_gensec(struct torture_context *tctx, const char *mech lpcfg_gensec_settings(tctx, tctx->lp_ctx)); torture_assert_ntstatus_ok(tctx, status, "gensec_client_start (client) failed"); - status = gensec_set_target_hostname(gensec_client_context, torture_setting_string(tctx, "host", NULL)); + /* + * dlz_bind9 use the special dns/host.domain account + */ + status = gensec_set_target_hostname(gensec_client_context, + talloc_asprintf(tctx, + "%s.%s", + torture_setting_string(tctx, "host", NULL), + lpcfg_dnsdomain(tctx->lp_ctx))); torture_assert_ntstatus_ok(tctx, status, "gensec_set_target_hostname (client) failed"); + status = gensec_set_target_service(gensec_client_context, "dns"); + torture_assert_ntstatus_ok(tctx, status, "gensec_set_target_service failed"); + status = gensec_set_credentials(gensec_client_context, cmdline_credentials); torture_assert_ntstatus_ok(tctx, status, "gensec_set_credentials (client) failed"); @@ -181,7 +191,7 @@ static bool test_dlz_bind9_gensec(struct torture_context *tctx, const char *mech client_to_server.length, client_to_server.data, dbdata), - ISC_R_SUCCESS, + ISC_TRUE, "Failed to check key for update rights samba_dlz"); dlz_destroy(dbdata); @@ -199,6 +209,850 @@ static bool test_dlz_bind9_spnego(struct torture_context *tctx) return test_dlz_bind9_gensec(tctx, "GSS-SPNEGO"); } +struct test_expected_record { + const char *name; + const char *type; + const char *data; + int ttl; + bool printed; +}; + +struct test_expected_rr { + struct torture_context *tctx; + const char *query_name; + size_t num_records; + struct test_expected_record *records; + size_t num_rr; +}; + +static bool dlz_bind9_putnamedrr_torture_hook(struct test_expected_rr *expected, + const char *name, + const char *type, + dns_ttl_t ttl, + const char *data) +{ + size_t i; + + torture_assert(expected->tctx, name != NULL, + talloc_asprintf(expected->tctx, + "Got unnamed record type[%s] data[%s]\n", + type, data)); + + expected->num_rr++; + torture_comment(expected->tctx, "%u: name[%s] type[%s] ttl[%u] data[%s]\n", + (unsigned)expected->num_rr, name, type, (unsigned)ttl, data); + + for (i = 0; i < expected->num_records; i++) { + if (expected->records[i].name != NULL) { + if (strcmp(name, expected->records[i].name) != 0) { + continue; + } + } + + if (strcmp(type, expected->records[i].type) != 0) { + continue; + } + + if (expected->records[i].data != NULL) { + if (strcmp(data, expected->records[i].data) != 0) { + continue; + } + } + + torture_assert_int_equal(expected->tctx, ttl, + expected->records[i].ttl, + talloc_asprintf(expected->tctx, + "TTL did not match expectations for type %s", + type)); + + expected->records[i].printed = true; + } + + return true; +} + +static isc_result_t dlz_bind9_putrr_hook(dns_sdlzlookup_t *lookup, + const char *type, + dns_ttl_t ttl, + const char *data) +{ + struct test_expected_rr *expected = + talloc_get_type_abort(lookup, struct test_expected_rr); + bool ok; + + ok = dlz_bind9_putnamedrr_torture_hook(expected, expected->query_name, + type, ttl, data); + if (!ok) { + return ISC_R_FAILURE; + } + + return ISC_R_SUCCESS; +} + +static isc_result_t dlz_bind9_putnamedrr_hook(dns_sdlzallnodes_t *allnodes, + const char *name, + const char *type, + dns_ttl_t ttl, + const char *data) +{ + struct test_expected_rr *expected = + talloc_get_type_abort(allnodes, struct test_expected_rr); + bool ok; + + ok = dlz_bind9_putnamedrr_torture_hook(expected, name, type, ttl, data); + if (!ok) { + return ISC_R_FAILURE; + } + + return ISC_R_SUCCESS; +} + +/* + * Tests some lookups + */ +static bool test_dlz_bind9_lookup(struct torture_context *tctx) +{ + size_t i; + void *dbdata; + const char *argv[] = { + "samba_dlz", + "-H", + lpcfg_private_path(tctx, tctx->lp_ctx, "dns/sam.ldb"), + NULL + }; + struct test_expected_rr *expected1 = NULL; + struct test_expected_rr *expected2 = NULL; + + tctx_static = tctx; + torture_assert_int_equal(tctx, dlz_create("samba_dlz", 3, argv, &dbdata, + "log", dlz_bind9_log_wrapper, + "writeable_zone", dlz_bind9_writeable_zone_hook, + "putrr", dlz_bind9_putrr_hook, + "putnamedrr", dlz_bind9_putnamedrr_hook, + NULL), + ISC_R_SUCCESS, + "Failed to create samba_dlz"); + + torture_assert_int_equal(tctx, dlz_configure((void*)tctx, dbdata), + ISC_R_SUCCESS, + "Failed to configure samba_dlz"); + + expected1 = talloc_zero(tctx, struct test_expected_rr); + torture_assert(tctx, expected1 != NULL, "talloc failed"); + expected1->tctx = tctx; + + expected1->query_name = "@"; + + expected1->num_records = 4; + expected1->records = talloc_zero_array(expected1, + struct test_expected_record, + expected1->num_records); + torture_assert(tctx, expected1->records != NULL, "talloc failed"); + + expected1->records[0].name = expected1->query_name; + expected1->records[0].type = "soa"; + expected1->records[0].ttl = 3600; + expected1->records[0].data = talloc_asprintf(expected1->records, + "%s.%s hostmaster.%s 1 900 600 86400 3600", + torture_setting_string(tctx, "host", NULL), + lpcfg_dnsdomain(tctx->lp_ctx), + lpcfg_dnsdomain(tctx->lp_ctx)); + torture_assert(tctx, expected1->records[0].data != NULL, "talloc failed"); + + expected1->records[1].name = expected1->query_name; + expected1->records[1].type = "ns"; + expected1->records[1].ttl = 900; + expected1->records[1].data = talloc_asprintf(expected1->records, "%s.%s", + torture_setting_string(tctx, "host", NULL), + lpcfg_dnsdomain(tctx->lp_ctx)); + torture_assert(tctx, expected1->records[1].data != NULL, "talloc failed"); + + expected1->records[2].name = expected1->query_name; + expected1->records[2].type = "aaaa"; + expected1->records[2].ttl = 900; + + expected1->records[3].name = expected1->query_name; + expected1->records[3].type = "a"; + expected1->records[3].ttl = 900; + + torture_assert_int_equal(tctx, dlz_lookup(lpcfg_dnsdomain(tctx->lp_ctx), + expected1->query_name, dbdata, + (dns_sdlzlookup_t *)expected1), + ISC_R_SUCCESS, + "Failed to lookup @"); + for (i = 0; i < expected1->num_records; i++) { + torture_assert(tctx, expected1->records[i].printed, + talloc_asprintf(tctx, + "Failed to have putrr callback run for type %s", + expected1->records[i].type)); + } + torture_assert_int_equal(tctx, expected1->num_rr, + expected1->num_records, + "Got too much data"); + + expected2 = talloc_zero(tctx, struct test_expected_rr); + torture_assert(tctx, expected2 != NULL, "talloc failed"); + expected2->tctx = tctx; + + expected2->query_name = torture_setting_string(tctx, "host", NULL); + torture_assert(tctx, expected2->query_name != NULL, "unknown host"); + + expected2->num_records = 2; + expected2->records = talloc_zero_array(expected2, + struct test_expected_record, + expected2->num_records); + torture_assert(tctx, expected2->records != NULL, "talloc failed"); + + expected2->records[0].name = expected2->query_name; + expected2->records[0].type = "aaaa"; + expected2->records[0].ttl = 900; + + expected2->records[1].name = expected2->query_name; + expected2->records[1].type = "a"; + expected2->records[1].ttl = 900; + + torture_assert_int_equal(tctx, dlz_lookup(lpcfg_dnsdomain(tctx->lp_ctx), + expected2->query_name, dbdata, + (dns_sdlzlookup_t *)expected2), + ISC_R_SUCCESS, + "Failed to lookup hostname"); + for (i = 0; i < expected2->num_records; i++) { + torture_assert(tctx, expected2->records[i].printed, + talloc_asprintf(tctx, + "Failed to have putrr callback run name[%s] for type %s", + expected2->records[i].name, + expected2->records[i].type)); + } + torture_assert_int_equal(tctx, expected2->num_rr, + expected2->num_records, + "Got too much data"); + + dlz_destroy(dbdata); + + return true; +} + +/* + * Test some zone dumps + */ +static bool test_dlz_bind9_zonedump(struct torture_context *tctx) +{ + size_t i; + void *dbdata; + const char *argv[] = { + "samba_dlz", + "-H", + lpcfg_private_path(tctx, tctx->lp_ctx, "dns/sam.ldb"), + NULL + }; + struct test_expected_rr *expected1 = NULL; + + tctx_static = tctx; + torture_assert_int_equal(tctx, dlz_create("samba_dlz", 3, argv, &dbdata, + "log", dlz_bind9_log_wrapper, + "writeable_zone", dlz_bind9_writeable_zone_hook, + "putrr", dlz_bind9_putrr_hook, + "putnamedrr", dlz_bind9_putnamedrr_hook, + NULL), + ISC_R_SUCCESS, + "Failed to create samba_dlz"); + + torture_assert_int_equal(tctx, dlz_configure((void*)tctx, dbdata), + ISC_R_SUCCESS, + "Failed to configure samba_dlz"); + + expected1 = talloc_zero(tctx, struct test_expected_rr); + torture_assert(tctx, expected1 != NULL, "talloc failed"); + expected1->tctx = tctx; + + expected1->num_records = 7; + expected1->records = talloc_zero_array(expected1, + struct test_expected_record, + expected1->num_records); + torture_assert(tctx, expected1->records != NULL, "talloc failed"); + + expected1->records[0].name = lpcfg_dnsdomain(tctx->lp_ctx); + expected1->records[0].type = "soa"; + expected1->records[0].ttl = 3600; + expected1->records[0].data = talloc_asprintf(expected1->records, + "%s.%s hostmaster.%s 1 900 600 86400 3600", + torture_setting_string(tctx, "host", NULL), + lpcfg_dnsdomain(tctx->lp_ctx), + lpcfg_dnsdomain(tctx->lp_ctx)); + torture_assert(tctx, expected1->records[0].data != NULL, "talloc failed"); + + expected1->records[1].name = lpcfg_dnsdomain(tctx->lp_ctx); + expected1->records[1].type = "ns"; + expected1->records[1].ttl = 900; + expected1->records[1].data = talloc_asprintf(expected1->records, "%s.%s", + torture_setting_string(tctx, "host", NULL), + lpcfg_dnsdomain(tctx->lp_ctx)); + torture_assert(tctx, expected1->records[1].data != NULL, "talloc failed"); + + expected1->records[2].name = lpcfg_dnsdomain(tctx->lp_ctx); + expected1->records[2].type = "aaaa"; + expected1->records[2].ttl = 900; + + expected1->records[3].name = lpcfg_dnsdomain(tctx->lp_ctx); + expected1->records[3].type = "a"; + expected1->records[3].ttl = 900; + + expected1->records[4].name = talloc_asprintf(expected1->records, "%s.%s", + torture_setting_string(tctx, "host", NULL), + lpcfg_dnsdomain(tctx->lp_ctx)); + torture_assert(tctx, expected1->records[4].name != NULL, "unknown host"); + expected1->records[4].type = "aaaa"; + expected1->records[4].ttl = 900; + + expected1->records[5].name = talloc_asprintf(expected1->records, "%s.%s", + torture_setting_string(tctx, "host", NULL), + lpcfg_dnsdomain(tctx->lp_ctx)); + torture_assert(tctx, expected1->records[5].name != NULL, "unknown host"); + expected1->records[5].type = "a"; + expected1->records[5].ttl = 900; + + /* + * We expect multiple srv records + */ + expected1->records[6].name = NULL; + expected1->records[6].type = "srv"; + expected1->records[6].ttl = 900; + + torture_assert_int_equal(tctx, dlz_allnodes(lpcfg_dnsdomain(tctx->lp_ctx), + dbdata, (dns_sdlzallnodes_t *)expected1), + ISC_R_SUCCESS, + "Failed to configure samba_dlz"); + for (i = 0; i < expected1->num_records; i++) { + torture_assert(tctx, expected1->records[i].printed, + talloc_asprintf(tctx, + "Failed to have putrr callback run name[%s] for type %s", + expected1->records[i].name, + expected1->records[i].type)); + } + torture_assert_int_equal(tctx, expected1->num_rr, 24, + "Got wrong record count"); + + dlz_destroy(dbdata); + + return true; +} + +/* + * Test some updates + */ +static bool test_dlz_bind9_update01(struct torture_context *tctx) +{ + NTSTATUS status; + struct gensec_security *gensec_client_context; + DATA_BLOB client_to_server, server_to_client; + void *dbdata; + void *version = NULL; + const char *argv[] = { + "samba_dlz", + "-H", + lpcfg_private_path(tctx, tctx->lp_ctx, "dns/sam.ldb"), + NULL + }; + struct test_expected_rr *expected1 = NULL; + char *name = NULL; + char *data0 = NULL; + char *data1 = NULL; + char *data2 = NULL; + bool ret = false; + + tctx_static = tctx; + torture_assert_int_equal(tctx, dlz_create("samba_dlz", 3, argv, &dbdata, + "log", dlz_bind9_log_wrapper, + "writeable_zone", dlz_bind9_writeable_zone_hook, + "putrr", dlz_bind9_putrr_hook, + "putnamedrr", dlz_bind9_putnamedrr_hook, + NULL), + ISC_R_SUCCESS, + "Failed to create samba_dlz"); + + torture_assert_int_equal(tctx, dlz_configure((void*)tctx, dbdata), + ISC_R_SUCCESS, + "Failed to configure samba_dlz"); + + expected1 = talloc_zero(tctx, struct test_expected_rr); + torture_assert(tctx, expected1 != NULL, "talloc failed"); + expected1->tctx = tctx; + + expected1->query_name = __func__; + + name = talloc_asprintf(expected1, "%s.%s", + expected1->query_name, + lpcfg_dnsdomain(tctx->lp_ctx)); + torture_assert(tctx, name != NULL, "talloc failed"); + + expected1->num_records = 2; + expected1->records = talloc_zero_array(expected1, + struct test_expected_record, + expected1->num_records); + torture_assert(tctx, expected1->records != NULL, "talloc failed"); + + expected1->records[0].name = expected1->query_name; + expected1->records[0].type = "a"; + expected1->records[0].ttl = 3600; + expected1->records[0].data = "127.1.2.3"; + expected1->records[0].printed = false; + + data0 = talloc_asprintf(expected1, + "%s.\t" "%u\t" "%s\t" "%s\t" "%s", + name, + (unsigned)expected1->records[0].ttl, + "in", + expected1->records[0].type, + expected1->records[0].data); + torture_assert(tctx, data0 != NULL, "talloc failed"); + + expected1->records[1].name = expected1->query_name; + expected1->records[1].type = "a"; + expected1->records[1].ttl = 3600; + expected1->records[1].data = "127.3.2.1"; + expected1->records[1].printed = false; + + data1 = talloc_asprintf(expected1, + "%s.\t" "%u\t" "%s\t" "%s\t" "%s", + name, + (unsigned)expected1->records[1].ttl, + "in", + expected1->records[1].type, + expected1->records[1].data); + torture_assert(tctx, data1 != NULL, "talloc failed"); + + data2 = talloc_asprintf(expected1, + "%s.\t" "0\t" "in\t" "a\t" "127.3.3.3", + name); + torture_assert(tctx, data2 != NULL, "talloc failed"); + + /* + * Prepare session info + */ + status = gensec_client_start(tctx, &gensec_client_context, + lpcfg_gensec_settings(tctx, tctx->lp_ctx)); + torture_assert_ntstatus_ok(tctx, status, "gensec_client_start (client) failed"); + + /* + * dlz_bind9 use the special dns/host.domain account + */ + status = gensec_set_target_hostname(gensec_client_context, + talloc_asprintf(tctx, + "%s.%s", + torture_setting_string(tctx, "host", NULL), + lpcfg_dnsdomain(tctx->lp_ctx))); + torture_assert_ntstatus_ok(tctx, status, "gensec_set_target_hostname (client) failed"); + + status = gensec_set_target_service(gensec_client_context, "dns"); + torture_assert_ntstatus_ok(tctx, status, "gensec_set_target_service failed"); + + status = gensec_set_credentials(gensec_client_context, cmdline_credentials); + torture_assert_ntstatus_ok(tctx, status, "gensec_set_credentials (client) failed"); + + status = gensec_start_mech_by_sasl_name(gensec_client_context, "GSS-SPNEGO"); + torture_assert_ntstatus_ok(tctx, status, "gensec_start_mech_by_sasl_name (client) failed"); + + server_to_client = data_blob(NULL, 0); + + /* Do one step of the client-server update dance */ + status = gensec_update(gensec_client_context, tctx, tctx->ev, server_to_client, &client_to_server); + if (!NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {; + torture_assert_ntstatus_ok(tctx, status, "gensec_update (client) failed"); + } + + torture_assert_int_equal(tctx, dlz_ssumatch(cli_credentials_get_username(cmdline_credentials), + name, + "127.0.0.1", + expected1->records[0].type, + "key", + client_to_server.length, + client_to_server.data, + dbdata), + ISC_TRUE, + "Failed to check key for update rights samba_dlz"); + + /* + * We test the following: + * + * 1. lookup the records => NOT_FOUND + * 2. delete all records => NOT_FOUND + * 3. delete 1st record => NOT_FOUND + * 4. create 1st record => SUCCESS + * 5. lookup the records => found 1st + * 6. create 2nd record => SUCCESS + * 7. lookup the records => found 1st and 2nd + * 8. delete unknown record => NOT_FOUND + * 9. lookup the records => found 1st and 2nd + * 10. delete 1st record => SUCCESS + * 11. lookup the records => found 2nd + * 12. delete 2nd record => SUCCESS + * 13. lookup the records => NOT_FOUND + * 14. create 1st record => SUCCESS + * 15. lookup the records => found 1st + * 16. create 2nd record => SUCCESS + * 17. lookup the records => found 1st and 2nd + * 18. update 1st record => SUCCESS + * 19. lookup the records => found 1st and 2nd + * 20. delete all unknown type records => NOT_FOUND + * 21. lookup the records => found 1st and 2nd + * 22. delete all records => SUCCESS + * 23. lookup the records => NOT_FOUND + */ + + /* Step 1. */ + expected1->num_rr = 0; + expected1->records[0].printed = false; + expected1->records[1].printed = false; + torture_assert_int_equal(tctx, dlz_lookup(lpcfg_dnsdomain(tctx->lp_ctx), + expected1->query_name, dbdata, + (dns_sdlzlookup_t *)expected1), + ISC_R_NOTFOUND, + "Found hostname"); + torture_assert_int_equal(tctx, expected1->num_rr, 0, + "Got wrong record count"); + + /* Step 2. */ + torture_assert_int_equal(tctx, dlz_newversion(lpcfg_dnsdomain(tctx->lp_ctx), + dbdata, &version), + ISC_R_SUCCESS, + "Failed to start transaction"); + torture_assert_int_equal_goto(tctx, + dlz_delrdataset(name, + expected1->records[0].type, + dbdata, version), + ISC_R_NOTFOUND, ret, cancel_version, + talloc_asprintf(tctx, "Deleted name[%s] type[%s]\n", + name, expected1->records[0].type)); + dlz_closeversion(lpcfg_dnsdomain(tctx->lp_ctx), false, dbdata, &version); + + /* Step 3. */ + torture_assert_int_equal(tctx, dlz_newversion(lpcfg_dnsdomain(tctx->lp_ctx), + dbdata, &version), + ISC_R_SUCCESS, + "Failed to start transaction"); + torture_assert_int_equal_goto(tctx, + dlz_subrdataset(name, data0, dbdata, version), + ISC_R_NOTFOUND, ret, cancel_version, + talloc_asprintf(tctx, "Deleted name[%s] data[%s]\n", + name, data0)); + dlz_closeversion(lpcfg_dnsdomain(tctx->lp_ctx), false, dbdata, &version); + + /* Step 4. */ + torture_assert_int_equal(tctx, dlz_newversion(lpcfg_dnsdomain(tctx->lp_ctx), + dbdata, &version), + ISC_R_SUCCESS, + "Failed to start transaction"); + torture_assert_int_equal_goto(tctx, + dlz_addrdataset(name, data0, dbdata, version), + ISC_R_SUCCESS, ret, cancel_version, + talloc_asprintf(tctx, "Failed to add name[%s] data[%s]\n", + name, data0)); + dlz_closeversion(lpcfg_dnsdomain(tctx->lp_ctx), true, dbdata, &version); + + /* Step 5. */ + expected1->num_rr = 0; + expected1->records[0].printed = false; + expected1->records[1].printed = false; + torture_assert_int_equal(tctx, dlz_lookup(lpcfg_dnsdomain(tctx->lp_ctx), + expected1->query_name, dbdata, + (dns_sdlzlookup_t *)expected1), + ISC_R_SUCCESS, + "Not found hostname"); + torture_assert(tctx, expected1->records[0].printed, + talloc_asprintf(tctx, + "Failed to have putrr callback run name[%s] for type %s", + expected1->records[0].name, + expected1->records[0].type)); + torture_assert_int_equal(tctx, expected1->num_rr, 1, + "Got wrong record count"); + + /* Step 6. */ + torture_assert_int_equal(tctx, dlz_newversion(lpcfg_dnsdomain(tctx->lp_ctx), + dbdata, &version), + ISC_R_SUCCESS, + "Failed to start transaction"); + torture_assert_int_equal_goto(tctx, + dlz_addrdataset(name, data1, dbdata, version), + ISC_R_SUCCESS, ret, cancel_version, + talloc_asprintf(tctx, "Failed to add name[%s] data[%s]\n", + name, data1)); + dlz_closeversion(lpcfg_dnsdomain(tctx->lp_ctx), true, dbdata, &version); + + /* Step 7. */ + expected1->num_rr = 0; + expected1->records[0].printed = false; + expected1->records[1].printed = false; + torture_assert_int_equal(tctx, dlz_lookup(lpcfg_dnsdomain(tctx->lp_ctx), + expected1->query_name, dbdata, + (dns_sdlzlookup_t *)expected1), + ISC_R_SUCCESS, + "Not found hostname"); + torture_assert(tctx, expected1->records[0].printed, + talloc_asprintf(tctx, + "Failed to have putrr callback run name[%s] for type %s", + expected1->records[0].name, + expected1->records[0].type)); + torture_assert(tctx, expected1->records[1].printed, + talloc_asprintf(tctx, + "Failed to have putrr callback run name[%s] for type %s", + expected1->records[1].name, + expected1->records[1].type)); + torture_assert_int_equal(tctx, expected1->num_rr, 2, + "Got wrong record count"); + + /* Step 8. */ + torture_assert_int_equal(tctx, dlz_newversion(lpcfg_dnsdomain(tctx->lp_ctx), + dbdata, &version), + ISC_R_SUCCESS, + "Failed to start transaction"); + torture_assert_int_equal_goto(tctx, + dlz_subrdataset(name, data2, dbdata, version), + ISC_R_NOTFOUND, ret, cancel_version, + talloc_asprintf(tctx, "Deleted name[%s] data[%s]\n", + name, data2)); + dlz_closeversion(lpcfg_dnsdomain(tctx->lp_ctx), true, dbdata, &version); + + /* Step 9. */ + expected1->num_rr = 0; + expected1->records[0].printed = false; + expected1->records[1].printed = false; + torture_assert_int_equal(tctx, dlz_lookup(lpcfg_dnsdomain(tctx->lp_ctx), + expected1->query_name, dbdata, + (dns_sdlzlookup_t *)expected1), + ISC_R_SUCCESS, + "Not found hostname"); + torture_assert(tctx, expected1->records[0].printed, + talloc_asprintf(tctx, + "Failed to have putrr callback run name[%s] for type %s", + expected1->records[0].name, + expected1->records[0].type)); + torture_assert(tctx, expected1->records[1].printed, + talloc_asprintf(tctx, + "Failed to have putrr callback run name[%s] for type %s", + expected1->records[1].name, + expected1->records[1].type)); + torture_assert_int_equal(tctx, expected1->num_rr, 2, + "Got wrong record count"); + + /* Step 10. */ + torture_assert_int_equal(tctx, dlz_newversion(lpcfg_dnsdomain(tctx->lp_ctx), + dbdata, &version), + ISC_R_SUCCESS, + "Failed to start transaction"); + torture_assert_int_equal_goto(tctx, + dlz_subrdataset(name, data0, dbdata, version), + ISC_R_SUCCESS, ret, cancel_version, + talloc_asprintf(tctx, "Failed to delete name[%s] data[%s]\n", + name, data0)); + dlz_closeversion(lpcfg_dnsdomain(tctx->lp_ctx), true, dbdata, &version); + + /* Step 11. */ + expected1->num_rr = 0; + expected1->records[0].printed = false; + expected1->records[1].printed = false; + torture_assert_int_equal(tctx, dlz_lookup(lpcfg_dnsdomain(tctx->lp_ctx), + expected1->query_name, dbdata, + (dns_sdlzlookup_t *)expected1), + ISC_R_SUCCESS, + "Not found hostname"); + torture_assert(tctx, expected1->records[1].printed, + talloc_asprintf(tctx, + "Failed to have putrr callback run name[%s] for type %s", + expected1->records[1].name, + expected1->records[1].type)); + torture_assert_int_equal(tctx, expected1->num_rr, 1, + "Got wrong record count"); + + /* Step 12. */ + torture_assert_int_equal(tctx, dlz_newversion(lpcfg_dnsdomain(tctx->lp_ctx), + dbdata, &version), + ISC_R_SUCCESS, + "Failed to start transaction"); + torture_assert_int_equal_goto(tctx, + dlz_subrdataset(name, data1, dbdata, version), + ISC_R_SUCCESS, ret, cancel_version, + talloc_asprintf(tctx, "Failed to delete name[%s] data[%s]\n", + name, data1)); + dlz_closeversion(lpcfg_dnsdomain(tctx->lp_ctx), true, dbdata, &version); + + /* Step 13. */ + expected1->num_rr = 0; + expected1->records[0].printed = false; + expected1->records[1].printed = false; + torture_assert_int_equal(tctx, dlz_lookup(lpcfg_dnsdomain(tctx->lp_ctx), + expected1->query_name, dbdata, + (dns_sdlzlookup_t *)expected1), + ISC_R_NOTFOUND, + "Found hostname"); + torture_assert_int_equal(tctx, expected1->num_rr, 0, + "Got wrong record count"); + + /* Step 14. */ + torture_assert_int_equal(tctx, dlz_newversion(lpcfg_dnsdomain(tctx->lp_ctx), + dbdata, &version), + ISC_R_SUCCESS, + "Failed to start transaction"); + torture_assert_int_equal_goto(tctx, + dlz_addrdataset(name, data0, dbdata, version), + ISC_R_SUCCESS, ret, cancel_version, + talloc_asprintf(tctx, "Failed to add name[%s] data[%s]\n", + name, data0)); + dlz_closeversion(lpcfg_dnsdomain(tctx->lp_ctx), true, dbdata, &version); + + /* Step 15. */ + expected1->num_rr = 0; + expected1->records[0].printed = false; + expected1->records[1].printed = false; + torture_assert_int_equal(tctx, dlz_lookup(lpcfg_dnsdomain(tctx->lp_ctx), + expected1->query_name, dbdata, + (dns_sdlzlookup_t *)expected1), + ISC_R_SUCCESS, + "Not found hostname"); + torture_assert(tctx, expected1->records[0].printed, + talloc_asprintf(tctx, + "Failed to have putrr callback run name[%s] for type %s", + expected1->records[0].name, + expected1->records[0].type)); + torture_assert_int_equal(tctx, expected1->num_rr, 1, + "Got wrong record count"); + + /* Step 16. */ + torture_assert_int_equal(tctx, dlz_newversion(lpcfg_dnsdomain(tctx->lp_ctx), + dbdata, &version), + ISC_R_SUCCESS, + "Failed to start transaction"); + torture_assert_int_equal_goto(tctx, + dlz_addrdataset(name, data1, dbdata, version), + ISC_R_SUCCESS, ret, cancel_version, + talloc_asprintf(tctx, "Failed to add name[%s] data[%s]\n", + name, data1)); + dlz_closeversion(lpcfg_dnsdomain(tctx->lp_ctx), true, dbdata, &version); + + /* Step 17. */ + expected1->num_rr = 0; + expected1->records[0].printed = false; + expected1->records[1].printed = false; + torture_assert_int_equal(tctx, dlz_lookup(lpcfg_dnsdomain(tctx->lp_ctx), + expected1->query_name, dbdata, + (dns_sdlzlookup_t *)expected1), + ISC_R_SUCCESS, + "Not found hostname"); + torture_assert(tctx, expected1->records[0].printed, + talloc_asprintf(tctx, + "Failed to have putrr callback run name[%s] for type %s", + expected1->records[0].name, + expected1->records[0].type)); + torture_assert(tctx, expected1->records[1].printed, + talloc_asprintf(tctx, + "Failed to have putrr callback run name[%s] for type %s", + expected1->records[1].name, + expected1->records[1].type)); + torture_assert_int_equal(tctx, expected1->num_rr, 2, + "Got wrong record count"); + + /* Step 18. */ + torture_assert_int_equal(tctx, dlz_newversion(lpcfg_dnsdomain(tctx->lp_ctx), + dbdata, &version), + ISC_R_SUCCESS, + "Failed to start transaction"); + torture_assert_int_equal_goto(tctx, + dlz_addrdataset(name, data0, dbdata, version), + ISC_R_SUCCESS, ret, cancel_version, + talloc_asprintf(tctx, "Failed to update name[%s] data[%s]\n", + name, data0)); + dlz_closeversion(lpcfg_dnsdomain(tctx->lp_ctx), true, dbdata, &version); + + /* Step 19. */ + expected1->num_rr = 0; + expected1->records[0].printed = false; + expected1->records[1].printed = false; + torture_assert_int_equal(tctx, dlz_lookup(lpcfg_dnsdomain(tctx->lp_ctx), + expected1->query_name, dbdata, + (dns_sdlzlookup_t *)expected1), + ISC_R_SUCCESS, + "Not found hostname"); + torture_assert(tctx, expected1->records[0].printed, + talloc_asprintf(tctx, + "Failed to have putrr callback run name[%s] for type %s", + expected1->records[0].name, + expected1->records[0].type)); + torture_assert(tctx, expected1->records[1].printed, + talloc_asprintf(tctx, + "Failed to have putrr callback run name[%s] for type %s", + expected1->records[1].name, + expected1->records[1].type)); + torture_assert_int_equal(tctx, expected1->num_rr, 2, + "Got wrong record count"); + + /* Step 20. */ + torture_assert_int_equal(tctx, dlz_newversion(lpcfg_dnsdomain(tctx->lp_ctx), + dbdata, &version), + ISC_R_SUCCESS, + "Failed to start transaction"); + torture_assert_int_equal_goto(tctx, + dlz_delrdataset(name, "txt", dbdata, version), + ISC_R_FAILURE, ret, cancel_version, + talloc_asprintf(tctx, "Deleted name[%s] type[%s]\n", + name, "txt")); + dlz_closeversion(lpcfg_dnsdomain(tctx->lp_ctx), false, dbdata, &version); + + /* Step 21. */ + expected1->num_rr = 0; + expected1->records[0].printed = false; + expected1->records[1].printed = false; + torture_assert_int_equal(tctx, dlz_lookup(lpcfg_dnsdomain(tctx->lp_ctx), + expected1->query_name, dbdata, + (dns_sdlzlookup_t *)expected1), + ISC_R_SUCCESS, + "Not found hostname"); + torture_assert(tctx, expected1->records[0].printed, + talloc_asprintf(tctx, + "Failed to have putrr callback run name[%s] for type %s", + expected1->records[0].name, + expected1->records[0].type)); + torture_assert(tctx, expected1->records[1].printed, + talloc_asprintf(tctx, + "Failed to have putrr callback run name[%s] for type %s", + expected1->records[1].name, + expected1->records[1].type)); + torture_assert_int_equal(tctx, expected1->num_rr, 2, + "Got wrong record count"); + + /* Step 22. */ + torture_assert_int_equal(tctx, dlz_newversion(lpcfg_dnsdomain(tctx->lp_ctx), + dbdata, &version), + ISC_R_SUCCESS, + "Failed to start transaction"); + torture_assert_int_equal_goto(tctx, + dlz_delrdataset(name, + expected1->records[0].type, + dbdata, version), + ISC_R_SUCCESS, ret, cancel_version, + talloc_asprintf(tctx, "Failed to delete name[%s] type[%s]\n", + name, expected1->records[0].type)); + dlz_closeversion(lpcfg_dnsdomain(tctx->lp_ctx), true, dbdata, &version); + + /* Step 23. */ + expected1->num_rr = 0; + expected1->records[0].printed = false; + expected1->records[1].printed = false; + torture_assert_int_equal(tctx, dlz_lookup(lpcfg_dnsdomain(tctx->lp_ctx), + expected1->query_name, dbdata, + (dns_sdlzlookup_t *)expected1), + ISC_R_NOTFOUND, + "Found hostname"); + torture_assert_int_equal(tctx, expected1->num_rr, 0, + "Got wrong record count"); + + dlz_destroy(dbdata); + + return true; + +cancel_version: + dlz_closeversion(lpcfg_dnsdomain(tctx->lp_ctx), false, dbdata, &version); + return ret; +} + static struct torture_suite *dlz_bind9_suite(TALLOC_CTX *ctx) { struct torture_suite *suite = torture_suite_create(ctx, "dlz_bind9"); @@ -210,12 +1064,16 @@ static struct torture_suite *dlz_bind9_suite(TALLOC_CTX *ctx) torture_suite_add_simple_test(suite, "configure", test_dlz_bind9_configure); torture_suite_add_simple_test(suite, "gssapi", test_dlz_bind9_gssapi); torture_suite_add_simple_test(suite, "spnego", test_dlz_bind9_spnego); + torture_suite_add_simple_test(suite, "lookup", test_dlz_bind9_lookup); + torture_suite_add_simple_test(suite, "zonedump", test_dlz_bind9_zonedump); + torture_suite_add_simple_test(suite, "update01", test_dlz_bind9_update01); return suite; } /** * DNS torture module initialization */ +NTSTATUS torture_bind_dns_init(void); NTSTATUS torture_bind_dns_init(void) { struct torture_suite *suite; diff --git a/source4/torture/ntp/ntp_signd.c b/source4/torture/ntp/ntp_signd.c index 89eb1a0022a..5f097fedd01 100644 --- a/source4/torture/ntp/ntp_signd.c +++ b/source4/torture/ntp/ntp_signd.c @@ -113,6 +113,7 @@ static bool test_ntp_signd(struct torture_context *tctx, creds = netlogon_creds_client_init(tctx, a.in.account_name, a.in.computer_name, + a.in.secure_channel_type, &credentials1, &credentials2, pwhash, &credentials3, negotiate_flags); diff --git a/source4/torture/rpc/lsa.c b/source4/torture/rpc/lsa.c index 107af110886..7385ad4b911 100644 --- a/source4/torture/rpc/lsa.c +++ b/source4/torture/rpc/lsa.c @@ -2715,6 +2715,7 @@ static bool check_pw_with_ServerAuthenticate3(struct dcerpc_pipe *p, creds = netlogon_creds_client_init(tctx, a.in.account_name, a.in.computer_name, + a.in.secure_channel_type, &credentials1, &credentials2, &mach_password, &credentials3, negotiate_flags); diff --git a/source4/torture/rpc/netlogon.c b/source4/torture/rpc/netlogon.c index dadf8bc4f98..7569117ddd5 100644 --- a/source4/torture/rpc/netlogon.c +++ b/source4/torture/rpc/netlogon.c @@ -173,6 +173,7 @@ bool test_SetupCredentials(struct dcerpc_pipe *p, struct torture_context *tctx, creds = netlogon_creds_client_init(tctx, a.in.account_name, a.in.computer_name, + a.in.secure_channel_type, &credentials1, &credentials2, mach_password, &credentials3, 0); @@ -201,28 +202,28 @@ bool test_SetupCredentials(struct dcerpc_pipe *p, struct torture_context *tctx, return true; } -bool test_SetupCredentials2(struct dcerpc_pipe *p, struct torture_context *tctx, - uint32_t negotiate_flags, - struct cli_credentials *machine_credentials, - enum netr_SchannelType sec_chan_type, - struct netlogon_creds_CredentialState **creds_out) +bool test_SetupCredentials2ex(struct dcerpc_pipe *p, struct torture_context *tctx, + uint32_t negotiate_flags, + struct cli_credentials *machine_credentials, + const char *computer_name, + enum netr_SchannelType sec_chan_type, + NTSTATUS expected_result, + struct netlogon_creds_CredentialState **creds_out) { struct netr_ServerReqChallenge r; struct netr_ServerAuthenticate2 a; struct netr_Credential credentials1, credentials2, credentials3; struct netlogon_creds_CredentialState *creds; const struct samr_Password *mach_password; - const char *machine_name; struct dcerpc_binding_handle *b = p->binding_handle; + const char *account_name = cli_credentials_get_username(machine_credentials); mach_password = cli_credentials_get_nt_hash(machine_credentials, tctx); - machine_name = cli_credentials_get_workstation(machine_credentials); torture_comment(tctx, "Testing ServerReqChallenge\n"); - r.in.server_name = NULL; - r.in.computer_name = machine_name; + r.in.computer_name = computer_name; r.in.credentials = &credentials1; r.out.return_credentials = &credentials2; @@ -233,9 +234,9 @@ bool test_SetupCredentials2(struct dcerpc_pipe *p, struct torture_context *tctx, torture_assert_ntstatus_ok(tctx, r.out.result, "ServerReqChallenge failed"); a.in.server_name = NULL; - a.in.account_name = talloc_asprintf(tctx, "%s$", machine_name); + a.in.account_name = account_name; a.in.secure_channel_type = sec_chan_type; - a.in.computer_name = machine_name; + a.in.computer_name = computer_name; a.in.negotiate_flags = &negotiate_flags; a.out.negotiate_flags = &negotiate_flags; a.in.credentials = &credentials3; @@ -243,6 +244,7 @@ bool test_SetupCredentials2(struct dcerpc_pipe *p, struct torture_context *tctx, creds = netlogon_creds_client_init(tctx, a.in.account_name, a.in.computer_name, + a.in.secure_channel_type, &credentials1, &credentials2, mach_password, &credentials3, negotiate_flags); @@ -253,10 +255,16 @@ bool test_SetupCredentials2(struct dcerpc_pipe *p, struct torture_context *tctx, torture_assert_ntstatus_ok(tctx, dcerpc_netr_ServerAuthenticate2_r(b, tctx, &a), "ServerAuthenticate2 failed"); - torture_assert_ntstatus_ok(tctx, a.out.result, "ServerAuthenticate2 failed"); + torture_assert_ntstatus_equal(tctx, a.out.result, expected_result, + "ServerAuthenticate2 unexpected"); - torture_assert(tctx, netlogon_creds_client_check(creds, &credentials3), - "Credential chaining failed"); + if (NT_STATUS_IS_OK(expected_result)) { + torture_assert(tctx, netlogon_creds_client_check(creds, &credentials3), + "Credential chaining failed"); + } else { + torture_assert(tctx, !netlogon_creds_client_check(creds, &credentials3), + "Credential chaining passed unexptected"); + } torture_comment(tctx, "negotiate_flags=0x%08x\n", negotiate_flags); @@ -264,6 +272,22 @@ bool test_SetupCredentials2(struct dcerpc_pipe *p, struct torture_context *tctx, return true; } +bool test_SetupCredentials2(struct dcerpc_pipe *p, struct torture_context *tctx, + uint32_t negotiate_flags, + struct cli_credentials *machine_credentials, + enum netr_SchannelType sec_chan_type, + struct netlogon_creds_CredentialState **creds_out) +{ + const char *computer_name = + cli_credentials_get_workstation(machine_credentials); + + return test_SetupCredentials2ex(p, tctx, negotiate_flags, + machine_credentials, + computer_name, + sec_chan_type, + NT_STATUS_OK, + creds_out); +} bool test_SetupCredentials3(struct dcerpc_pipe *p, struct torture_context *tctx, uint32_t negotiate_flags, @@ -310,6 +334,7 @@ bool test_SetupCredentials3(struct dcerpc_pipe *p, struct torture_context *tctx, creds = netlogon_creds_client_init(tctx, a.in.account_name, a.in.computer_name, + a.in.secure_channel_type, &credentials1, &credentials2, &mach_password, &credentials3, negotiate_flags); @@ -1041,6 +1066,115 @@ static bool test_SamLogon(struct torture_context *tctx, return test_netlogon_ops(p, tctx, credentials, creds); } +static bool test_invalidAuthenticate2(struct torture_context *tctx, + struct dcerpc_pipe *p, + struct cli_credentials *credentials) +{ + struct netlogon_creds_CredentialState *creds; + uint32_t flags = NETLOGON_NEG_AUTH2_FLAGS | NETLOGON_NEG_SUPPORTS_AES; + + torture_comment(tctx, "Testing invalidAuthenticate2\n"); + + if (!test_SetupCredentials2(p, tctx, flags, + credentials, + cli_credentials_get_secure_channel_type(credentials), + &creds)) { + return false; + } + + if (!test_SetupCredentials2ex(p, tctx, flags, + credentials, + "1234567890123456", + cli_credentials_get_secure_channel_type(credentials), + STATUS_BUFFER_OVERFLOW, + &creds)) { + return false; + } + + if (!test_SetupCredentials2ex(p, tctx, flags, + credentials, + "123456789012345", + cli_credentials_get_secure_channel_type(credentials), + NT_STATUS_OK, + &creds)) { + return false; + } + + return true; +} + +static bool test_ServerReqChallengeGlobal(struct torture_context *tctx, + struct dcerpc_pipe *p1, + struct cli_credentials *machine_credentials) +{ + uint32_t flags = NETLOGON_NEG_AUTH2_FLAGS | NETLOGON_NEG_SUPPORTS_AES; + struct netr_ServerReqChallenge r; + struct netr_ServerAuthenticate3 a; + struct netr_Credential credentials1, credentials2, credentials3; + struct netlogon_creds_CredentialState *creds; + struct samr_Password mach_password; + uint32_t rid; + const char *machine_name; + const char *plain_pass; + struct dcerpc_binding_handle *b1 = p1->binding_handle; + struct dcerpc_pipe *p2 = NULL; + struct dcerpc_binding_handle *b2 = NULL; + + machine_name = cli_credentials_get_workstation(machine_credentials); + plain_pass = cli_credentials_get_password(machine_credentials); + + torture_comment(tctx, "Testing ServerReqChallenge on b1\n"); + + torture_assert_ntstatus_ok(tctx, + dcerpc_pipe_connect_b(tctx, &p2, p1->binding, + &ndr_table_netlogon, + machine_credentials, + tctx->ev, tctx->lp_ctx), + "dcerpc_pipe_connect_b failed"); + b2 = p2->binding_handle; + + r.in.server_name = NULL; + r.in.computer_name = machine_name; + r.in.credentials = &credentials1; + r.out.return_credentials = &credentials2; + + generate_random_buffer(credentials1.data, sizeof(credentials1.data)); + + torture_assert_ntstatus_ok(tctx, dcerpc_netr_ServerReqChallenge_r(b1, tctx, &r), + "ServerReqChallenge failed on b1"); + torture_assert_ntstatus_ok(tctx, r.out.result, "ServerReqChallenge failed on b1"); + + E_md4hash(plain_pass, mach_password.hash); + + a.in.server_name = NULL; + a.in.account_name = talloc_asprintf(tctx, "%s$", machine_name); + a.in.secure_channel_type = cli_credentials_get_secure_channel_type(machine_credentials); + a.in.computer_name = machine_name; + a.in.negotiate_flags = &flags; + a.in.credentials = &credentials3; + a.out.return_credentials = &credentials3; + a.out.negotiate_flags = &flags; + a.out.rid = &rid; + + creds = netlogon_creds_client_init(tctx, a.in.account_name, + a.in.computer_name, + a.in.secure_channel_type, + &credentials1, &credentials2, + &mach_password, &credentials3, + flags); + + torture_assert(tctx, creds != NULL, "memory allocation"); + + torture_comment(tctx, "Testing ServerAuthenticate3 on b2\n"); + + torture_assert_ntstatus_ok(tctx, dcerpc_netr_ServerAuthenticate3_r(b2, tctx, &a), + "ServerAuthenticate3 failed on b2"); + torture_assert_ntstatus_ok(tctx, a.out.result, "ServerAuthenticate3 failed on b2"); + torture_assert(tctx, netlogon_creds_client_check(creds, &credentials3), "Credential chaining failed"); + + return true; +} + static bool test_SamLogon_NULL_domain(struct torture_context *tctx, struct dcerpc_pipe *p, struct cli_credentials *credentials) @@ -3880,6 +4014,8 @@ struct torture_suite *torture_rpc_netlogon(TALLOC_CTX *mem_ctx) torture_rpc_tcase_add_test(tcase, "LogonUasLogon", test_LogonUasLogon); torture_rpc_tcase_add_test(tcase, "LogonUasLogoff", test_LogonUasLogoff); torture_rpc_tcase_add_test_creds(tcase, "SamLogon", test_SamLogon); + torture_rpc_tcase_add_test_creds(tcase, "invalidAuthenticate2", test_invalidAuthenticate2); + torture_rpc_tcase_add_test_creds(tcase, "ServerReqChallengeGlobal", test_ServerReqChallengeGlobal); torture_rpc_tcase_add_test_creds(tcase, "SetPassword", test_SetPassword); torture_rpc_tcase_add_test_creds(tcase, "SetPassword2", test_SetPassword2); torture_rpc_tcase_add_test_creds(tcase, "SetPassword2_AES", test_SetPassword2_AES); diff --git a/source4/torture/rpc/samba3rpc.c b/source4/torture/rpc/samba3rpc.c index 9443d5e8480..432e9d53509 100644 --- a/source4/torture/rpc/samba3rpc.c +++ b/source4/torture/rpc/samba3rpc.c @@ -1015,6 +1015,7 @@ static bool auth2(struct torture_context *tctx, creds_state = netlogon_creds_client_init(mem_ctx, a.in.account_name, a.in.computer_name, + a.in.secure_channel_type, r.in.credentials, r.out.return_credentials, &mach_pw, &netr_cred, negotiate_flags); @@ -2146,6 +2147,7 @@ static bool torture_samba3_rpc_randomauth2(struct torture_context *torture) creds_state = netlogon_creds_client_init(mem_ctx, a.in.account_name, a.in.computer_name, + a.in.secure_channel_type, r.in.credentials, r.out.return_credentials, &mach_pw, &netr_cred, negotiate_flags); |