s3-krb5 Only build ADS support if arcfour-hmac-md5 is available
authorAndrew Bartlett <abartlet@samba.org>
Mon, 9 Aug 2010 22:25:02 +0000 (08:25 +1000)
committerSimo Sorce <idra@samba.org>
Fri, 13 Aug 2010 13:08:27 +0000 (09:08 -0400)
Modern Kerberos implementations have either defines or enums for these
key types, which makes doing #ifdef difficult.  This shows up in files
such as libnet_samsync_keytab.c, the bulk of which is not compiled on
current Fedora 12, for example.

The downside is that this makes Samba unconditionally depend on the
arcfour-hmac-md5 encryption type at build time.  We will no longer
support libraries that only support the DES based encryption types.
However, the single-DES types that are supported in common with AD are
already painfully weak - so much so that they are disabled by default
in modern Kerberos libraries.

If not found, ADS support will not be compiled in.

This means that our 'net ads join' will no longer set the
ACB_USE_DES_KEY_ONLY flag, and we will always try to use
arcfour-hmac-md5.

A future improvement would be to remove the use of the DES encryption
types totally, but this would require that any ACB_USE_DES_KEY_ONLY
flag be removed from existing joins.

Andrew Bartlett

Signed-off-by: Simo Sorce <idra@samba.org>
source3/configure.in
source3/include/smb_krb5.h
source3/libads/kerberos_keytab.c
source3/libads/kerberos_verify.c
source3/libnet/libnet_dssync_keytab.c
source3/libnet/libnet_join.c
source3/libnet/libnet_join.c.orig [new file with mode: 0644]
source3/libnet/libnet_samsync_keytab.c
source3/libnet/libnet_samsync_keytab.c.orig [new file with mode: 0644]
source3/libsmb/clikrb5.c

index 6b736712d8e8746f1d15ae88bea67dca40885acf..b4564eec0831015044761b9755f75fa2b86d87f4 100644 (file)
@@ -4040,6 +4040,7 @@ if test x"$with_ads_support" != x"no"; then
               [Whether the krb5_keyblock struct has a keyvalue property])
   fi
 
+  found_arcfour_hmac=no
   AC_CACHE_CHECK([for ENCTYPE_ARCFOUR_HMAC_MD5],
                  samba_cv_HAVE_ENCTYPE_ARCFOUR_HMAC_MD5,[
     AC_TRY_COMPILE([#include <krb5.h>],
@@ -4057,7 +4058,19 @@ if test x"$with_ads_support" != x"no"; then
   if test x"$samba_cv_HAVE_ENCTYPE_ARCFOUR_HMAC_MD5" = x"yes" -a\
           x"$samba_cv_HAVE_KEYTYPE_ARCFOUR_56" = x"yes"; then
     AC_DEFINE(HAVE_ENCTYPE_ARCFOUR_HMAC_MD5,1,
-              [Whether the ENCTYPE_ARCFOUR_HMAC_MD5 key type is available])
+              [Whether the ENCTYPE_ARCFOUR_HMAC_MD5 key type definition is available])
+    found_arcfour_hmac=yes
+  fi
+  AC_CACHE_CHECK([for ENCTYPE_ARCFOUR_HMAC],
+                 samba_cv_HAVE_ENCTYPE_ARCFOUR_HMAC,[
+    AC_TRY_COMPILE([#include <krb5.h>],
+      [krb5_enctype enctype; enctype = ENCTYPE_ARCFOUR_HMAC;],
+      samba_cv_HAVE_ENCTYPE_ARCFOUR_HMAC=yes,
+      samba_cv_HAVE_ENCTYPE_ARCFOUR_HMAC=no)])
+  if test x"$samba_cv_HAVE_ENCTYPE_ARCFOUR_HMAC" = x"yes"; then
+    AC_DEFINE(HAVE_ENCTYPE_ARCFOUR_HMAC,1,
+              [Whether the ENCTYPE_ARCFOUR_HMAC key type definition is available])
+    found_arcfour_hmac=yes
   fi
 
   AC_CACHE_CHECK([for AP_OPTS_USE_SUBKEY],
@@ -4300,6 +4313,11 @@ if test x"$with_ads_support" != x"no"; then
   # NOTE: all tests should be done before this block!
   #
   #
+  if test x"$found_arcfour_hmac" != x"yes"; then
+    AC_MSG_WARN(arcfour-hmac-md5 encryption type not found in -lkrb5)
+    use_ads=no
+  fi
+
   if test x"$ac_cv_lib_ext_krb5_krb5_mk_req_extended" != x"yes"; then
     AC_MSG_WARN(krb5_mk_req_extended not found in -lkrb5)
     use_ads=no
index 35cc047868b5c28f53628ea1268f6c47e22544d1..0a6ba79038a18c56b92fe7d63ca57b98cb5a44ad 100644 (file)
@@ -25,7 +25,7 @@
 #endif
 
 /* Heimdal uses a slightly different name */
-#if defined(HAVE_ENCTYPE_ARCFOUR_HMAC_MD5)
+#if defined(HAVE_ENCTYPE_ARCFOUR_HMAC_MD5) && !defined(HAVE_ENCTYPE_ARCFOUR_HMAC)
 #define ENCTYPE_ARCFOUR_HMAC ENCTYPE_ARCFOUR_HMAC_MD5
 #endif
 
index a874901caf3b303170bdc6f563eae5f7e224faa9..386ce83ea7bc5755823fd5091416a99e1ed50ea3 100644 (file)
@@ -236,17 +236,16 @@ int ads_keytab_add_entry(ADS_STRUCT *ads, const char *srvPrinc)
        krb5_keytab keytab = NULL;
        krb5_data password;
        krb5_kvno kvno;
-        krb5_enctype enctypes[4] = { ENCTYPE_DES_CBC_CRC, ENCTYPE_DES_CBC_MD5, 0, 0 };
+        krb5_enctype enctypes[4] = { ENCTYPE_DES_CBC_CRC, 
+                                    ENCTYPE_DES_CBC_MD5, 
+                                    ENCTYPE_ARCFOUR_HMAC, 
+                                    0 };
        char *princ_s = NULL, *short_princ_s = NULL;
        char *password_s = NULL;
        char *my_fqdn;
        TALLOC_CTX *ctx = NULL;
        char *machine_name;
 
-#if defined(ENCTYPE_ARCFOUR_HMAC)
-        enctypes[2] = ENCTYPE_ARCFOUR_HMAC;
-#endif
-
        initialize_krb5_error_table();
        ret = krb5_init_context(&context);
        if (ret) {
index 887dac05025511100d230ff74ac94cd1338ad373..7eda7fd8dd80c73752d76da8124e088fed8fdddd 100644 (file)
@@ -344,9 +344,7 @@ static krb5_error_code ads_secrets_verify_ticket(krb5_context context,
        /* Let's make some room for 2 password (old and new)*/
        krb5_data passwords[2];
        krb5_enctype enctypes[] = {
-#if defined(ENCTYPE_ARCFOUR_HMAC)
                ENCTYPE_ARCFOUR_HMAC,
-#endif
                ENCTYPE_DES_CBC_CRC,
                ENCTYPE_DES_CBC_MD5,
                ENCTYPE_NULL
index b0c745d1107ade72e80c866a9d95d2feefe49f87..96fb9c994b048bb66ab7eedb7e5d622bcfa9ec01 100644 (file)
@@ -25,7 +25,7 @@
 #include "libnet/libnet_keytab.h"
 #include "librpc/gen_ndr/ndr_drsblobs.h"
 
-#if defined(HAVE_ADS) && defined(ENCTYPE_ARCFOUR_HMAC)
+#if defined(HAVE_ADS)
 
 static NTSTATUS keytab_startup(struct dssync_context *ctx, TALLOC_CTX *mem_ctx,
                               struct replUpToDateVectorBlob **pold_utdv)
@@ -601,7 +601,7 @@ static NTSTATUS keytab_process_objects(struct dssync_context *ctx,
 {
        return NT_STATUS_NOT_SUPPORTED;
 }
-#endif /* defined(HAVE_ADS) && defined(ENCTYPE_ARCFOUR_HMAC) */
+#endif /* defined(HAVE_ADS) */
 
 const struct dssync_ops libnet_dssync_keytab_ops = {
        .startup                = keytab_startup,
index c710f9e1b1d8ca4db28be9fc7ebe8441cfa9326f..7d83dc3253a0bd5fa07f3536f0c6497426e60ffd 100644 (file)
@@ -982,12 +982,6 @@ static NTSTATUS libnet_join_joindomain_rpc(TALLOC_CTX *mem_ctx,
        /* Fill in the additional account flags now */
 
        acct_flags |= ACB_PWNOEXP;
-       if (r->out.domain_is_ad) {
-#if !defined(ENCTYPE_ARCFOUR_HMAC)
-               acct_flags |= ACB_USE_DES_KEY_ONLY;
-#endif
-               ;;
-       }
 
        /* Set account flags on machine account */
        ZERO_STRUCT(user_info.info16);
diff --git a/source3/libnet/libnet_join.c.orig b/source3/libnet/libnet_join.c.orig
new file mode 100644 (file)
index 0000000..c710f9e
--- /dev/null
@@ -0,0 +1,2187 @@
+/*
+ *  Unix SMB/CIFS implementation.
+ *  libnet Join Support
+ *  Copyright (C) Gerald (Jerry) Carter 2006
+ *  Copyright (C) Guenther Deschner 2007-2008
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "includes.h"
+#include "ads.h"
+#include "librpc/gen_ndr/ndr_libnet_join.h"
+#include "libnet/libnet_join.h"
+#include "libcli/auth/libcli_auth.h"
+#include "../librpc/gen_ndr/cli_samr.h"
+#include "rpc_client/init_samr.h"
+#include "../librpc/gen_ndr/cli_lsa.h"
+#include "rpc_client/cli_lsarpc.h"
+#include "../librpc/gen_ndr/cli_netlogon.h"
+#include "rpc_client/cli_netlogon.h"
+#include "lib/smbconf/smbconf.h"
+#include "lib/smbconf/smbconf_reg.h"
+#include "../libds/common/flags.h"
+#include "secrets.h"
+
+/****************************************************************
+****************************************************************/
+
+#define LIBNET_JOIN_DUMP_CTX(ctx, r, f) \
+       do { \
+               char *str = NULL; \
+               str = NDR_PRINT_FUNCTION_STRING(ctx, libnet_JoinCtx, f, r); \
+               DEBUG(1,("libnet_Join:\n%s", str)); \
+               TALLOC_FREE(str); \
+       } while (0)
+
+#define LIBNET_JOIN_IN_DUMP_CTX(ctx, r) \
+       LIBNET_JOIN_DUMP_CTX(ctx, r, NDR_IN | NDR_SET_VALUES)
+#define LIBNET_JOIN_OUT_DUMP_CTX(ctx, r) \
+       LIBNET_JOIN_DUMP_CTX(ctx, r, NDR_OUT)
+
+#define LIBNET_UNJOIN_DUMP_CTX(ctx, r, f) \
+       do { \
+               char *str = NULL; \
+               str = NDR_PRINT_FUNCTION_STRING(ctx, libnet_UnjoinCtx, f, r); \
+               DEBUG(1,("libnet_Unjoin:\n%s", str)); \
+               TALLOC_FREE(str); \
+       } while (0)
+
+#define LIBNET_UNJOIN_IN_DUMP_CTX(ctx, r) \
+       LIBNET_UNJOIN_DUMP_CTX(ctx, r, NDR_IN | NDR_SET_VALUES)
+#define LIBNET_UNJOIN_OUT_DUMP_CTX(ctx, r) \
+       LIBNET_UNJOIN_DUMP_CTX(ctx, r, NDR_OUT)
+
+/****************************************************************
+****************************************************************/
+
+static void libnet_join_set_error_string(TALLOC_CTX *mem_ctx,
+                                        struct libnet_JoinCtx *r,
+                                        const char *format, ...)
+{
+       va_list args;
+
+       if (r->out.error_string) {
+               return;
+       }
+
+       va_start(args, format);
+       r->out.error_string = talloc_vasprintf(mem_ctx, format, args);
+       va_end(args);
+}
+
+/****************************************************************
+****************************************************************/
+
+static void libnet_unjoin_set_error_string(TALLOC_CTX *mem_ctx,
+                                          struct libnet_UnjoinCtx *r,
+                                          const char *format, ...)
+{
+       va_list args;
+
+       if (r->out.error_string) {
+               return;
+       }
+
+       va_start(args, format);
+       r->out.error_string = talloc_vasprintf(mem_ctx, format, args);
+       va_end(args);
+}
+
+#ifdef WITH_ADS
+
+/****************************************************************
+****************************************************************/
+
+static ADS_STATUS libnet_connect_ads(const char *dns_domain_name,
+                                    const char *netbios_domain_name,
+                                    const char *dc_name,
+                                    const char *user_name,
+                                    const char *password,
+                                    ADS_STRUCT **ads)
+{
+       ADS_STATUS status;
+       ADS_STRUCT *my_ads = NULL;
+       char *cp;
+
+       my_ads = ads_init(dns_domain_name,
+                         netbios_domain_name,
+                         dc_name);
+       if (!my_ads) {
+               return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
+       }
+
+       if (user_name) {
+               SAFE_FREE(my_ads->auth.user_name);
+               my_ads->auth.user_name = SMB_STRDUP(user_name);
+               if ((cp = strchr_m(my_ads->auth.user_name, '@'))!=0) {
+                       *cp++ = '\0';
+                       SAFE_FREE(my_ads->auth.realm);
+                       my_ads->auth.realm = smb_xstrdup(cp);
+                       strupper_m(my_ads->auth.realm);
+               }
+       }
+
+       if (password) {
+               SAFE_FREE(my_ads->auth.password);
+               my_ads->auth.password = SMB_STRDUP(password);
+       }
+
+       status = ads_connect_user_creds(my_ads);
+       if (!ADS_ERR_OK(status)) {
+               ads_destroy(&my_ads);
+               return status;
+       }
+
+       *ads = my_ads;
+       return ADS_SUCCESS;
+}
+
+/****************************************************************
+****************************************************************/
+
+static ADS_STATUS libnet_join_connect_ads(TALLOC_CTX *mem_ctx,
+                                         struct libnet_JoinCtx *r)
+{
+       ADS_STATUS status;
+
+       status = libnet_connect_ads(r->out.dns_domain_name,
+                                   r->out.netbios_domain_name,
+                                   r->in.dc_name,
+                                   r->in.admin_account,
+                                   r->in.admin_password,
+                                   &r->in.ads);
+       if (!ADS_ERR_OK(status)) {
+               libnet_join_set_error_string(mem_ctx, r,
+                       "failed to connect to AD: %s",
+                       ads_errstr(status));
+               return status;
+       }
+
+       if (!r->out.netbios_domain_name) {
+               r->out.netbios_domain_name = talloc_strdup(mem_ctx,
+                                                          r->in.ads->server.workgroup);
+               ADS_ERROR_HAVE_NO_MEMORY(r->out.netbios_domain_name);
+       }
+
+       if (!r->out.dns_domain_name) {
+               r->out.dns_domain_name = talloc_strdup(mem_ctx,
+                                                      r->in.ads->config.realm);
+               ADS_ERROR_HAVE_NO_MEMORY(r->out.dns_domain_name);
+       }
+
+       r->out.domain_is_ad = true;
+
+       return ADS_SUCCESS;
+}
+
+/****************************************************************
+****************************************************************/
+
+static ADS_STATUS libnet_unjoin_connect_ads(TALLOC_CTX *mem_ctx,
+                                           struct libnet_UnjoinCtx *r)
+{
+       ADS_STATUS status;
+
+       status = libnet_connect_ads(r->in.domain_name,
+                                   r->in.domain_name,
+                                   r->in.dc_name,
+                                   r->in.admin_account,
+                                   r->in.admin_password,
+                                   &r->in.ads);
+       if (!ADS_ERR_OK(status)) {
+               libnet_unjoin_set_error_string(mem_ctx, r,
+                       "failed to connect to AD: %s",
+                       ads_errstr(status));
+       }
+
+       return status;
+}
+
+/****************************************************************
+ join a domain using ADS (LDAP mods)
+****************************************************************/
+
+static ADS_STATUS libnet_join_precreate_machine_acct(TALLOC_CTX *mem_ctx,
+                                                    struct libnet_JoinCtx *r)
+{
+       ADS_STATUS status;
+       LDAPMessage *res = NULL;
+       const char *attrs[] = { "dn", NULL };
+       bool moved = false;
+
+       status = ads_check_ou_dn(mem_ctx, r->in.ads, &r->in.account_ou);
+       if (!ADS_ERR_OK(status)) {
+               return status;
+       }
+
+       status = ads_search_dn(r->in.ads, &res, r->in.account_ou, attrs);
+       if (!ADS_ERR_OK(status)) {
+               return status;
+       }
+
+       if (ads_count_replies(r->in.ads, res) != 1) {
+               ads_msgfree(r->in.ads, res);
+               return ADS_ERROR_LDAP(LDAP_NO_SUCH_OBJECT);
+       }
+
+       ads_msgfree(r->in.ads, res);
+
+       /* Attempt to create the machine account and bail if this fails.
+          Assume that the admin wants exactly what they requested */
+
+       status = ads_create_machine_acct(r->in.ads,
+                                        r->in.machine_name,
+                                        r->in.account_ou);
+
+       if (ADS_ERR_OK(status)) {
+               DEBUG(1,("machine account creation created\n"));
+               return status;
+       } else  if ((status.error_type == ENUM_ADS_ERROR_LDAP) &&
+                   (status.err.rc == LDAP_ALREADY_EXISTS)) {
+               status = ADS_SUCCESS;
+       }
+
+       if (!ADS_ERR_OK(status)) {
+               DEBUG(1,("machine account creation failed\n"));
+               return status;
+       }
+
+       status = ads_move_machine_acct(r->in.ads,
+                                      r->in.machine_name,
+                                      r->in.account_ou,
+                                      &moved);
+       if (!ADS_ERR_OK(status)) {
+               DEBUG(1,("failure to locate/move pre-existing "
+                       "machine account\n"));
+               return status;
+       }
+
+       DEBUG(1,("The machine account %s the specified OU.\n",
+               moved ? "was moved into" : "already exists in"));
+
+       return status;
+}
+
+/****************************************************************
+****************************************************************/
+
+static ADS_STATUS libnet_unjoin_remove_machine_acct(TALLOC_CTX *mem_ctx,
+                                                   struct libnet_UnjoinCtx *r)
+{
+       ADS_STATUS status;
+
+       if (!r->in.ads) {
+               status = libnet_unjoin_connect_ads(mem_ctx, r);
+               if (!ADS_ERR_OK(status)) {
+                       libnet_unjoin_set_error_string(mem_ctx, r,
+                               "failed to connect to AD: %s",
+                               ads_errstr(status));
+                       return status;
+               }
+       }
+
+       status = ads_leave_realm(r->in.ads, r->in.machine_name);
+       if (!ADS_ERR_OK(status)) {
+               libnet_unjoin_set_error_string(mem_ctx, r,
+                       "failed to leave realm: %s",
+                       ads_errstr(status));
+               return status;
+       }
+
+       return ADS_SUCCESS;
+}
+
+/****************************************************************
+****************************************************************/
+
+static ADS_STATUS libnet_join_find_machine_acct(TALLOC_CTX *mem_ctx,
+                                               struct libnet_JoinCtx *r)
+{
+       ADS_STATUS status;
+       LDAPMessage *res = NULL;
+       char *dn = NULL;
+
+       if (!r->in.machine_name) {
+               return ADS_ERROR(LDAP_NO_MEMORY);
+       }
+
+       status = ads_find_machine_acct(r->in.ads,
+                                      &res,
+                                      r->in.machine_name);
+       if (!ADS_ERR_OK(status)) {
+               return status;
+       }
+
+       if (ads_count_replies(r->in.ads, res) != 1) {
+               status = ADS_ERROR_LDAP(LDAP_NO_MEMORY);
+               goto done;
+       }
+
+       dn = ads_get_dn(r->in.ads, mem_ctx, res);
+       if (!dn) {
+               status = ADS_ERROR_LDAP(LDAP_NO_MEMORY);
+               goto done;
+       }
+
+       r->out.dn = talloc_strdup(mem_ctx, dn);
+       if (!r->out.dn) {
+               status = ADS_ERROR_LDAP(LDAP_NO_MEMORY);
+               goto done;
+       }
+
+ done:
+       ads_msgfree(r->in.ads, res);
+       TALLOC_FREE(dn);
+
+       return status;
+}
+
+/****************************************************************
+ Set a machines dNSHostName and servicePrincipalName attributes
+****************************************************************/
+
+static ADS_STATUS libnet_join_set_machine_spn(TALLOC_CTX *mem_ctx,
+                                             struct libnet_JoinCtx *r)
+{
+       ADS_STATUS status;
+       ADS_MODLIST mods;
+       fstring my_fqdn;
+       const char *spn_array[3] = {NULL, NULL, NULL};
+       char *spn = NULL;
+
+       /* Find our DN */
+
+       status = libnet_join_find_machine_acct(mem_ctx, r);
+       if (!ADS_ERR_OK(status)) {
+               return status;
+       }
+
+       /* Windows only creates HOST/shortname & HOST/fqdn. */
+
+       spn = talloc_asprintf(mem_ctx, "HOST/%s", r->in.machine_name);
+       if (!spn) {
+               return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
+       }
+       strupper_m(spn);
+       spn_array[0] = spn;
+
+       if (!name_to_fqdn(my_fqdn, r->in.machine_name)
+           || (strchr(my_fqdn, '.') == NULL)) {
+               fstr_sprintf(my_fqdn, "%s.%s", r->in.machine_name,
+                            r->out.dns_domain_name);
+       }
+
+       strlower_m(my_fqdn);
+
+       if (!strequal(my_fqdn, r->in.machine_name)) {
+               spn = talloc_asprintf(mem_ctx, "HOST/%s", my_fqdn);
+               if (!spn) {
+                       return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
+               }
+               spn_array[1] = spn;
+       }
+
+       mods = ads_init_mods(mem_ctx);
+       if (!mods) {
+               return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
+       }
+
+       /* fields of primary importance */
+
+       status = ads_mod_str(mem_ctx, &mods, "dNSHostName", my_fqdn);
+       if (!ADS_ERR_OK(status)) {
+               return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
+       }
+
+       status = ads_mod_strlist(mem_ctx, &mods, "servicePrincipalName",
+                                spn_array);
+       if (!ADS_ERR_OK(status)) {
+               return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
+       }
+
+       return ads_gen_mod(r->in.ads, r->out.dn, mods);
+}
+
+/****************************************************************
+****************************************************************/
+
+static ADS_STATUS libnet_join_set_machine_upn(TALLOC_CTX *mem_ctx,
+                                             struct libnet_JoinCtx *r)
+{
+       ADS_STATUS status;
+       ADS_MODLIST mods;
+
+       if (!r->in.create_upn) {
+               return ADS_SUCCESS;
+       }
+
+       /* Find our DN */
+
+       status = libnet_join_find_machine_acct(mem_ctx, r);
+       if (!ADS_ERR_OK(status)) {
+               return status;
+       }
+
+       if (!r->in.upn) {
+               r->in.upn = talloc_asprintf(mem_ctx,
+                                           "host/%s@%s",
+                                           r->in.machine_name,
+                                           r->out.dns_domain_name);
+               if (!r->in.upn) {
+                       return ADS_ERROR(LDAP_NO_MEMORY);
+               }
+       }
+
+       /* now do the mods */
+
+       mods = ads_init_mods(mem_ctx);
+       if (!mods) {
+               return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
+       }
+
+       /* fields of primary importance */
+
+       status = ads_mod_str(mem_ctx, &mods, "userPrincipalName", r->in.upn);
+       if (!ADS_ERR_OK(status)) {
+               return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
+       }
+
+       return ads_gen_mod(r->in.ads, r->out.dn, mods);
+}
+
+
+/****************************************************************
+****************************************************************/
+
+static ADS_STATUS libnet_join_set_os_attributes(TALLOC_CTX *mem_ctx,
+                                               struct libnet_JoinCtx *r)
+{
+       ADS_STATUS status;
+       ADS_MODLIST mods;
+       char *os_sp = NULL;
+
+       if (!r->in.os_name || !r->in.os_version ) {
+               return ADS_SUCCESS;
+       }
+
+       /* Find our DN */
+
+       status = libnet_join_find_machine_acct(mem_ctx, r);
+       if (!ADS_ERR_OK(status)) {
+               return status;
+       }
+
+       /* now do the mods */
+
+       mods = ads_init_mods(mem_ctx);
+       if (!mods) {
+               return ADS_ERROR(LDAP_NO_MEMORY);
+       }
+
+       os_sp = talloc_asprintf(mem_ctx, "Samba %s", samba_version_string());
+       if (!os_sp) {
+               return ADS_ERROR(LDAP_NO_MEMORY);
+       }
+
+       /* fields of primary importance */
+
+       status = ads_mod_str(mem_ctx, &mods, "operatingSystem",
+                            r->in.os_name);
+       if (!ADS_ERR_OK(status)) {
+               return status;
+       }
+
+       status = ads_mod_str(mem_ctx, &mods, "operatingSystemVersion",
+                            r->in.os_version);
+       if (!ADS_ERR_OK(status)) {
+               return status;
+       }
+
+       status = ads_mod_str(mem_ctx, &mods, "operatingSystemServicePack",
+                            os_sp);
+       if (!ADS_ERR_OK(status)) {
+               return status;
+       }
+
+       return ads_gen_mod(r->in.ads, r->out.dn, mods);
+}
+
+/****************************************************************
+****************************************************************/
+
+static bool libnet_join_create_keytab(TALLOC_CTX *mem_ctx,
+                                     struct libnet_JoinCtx *r)
+{
+       if (!USE_SYSTEM_KEYTAB) {
+               return true;
+       }
+
+       if (ads_keytab_create_default(r->in.ads) != 0) {
+               return false;
+       }
+
+       return true;
+}
+
+/****************************************************************
+****************************************************************/
+
+static bool libnet_join_derive_salting_principal(TALLOC_CTX *mem_ctx,
+                                                struct libnet_JoinCtx *r)
+{
+       uint32_t domain_func;
+       ADS_STATUS status;
+       const char *salt = NULL;
+       char *std_salt = NULL;
+
+       status = ads_domain_func_level(r->in.ads, &domain_func);
+       if (!ADS_ERR_OK(status)) {
+               libnet_join_set_error_string(mem_ctx, r,
+                       "failed to determine domain functional level: %s",
+                       ads_errstr(status));
+               return false;
+       }
+
+       /* go ahead and setup the default salt */
+
+       std_salt = kerberos_standard_des_salt();
+       if (!std_salt) {
+               libnet_join_set_error_string(mem_ctx, r,
+                       "failed to obtain standard DES salt");
+               return false;
+       }
+
+       salt = talloc_strdup(mem_ctx, std_salt);
+       if (!salt) {
+               return false;
+       }
+
+       SAFE_FREE(std_salt);
+
+       /* if it's a Windows functional domain, we have to look for the UPN */
+
+       if (domain_func == DS_DOMAIN_FUNCTION_2000) {
+               char *upn;
+
+               upn = ads_get_upn(r->in.ads, mem_ctx,
+                                 r->in.machine_name);
+               if (upn) {
+                       salt = talloc_strdup(mem_ctx, upn);
+                       if (!salt) {
+                               return false;
+                       }
+               }
+       }
+
+       return kerberos_secrets_store_des_salt(salt);
+}
+
+/****************************************************************
+****************************************************************/
+
+static ADS_STATUS libnet_join_post_processing_ads(TALLOC_CTX *mem_ctx,
+                                                 struct libnet_JoinCtx *r)
+{
+       ADS_STATUS status;
+
+       if (!r->in.ads) {
+               status = libnet_join_connect_ads(mem_ctx, r);
+               if (!ADS_ERR_OK(status)) {
+                       return status;
+               }
+       }
+
+       status = libnet_join_set_machine_spn(mem_ctx, r);
+       if (!ADS_ERR_OK(status)) {
+               libnet_join_set_error_string(mem_ctx, r,
+                       "failed to set machine spn: %s",
+                       ads_errstr(status));
+               return status;
+       }
+
+       status = libnet_join_set_os_attributes(mem_ctx, r);
+       if (!ADS_ERR_OK(status)) {
+               libnet_join_set_error_string(mem_ctx, r,
+                       "failed to set machine os attributes: %s",
+                       ads_errstr(status));
+               return status;
+       }
+
+       status = libnet_join_set_machine_upn(mem_ctx, r);
+       if (!ADS_ERR_OK(status)) {
+               libnet_join_set_error_string(mem_ctx, r,
+                       "failed to set machine upn: %s",
+                       ads_errstr(status));
+               return status;
+       }
+
+       if (!libnet_join_derive_salting_principal(mem_ctx, r)) {
+               return ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL);
+       }
+
+       if (!libnet_join_create_keytab(mem_ctx, r)) {
+               libnet_join_set_error_string(mem_ctx, r,
+                       "failed to create kerberos keytab");
+               return ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL);
+       }
+
+       return ADS_SUCCESS;
+}
+#endif /* WITH_ADS */
+
+/****************************************************************
+ Store the machine password and domain SID
+****************************************************************/
+
+static bool libnet_join_joindomain_store_secrets(TALLOC_CTX *mem_ctx,
+                                                struct libnet_JoinCtx *r)
+{
+       if (!secrets_store_domain_sid(r->out.netbios_domain_name,
+                                     r->out.domain_sid))
+       {
+               DEBUG(1,("Failed to save domain sid\n"));
+               return false;
+       }
+
+       if (!secrets_store_machine_password(r->in.machine_password,
+                                           r->out.netbios_domain_name,
+                                           r->in.secure_channel_type))
+       {
+               DEBUG(1,("Failed to save machine password\n"));
+               return false;
+       }
+
+       return true;
+}
+
+/****************************************************************
+ Connect dc's IPC$ share
+****************************************************************/
+
+static NTSTATUS libnet_join_connect_dc_ipc(const char *dc,
+                                          const char *user,
+                                          const char *pass,
+                                          bool use_kerberos,
+                                          struct cli_state **cli)
+{
+       int flags = 0;
+
+       if (use_kerberos) {
+               flags |= CLI_FULL_CONNECTION_USE_KERBEROS;
+       }
+
+       if (use_kerberos && pass) {
+               flags |= CLI_FULL_CONNECTION_FALLBACK_AFTER_KERBEROS;
+       }
+
+       return cli_full_connection(cli, NULL,
+                                  dc,
+                                  NULL, 0,
+                                  "IPC$", "IPC",
+                                  user,
+                                  NULL,
+                                  pass,
+                                  flags,
+                                  Undefined, NULL);
+}
+
+/****************************************************************
+ Lookup domain dc's info
+****************************************************************/
+
+static NTSTATUS libnet_join_lookup_dc_rpc(TALLOC_CTX *mem_ctx,
+                                         struct libnet_JoinCtx *r,
+                                         struct cli_state **cli)
+{
+       struct rpc_pipe_client *pipe_hnd = NULL;
+       struct policy_handle lsa_pol;
+       NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
+       union lsa_PolicyInformation *info = NULL;
+
+       status = libnet_join_connect_dc_ipc(r->in.dc_name,
+                                           r->in.admin_account,
+                                           r->in.admin_password,
+                                           r->in.use_kerberos,
+                                           cli);
+       if (!NT_STATUS_IS_OK(status)) {
+               goto done;
+       }
+
+       status = cli_rpc_pipe_open_noauth(*cli, &ndr_table_lsarpc.syntax_id,
+                                         &pipe_hnd);
+       if (!NT_STATUS_IS_OK(status)) {
+               DEBUG(0,("Error connecting to LSA pipe. Error was %s\n",
+                       nt_errstr(status)));
+               goto done;
+       }
+
+       status = rpccli_lsa_open_policy(pipe_hnd, mem_ctx, true,
+                                       SEC_FLAG_MAXIMUM_ALLOWED, &lsa_pol);
+       if (!NT_STATUS_IS_OK(status)) {
+               goto done;
+       }
+
+       status = rpccli_lsa_QueryInfoPolicy2(pipe_hnd, mem_ctx,
+                                            &lsa_pol,
+                                            LSA_POLICY_INFO_DNS,
+                                            &info);
+       if (NT_STATUS_IS_OK(status)) {
+               r->out.domain_is_ad = true;
+               r->out.netbios_domain_name = info->dns.name.string;
+               r->out.dns_domain_name = info->dns.dns_domain.string;
+               r->out.forest_name = info->dns.dns_forest.string;
+               r->out.domain_sid = sid_dup_talloc(mem_ctx, info->dns.sid);
+               NT_STATUS_HAVE_NO_MEMORY(r->out.domain_sid);
+       }
+
+       if (!NT_STATUS_IS_OK(status)) {
+               status = rpccli_lsa_QueryInfoPolicy(pipe_hnd, mem_ctx,
+                                                   &lsa_pol,
+                                                   LSA_POLICY_INFO_ACCOUNT_DOMAIN,
+                                                   &info);
+               if (!NT_STATUS_IS_OK(status)) {
+                       goto done;
+               }
+
+               r->out.netbios_domain_name = info->account_domain.name.string;
+               r->out.domain_sid = sid_dup_talloc(mem_ctx, info->account_domain.sid);
+               NT_STATUS_HAVE_NO_MEMORY(r->out.domain_sid);
+       }
+
+       rpccli_lsa_Close(pipe_hnd, mem_ctx, &lsa_pol);
+       TALLOC_FREE(pipe_hnd);
+
+ done:
+       return status;
+}
+
+/****************************************************************
+ Do the domain join unsecure
+****************************************************************/
+
+static NTSTATUS libnet_join_joindomain_rpc_unsecure(TALLOC_CTX *mem_ctx,
+                                                   struct libnet_JoinCtx *r,
+                                                   struct cli_state *cli)
+{
+       struct rpc_pipe_client *pipe_hnd = NULL;
+       unsigned char orig_trust_passwd_hash[16];
+       unsigned char new_trust_passwd_hash[16];
+       fstring trust_passwd;
+       NTSTATUS status;
+
+       status = cli_rpc_pipe_open_noauth(cli, &ndr_table_netlogon.syntax_id,
+                                         &pipe_hnd);
+       if (!NT_STATUS_IS_OK(status)) {
+               return status;
+       }
+
+       if (!r->in.machine_password) {
+               r->in.machine_password = generate_random_str(mem_ctx, DEFAULT_TRUST_ACCOUNT_PASSWORD_LENGTH);
+               NT_STATUS_HAVE_NO_MEMORY(r->in.machine_password);
+       }
+
+       E_md4hash(r->in.machine_password, new_trust_passwd_hash);
+
+       /* according to WKSSVC_JOIN_FLAGS_MACHINE_PWD_PASSED */
+       fstrcpy(trust_passwd, r->in.admin_password);
+       strlower_m(trust_passwd);
+
+       /*
+        * Machine names can be 15 characters, but the max length on
+        * a password is 14.  --jerry
+        */
+
+       trust_passwd[14] = '\0';
+
+       E_md4hash(trust_passwd, orig_trust_passwd_hash);
+
+       status = rpccli_netlogon_set_trust_password(pipe_hnd, mem_ctx,
+                                                   r->in.machine_name,
+                                                   orig_trust_passwd_hash,
+                                                   r->in.machine_password,
+                                                   new_trust_passwd_hash,
+                                                   r->in.secure_channel_type);
+
+       return status;
+}
+
+/****************************************************************
+ Do the domain join
+****************************************************************/
+
+static NTSTATUS libnet_join_joindomain_rpc(TALLOC_CTX *mem_ctx,
+                                          struct libnet_JoinCtx *r,
+                                          struct cli_state *cli)
+{
+       struct rpc_pipe_client *pipe_hnd = NULL;
+       struct policy_handle sam_pol, domain_pol, user_pol;
+       NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
+       char *acct_name;
+       struct lsa_String lsa_acct_name;
+       uint32_t user_rid;
+       uint32_t acct_flags = ACB_WSTRUST;
+       struct samr_Ids user_rids;
+       struct samr_Ids name_types;
+       union samr_UserInfo user_info;
+
+       struct samr_CryptPassword crypt_pwd;
+       struct samr_CryptPasswordEx crypt_pwd_ex;
+
+       ZERO_STRUCT(sam_pol);
+       ZERO_STRUCT(domain_pol);
+       ZERO_STRUCT(user_pol);
+
+       switch (r->in.secure_channel_type) {
+       case SEC_CHAN_WKSTA:
+               acct_flags = ACB_WSTRUST;
+               break;
+       case SEC_CHAN_BDC:
+               acct_flags = ACB_SVRTRUST;
+               break;
+       default:
+               return NT_STATUS_INVALID_PARAMETER;
+       }
+
+       if (!r->in.machine_password) {
+               r->in.machine_password = generate_random_str(mem_ctx, DEFAULT_TRUST_ACCOUNT_PASSWORD_LENGTH);
+               NT_STATUS_HAVE_NO_MEMORY(r->in.machine_password);
+       }
+
+       /* Open the domain */
+
+       status = cli_rpc_pipe_open_noauth(cli, &ndr_table_samr.syntax_id,
+                                         &pipe_hnd);
+       if (!NT_STATUS_IS_OK(status)) {
+               DEBUG(0,("Error connecting to SAM pipe. Error was %s\n",
+                       nt_errstr(status)));
+               goto done;
+       }
+
+       status = rpccli_samr_Connect2(pipe_hnd, mem_ctx,
+                                     pipe_hnd->desthost,
+                                     SAMR_ACCESS_ENUM_DOMAINS
+                                     | SAMR_ACCESS_LOOKUP_DOMAIN,
+                                     &sam_pol);
+       if (!NT_STATUS_IS_OK(status)) {
+               goto done;
+       }
+
+       status = rpccli_samr_OpenDomain(pipe_hnd, mem_ctx,
+                                       &sam_pol,
+                                       SAMR_DOMAIN_ACCESS_LOOKUP_INFO_1
+                                       | SAMR_DOMAIN_ACCESS_CREATE_USER
+                                       | SAMR_DOMAIN_ACCESS_OPEN_ACCOUNT,
+                                       r->out.domain_sid,
+                                       &domain_pol);
+       if (!NT_STATUS_IS_OK(status)) {
+               goto done;
+       }
+
+       /* Create domain user */
+
+       acct_name = talloc_asprintf(mem_ctx, "%s$", r->in.machine_name);
+       strlower_m(acct_name);
+
+       init_lsa_String(&lsa_acct_name, acct_name);
+
+       if (r->in.join_flags & WKSSVC_JOIN_FLAGS_ACCOUNT_CREATE) {
+               uint32_t access_desired =
+                       SEC_GENERIC_READ | SEC_GENERIC_WRITE | SEC_GENERIC_EXECUTE |
+                       SEC_STD_WRITE_DAC | SEC_STD_DELETE |
+                       SAMR_USER_ACCESS_SET_PASSWORD |
+                       SAMR_USER_ACCESS_GET_ATTRIBUTES |
+                       SAMR_USER_ACCESS_SET_ATTRIBUTES;
+               uint32_t access_granted = 0;
+
+               DEBUG(10,("Creating account with desired access mask: %d\n",
+                       access_desired));
+
+               status = rpccli_samr_CreateUser2(pipe_hnd, mem_ctx,
+                                                &domain_pol,
+                                                &lsa_acct_name,
+                                                acct_flags,
+                                                access_desired,
+                                                &user_pol,
+                                                &access_granted,
+                                                &user_rid);
+               if (!NT_STATUS_IS_OK(status) &&
+                   !NT_STATUS_EQUAL(status, NT_STATUS_USER_EXISTS)) {
+
+                       DEBUG(10,("Creation of workstation account failed: %s\n",
+                               nt_errstr(status)));
+
+                       /* If NT_STATUS_ACCESS_DENIED then we have a valid
+                          username/password combo but the user does not have
+                          administrator access. */
+
+                       if (NT_STATUS_EQUAL(status, NT_STATUS_ACCESS_DENIED)) {
+                               libnet_join_set_error_string(mem_ctx, r,
+                                       "User specified does not have "
+                                       "administrator privileges");
+                       }
+
+                       goto done;
+               }
+
+               if (NT_STATUS_EQUAL(status, NT_STATUS_USER_EXISTS)) {
+                       if (!(r->in.join_flags &
+                             WKSSVC_JOIN_FLAGS_DOMAIN_JOIN_IF_JOINED)) {
+                               goto done;
+                       }
+               }
+
+               /* We *must* do this.... don't ask... */
+
+               if (NT_STATUS_IS_OK(status)) {
+                       rpccli_samr_Close(pipe_hnd, mem_ctx, &user_pol);
+               }
+       }
+
+       status = rpccli_samr_LookupNames(pipe_hnd, mem_ctx,
+                                        &domain_pol,
+                                        1,
+                                        &lsa_acct_name,
+                                        &user_rids,
+                                        &name_types);
+       if (!NT_STATUS_IS_OK(status)) {
+               goto done;
+       }
+
+       if (name_types.ids[0] != SID_NAME_USER) {
+               DEBUG(0,("%s is not a user account (type=%d)\n",
+                       acct_name, name_types.ids[0]));
+               status = NT_STATUS_INVALID_WORKSTATION;
+               goto done;
+       }
+
+       user_rid = user_rids.ids[0];
+
+       /* Open handle on user */
+
+       status = rpccli_samr_OpenUser(pipe_hnd, mem_ctx,
+                                     &domain_pol,
+                                     SEC_FLAG_MAXIMUM_ALLOWED,
+                                     user_rid,
+                                     &user_pol);
+       if (!NT_STATUS_IS_OK(status)) {
+               goto done;
+       }
+
+       /* Fill in the additional account flags now */
+
+       acct_flags |= ACB_PWNOEXP;
+       if (r->out.domain_is_ad) {
+#if !defined(ENCTYPE_ARCFOUR_HMAC)
+               acct_flags |= ACB_USE_DES_KEY_ONLY;
+#endif
+               ;;
+       }
+
+       /* Set account flags on machine account */
+       ZERO_STRUCT(user_info.info16);
+       user_info.info16.acct_flags = acct_flags;
+
+       status = rpccli_samr_SetUserInfo(pipe_hnd, mem_ctx,
+                                        &user_pol,
+                                        16,
+                                        &user_info);
+
+       if (!NT_STATUS_IS_OK(status)) {
+
+               rpccli_samr_DeleteUser(pipe_hnd, mem_ctx,
+                                      &user_pol);
+
+               libnet_join_set_error_string(mem_ctx, r,
+                       "Failed to set account flags for machine account (%s)\n",
+                       nt_errstr(status));
+               goto done;
+       }
+
+       /* Set password on machine account - first try level 26 */
+
+       init_samr_CryptPasswordEx(r->in.machine_password,
+                                 &cli->user_session_key,
+                                 &crypt_pwd_ex);
+
+       user_info.info26.password = crypt_pwd_ex;
+       user_info.info26.password_expired = PASS_DONT_CHANGE_AT_NEXT_LOGON;
+
+       status = rpccli_samr_SetUserInfo2(pipe_hnd, mem_ctx,
+                                         &user_pol,
+                                         26,
+                                         &user_info);
+
+       if (NT_STATUS_EQUAL(status, NT_STATUS(DCERPC_FAULT_INVALID_TAG))) {
+
+               /* retry with level 24 */
+
+               init_samr_CryptPassword(r->in.machine_password,
+                                       &cli->user_session_key,
+                                       &crypt_pwd);
+
+               user_info.info24.password = crypt_pwd;
+               user_info.info24.password_expired = PASS_DONT_CHANGE_AT_NEXT_LOGON;
+
+               status = rpccli_samr_SetUserInfo2(pipe_hnd, mem_ctx,
+                                                 &user_pol,
+                                                 24,
+                                                 &user_info);
+       }
+
+       if (!NT_STATUS_IS_OK(status)) {
+
+               rpccli_samr_DeleteUser(pipe_hnd, mem_ctx,
+                                      &user_pol);
+
+               libnet_join_set_error_string(mem_ctx, r,
+                       "Failed to set password for machine account (%s)\n",
+                       nt_errstr(status));
+               goto done;
+       }
+
+       status = NT_STATUS_OK;
+
+ done:
+       if (!pipe_hnd) {
+               return status;
+       }
+
+       if (is_valid_policy_hnd(&sam_pol)) {
+               rpccli_samr_Close(pipe_hnd, mem_ctx, &sam_pol);
+       }
+       if (is_valid_policy_hnd(&domain_pol)) {
+               rpccli_samr_Close(pipe_hnd, mem_ctx, &domain_pol);
+       }
+       if (is_valid_policy_hnd(&user_pol)) {
+               rpccli_samr_Close(pipe_hnd, mem_ctx, &user_pol);
+       }
+       TALLOC_FREE(pipe_hnd);
+
+       return status;
+}
+
+/****************************************************************
+****************************************************************/
+
+NTSTATUS libnet_join_ok(const char *netbios_domain_name,
+                       const char *machine_name,
+                       const char *dc_name)
+{
+       uint32_t neg_flags = NETLOGON_NEG_AUTH2_ADS_FLAGS;
+       struct cli_state *cli = NULL;
+       struct rpc_pipe_client *pipe_hnd = NULL;
+       struct rpc_pipe_client *netlogon_pipe = NULL;
+       NTSTATUS status;
+       char *machine_password = NULL;
+       char *machine_account = NULL;
+
+       if (!dc_name) {
+               return NT_STATUS_INVALID_PARAMETER;
+       }
+
+       if (!secrets_init()) {
+               return NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
+       }
+
+       machine_password = secrets_fetch_machine_password(netbios_domain_name,
+                                                         NULL, NULL);
+       if (!machine_password) {
+               return NT_STATUS_NO_TRUST_LSA_SECRET;
+       }
+
+       if (asprintf(&machine_account, "%s$", machine_name) == -1) {
+               SAFE_FREE(machine_password);
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       status = cli_full_connection(&cli, NULL,
+                                    dc_name,
+                                    NULL, 0,
+                                    "IPC$", "IPC",
+                                    machine_account,
+                                    NULL,
+                                    machine_password,
+                                    0,
+                                    Undefined, NULL);
+       free(machine_account);
+       free(machine_password);
+
+       if (!NT_STATUS_IS_OK(status)) {
+               status = cli_full_connection(&cli, NULL,
+                                            dc_name,
+                                            NULL, 0,
+                                            "IPC$", "IPC",
+                                            "",
+                                            NULL,
+                                            "",
+                                            0,
+                                            Undefined, NULL);
+       }
+
+       if (!NT_STATUS_IS_OK(status)) {
+               return status;
+       }
+
+       status = get_schannel_session_key(cli, netbios_domain_name,
+                                         &neg_flags, &netlogon_pipe);
+       if (!NT_STATUS_IS_OK(status)) {
+               if (NT_STATUS_EQUAL(status, NT_STATUS_INVALID_NETWORK_RESPONSE)) {
+                       cli_shutdown(cli);
+                       return NT_STATUS_OK;
+               }
+
+               DEBUG(0,("libnet_join_ok: failed to get schannel session "
+                       "key from server %s for domain %s. Error was %s\n",
+               cli->desthost, netbios_domain_name, nt_errstr(status)));
+               cli_shutdown(cli);
+               return status;
+       }
+
+       if (!lp_client_schannel()) {
+               cli_shutdown(cli);
+               return NT_STATUS_OK;
+       }
+
+       status = cli_rpc_pipe_open_schannel_with_key(
+               cli, &ndr_table_netlogon.syntax_id, NCACN_NP,
+               DCERPC_AUTH_LEVEL_PRIVACY,
+               netbios_domain_name, &netlogon_pipe->dc, &pipe_hnd);
+
+       cli_shutdown(cli);
+
+       if (!NT_STATUS_IS_OK(status)) {
+               DEBUG(0,("libnet_join_ok: failed to open schannel session "
+                       "on netlogon pipe to server %s for domain %s. "
+                       "Error was %s\n",
+                       cli->desthost, netbios_domain_name, nt_errstr(status)));
+               return status;
+       }
+
+       return NT_STATUS_OK;
+}
+
+/****************************************************************
+****************************************************************/
+
+static WERROR libnet_join_post_verify(TALLOC_CTX *mem_ctx,
+                                     struct libnet_JoinCtx *r)
+{
+       NTSTATUS status;
+
+       status = libnet_join_ok(r->out.netbios_domain_name,
+                               r->in.machine_name,
+                               r->in.dc_name);
+       if (!NT_STATUS_IS_OK(status)) {
+               libnet_join_set_error_string(mem_ctx, r,
+                       "failed to verify domain membership after joining: %s",
+                       get_friendly_nt_error_msg(status));
+               return WERR_SETUP_NOT_JOINED;
+       }
+
+       return WERR_OK;
+}
+
+/****************************************************************
+****************************************************************/
+
+static bool libnet_join_unjoindomain_remove_secrets(TALLOC_CTX *mem_ctx,
+                                                   struct libnet_UnjoinCtx *r)
+{
+       if (!secrets_delete_machine_password_ex(lp_workgroup())) {
+               return false;
+       }
+
+       if (!secrets_delete_domain_sid(lp_workgroup())) {
+               return false;
+       }
+
+       return true;
+}
+
+/****************************************************************
+****************************************************************/
+
+static NTSTATUS libnet_join_unjoindomain_rpc(TALLOC_CTX *mem_ctx,
+                                            struct libnet_UnjoinCtx *r)
+{
+       struct cli_state *cli = NULL;
+       struct rpc_pipe_client *pipe_hnd = NULL;
+       struct policy_handle sam_pol, domain_pol, user_pol;
+       NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
+       char *acct_name;
+       uint32_t user_rid;
+       struct lsa_String lsa_acct_name;
+       struct samr_Ids user_rids;
+       struct samr_Ids name_types;
+       union samr_UserInfo *info = NULL;
+
+       ZERO_STRUCT(sam_pol);
+       ZERO_STRUCT(domain_pol);
+       ZERO_STRUCT(user_pol);
+
+       status = libnet_join_connect_dc_ipc(r->in.dc_name,
+                                           r->in.admin_account,
+                                           r->in.admin_password,
+                                           r->in.use_kerberos,
+                                           &cli);
+       if (!NT_STATUS_IS_OK(status)) {
+               goto done;
+       }
+
+       /* Open the domain */
+
+       status = cli_rpc_pipe_open_noauth(cli, &ndr_table_samr.syntax_id,
+                                         &pipe_hnd);
+       if (!NT_STATUS_IS_OK(status)) {
+               DEBUG(0,("Error connecting to SAM pipe. Error was %s\n",
+                       nt_errstr(status)));
+               goto done;
+       }
+
+       status = rpccli_samr_Connect2(pipe_hnd, mem_ctx,
+                                     pipe_hnd->desthost,
+                                     SEC_FLAG_MAXIMUM_ALLOWED,
+                                     &sam_pol);
+       if (!NT_STATUS_IS_OK(status)) {
+               goto done;
+       }
+
+       status = rpccli_samr_OpenDomain(pipe_hnd, mem_ctx,
+                                       &sam_pol,
+                                       SEC_FLAG_MAXIMUM_ALLOWED,
+                                       r->in.domain_sid,
+                                       &domain_pol);
+       if (!NT_STATUS_IS_OK(status)) {
+               goto done;
+       }
+
+       /* Create domain user */
+
+       acct_name = talloc_asprintf(mem_ctx, "%s$", r->in.machine_name);
+       strlower_m(acct_name);
+
+       init_lsa_String(&lsa_acct_name, acct_name);
+
+       status = rpccli_samr_LookupNames(pipe_hnd, mem_ctx,
+                                        &domain_pol,
+                                        1,
+                                        &lsa_acct_name,
+                                        &user_rids,
+                                        &name_types);
+
+       if (!NT_STATUS_IS_OK(status)) {
+               goto done;
+       }
+
+       if (name_types.ids[0] != SID_NAME_USER) {
+               DEBUG(0, ("%s is not a user account (type=%d)\n", acct_name,
+                       name_types.ids[0]));
+               status = NT_STATUS_INVALID_WORKSTATION;
+               goto done;
+       }
+
+       user_rid = user_rids.ids[0];
+
+       /* Open handle on user */
+
+       status = rpccli_samr_OpenUser(pipe_hnd, mem_ctx,
+                                     &domain_pol,
+                                     SEC_FLAG_MAXIMUM_ALLOWED,
+                                     user_rid,
+                                     &user_pol);
+       if (!NT_STATUS_IS_OK(status)) {
+               goto done;
+       }
+
+       /* Get user info */
+
+       status = rpccli_samr_QueryUserInfo(pipe_hnd, mem_ctx,
+                                          &user_pol,
+                                          16,
+                                          &info);
+       if (!NT_STATUS_IS_OK(status)) {
+               rpccli_samr_Close(pipe_hnd, mem_ctx, &user_pol);
+               goto done;
+       }
+
+       /* now disable and setuser info */
+
+       info->info16.acct_flags |= ACB_DISABLED;
+
+       status = rpccli_samr_SetUserInfo(pipe_hnd, mem_ctx,
+                                        &user_pol,
+                                        16,
+                                        info);
+
+       rpccli_samr_Close(pipe_hnd, mem_ctx, &user_pol);
+
+done:
+       if (pipe_hnd) {
+               if (is_valid_policy_hnd(&domain_pol)) {
+                       rpccli_samr_Close(pipe_hnd, mem_ctx, &domain_pol);
+               }
+               if (is_valid_policy_hnd(&sam_pol)) {
+                       rpccli_samr_Close(pipe_hnd, mem_ctx, &sam_pol);
+               }
+               TALLOC_FREE(pipe_hnd);
+       }
+
+       if (cli) {
+               cli_shutdown(cli);
+       }
+
+       return status;
+}
+
+/****************************************************************
+****************************************************************/
+
+static WERROR do_join_modify_vals_config(struct libnet_JoinCtx *r)
+{
+       WERROR werr;
+       struct smbconf_ctx *ctx;
+
+       werr = smbconf_init_reg(r, &ctx, NULL);
+       if (!W_ERROR_IS_OK(werr)) {
+               goto done;
+       }
+
+       if (!(r->in.join_flags & WKSSVC_JOIN_FLAGS_JOIN_TYPE)) {
+
+               werr = smbconf_set_global_parameter(ctx, "security", "user");
+               W_ERROR_NOT_OK_GOTO_DONE(werr);
+
+               werr = smbconf_set_global_parameter(ctx, "workgroup",
+                                                   r->in.domain_name);
+
+               smbconf_delete_global_parameter(ctx, "realm");
+               goto done;
+       }
+
+       werr = smbconf_set_global_parameter(ctx, "security", "domain");
+       W_ERROR_NOT_OK_GOTO_DONE(werr);
+
+       werr = smbconf_set_global_parameter(ctx, "workgroup",
+                                           r->out.netbios_domain_name);
+       W_ERROR_NOT_OK_GOTO_DONE(werr);
+
+       if (r->out.domain_is_ad) {
+               werr = smbconf_set_global_parameter(ctx, "security", "ads");
+               W_ERROR_NOT_OK_GOTO_DONE(werr);
+
+               werr = smbconf_set_global_parameter(ctx, "realm",
+                                                   r->out.dns_domain_name);
+               W_ERROR_NOT_OK_GOTO_DONE(werr);
+       }
+
+ done:
+       smbconf_shutdown(ctx);
+       return werr;
+}
+
+/****************************************************************
+****************************************************************/
+
+static WERROR do_unjoin_modify_vals_config(struct libnet_UnjoinCtx *r)
+{
+       WERROR werr = WERR_OK;
+       struct smbconf_ctx *ctx;
+
+       werr = smbconf_init_reg(r, &ctx, NULL);
+       if (!W_ERROR_IS_OK(werr)) {
+               goto done;
+       }
+
+       if (r->in.unjoin_flags & WKSSVC_JOIN_FLAGS_JOIN_TYPE) {
+
+               werr = smbconf_set_global_parameter(ctx, "security", "user");
+               W_ERROR_NOT_OK_GOTO_DONE(werr);
+
+               werr = smbconf_delete_global_parameter(ctx, "workgroup");
+               W_ERROR_NOT_OK_GOTO_DONE(werr);
+
+               smbconf_delete_global_parameter(ctx, "realm");
+       }
+
+ done:
+       smbconf_shutdown(ctx);
+       return werr;
+}
+
+/****************************************************************
+****************************************************************/
+
+static WERROR do_JoinConfig(struct libnet_JoinCtx *r)
+{
+       WERROR werr;
+
+       if (!W_ERROR_IS_OK(r->out.result)) {
+               return r->out.result;
+       }
+
+       if (!r->in.modify_config) {
+               return WERR_OK;
+       }
+
+       werr = do_join_modify_vals_config(r);
+       if (!W_ERROR_IS_OK(werr)) {
+               return werr;
+       }
+
+       lp_load(get_dyn_CONFIGFILE(),true,false,false,true);
+
+       r->out.modified_config = true;
+       r->out.result = werr;
+
+       return werr;
+}
+
+/****************************************************************
+****************************************************************/
+
+static WERROR libnet_unjoin_config(struct libnet_UnjoinCtx *r)
+{
+       WERROR werr;
+
+       if (!W_ERROR_IS_OK(r->out.result)) {
+               return r->out.result;
+       }
+
+       if (!r->in.modify_config) {
+               return WERR_OK;
+       }
+
+       werr = do_unjoin_modify_vals_config(r);
+       if (!W_ERROR_IS_OK(werr)) {
+               return werr;
+       }
+
+       lp_load(get_dyn_CONFIGFILE(),true,false,false,true);
+
+       r->out.modified_config = true;
+       r->out.result = werr;
+
+       return werr;
+}
+
+/****************************************************************
+****************************************************************/
+
+static bool libnet_parse_domain_dc(TALLOC_CTX *mem_ctx,
+                                  const char *domain_str,
+                                  const char **domain_p,
+                                  const char **dc_p)
+{
+       char *domain = NULL;
+       char *dc = NULL;
+       const char *p = NULL;
+
+       if (!domain_str || !domain_p || !dc_p) {
+               return false;
+       }
+
+       p = strchr_m(domain_str, '\\');
+
+       if (p != NULL) {
+               domain = talloc_strndup(mem_ctx, domain_str,
+                                        PTR_DIFF(p, domain_str));
+               dc = talloc_strdup(mem_ctx, p+1);
+               if (!dc) {
+                       return false;
+               }
+       } else {
+               domain = talloc_strdup(mem_ctx, domain_str);
+               dc = NULL;
+       }
+       if (!domain) {
+               return false;
+       }
+
+       *domain_p = domain;
+
+       if (!*dc_p && dc) {
+               *dc_p = dc;
+       }
+
+       return true;
+}
+
+/****************************************************************
+****************************************************************/
+
+static WERROR libnet_join_pre_processing(TALLOC_CTX *mem_ctx,
+                                        struct libnet_JoinCtx *r)
+{
+       if (!r->in.domain_name) {
+               libnet_join_set_error_string(mem_ctx, r,
+                       "No domain name defined");
+               return WERR_INVALID_PARAM;
+       }
+
+       if (!libnet_parse_domain_dc(mem_ctx, r->in.domain_name,
+                                   &r->in.domain_name,
+                                   &r->in.dc_name)) {
+               libnet_join_set_error_string(mem_ctx, r,
+                       "Failed to parse domain name");
+               return WERR_INVALID_PARAM;
+       }
+
+       if (IS_DC) {
+               return WERR_SETUP_DOMAIN_CONTROLLER;
+       }
+
+       if (!secrets_init()) {
+               libnet_join_set_error_string(mem_ctx, r,
+                       "Unable to open secrets database");
+               return WERR_CAN_NOT_COMPLETE;
+       }
+
+       return WERR_OK;
+}
+
+/****************************************************************
+****************************************************************/
+
+static void libnet_join_add_dom_rids_to_builtins(struct dom_sid *domain_sid)
+{
+       NTSTATUS status;
+
+       /* Try adding dom admins to builtin\admins. Only log failures. */
+       status = create_builtin_administrators(domain_sid);
+       if (NT_STATUS_EQUAL(status, NT_STATUS_PROTOCOL_UNREACHABLE)) {
+               DEBUG(10,("Unable to auto-add domain administrators to "
+                         "BUILTIN\\Administrators during join because "
+                         "winbindd must be running."));
+       } else if (!NT_STATUS_IS_OK(status)) {
+               DEBUG(5, ("Failed to auto-add domain administrators to "
+                         "BUILTIN\\Administrators during join: %s\n",
+                         nt_errstr(status)));
+       }
+
+       /* Try adding dom users to builtin\users. Only log failures. */
+       status = create_builtin_users(domain_sid);
+       if (NT_STATUS_EQUAL(status, NT_STATUS_PROTOCOL_UNREACHABLE)) {
+               DEBUG(10,("Unable to auto-add domain users to BUILTIN\\users "
+                         "during join because winbindd must be running."));
+       } else if (!NT_STATUS_IS_OK(status)) {
+               DEBUG(5, ("Failed to auto-add domain administrators to "
+                         "BUILTIN\\Administrators during join: %s\n",
+                         nt_errstr(status)));
+       }
+}
+
+/****************************************************************
+****************************************************************/
+
+static WERROR libnet_join_post_processing(TALLOC_CTX *mem_ctx,
+                                         struct libnet_JoinCtx *r)
+{
+       WERROR werr;
+
+       if (!W_ERROR_IS_OK(r->out.result)) {
+               return r->out.result;
+       }
+
+       werr = do_JoinConfig(r);
+       if (!W_ERROR_IS_OK(werr)) {
+               return werr;
+       }
+
+       if (!(r->in.join_flags & WKSSVC_JOIN_FLAGS_JOIN_TYPE)) {
+               return WERR_OK;
+       }
+
+       saf_join_store(r->out.netbios_domain_name, r->in.dc_name);
+       if (r->out.dns_domain_name) {
+               saf_join_store(r->out.dns_domain_name, r->in.dc_name);
+       }
+
+#ifdef WITH_ADS
+       if (r->out.domain_is_ad &&
+           !(r->in.join_flags & WKSSVC_JOIN_FLAGS_JOIN_UNSECURE)) {
+               ADS_STATUS ads_status;
+
+               ads_status  = libnet_join_post_processing_ads(mem_ctx, r);
+               if (!ADS_ERR_OK(ads_status)) {
+                       return WERR_GENERAL_FAILURE;
+               }
+       }
+#endif /* WITH_ADS */
+
+       libnet_join_add_dom_rids_to_builtins(r->out.domain_sid);
+
+       return WERR_OK;
+}
+
+/****************************************************************
+****************************************************************/
+
+static int libnet_destroy_JoinCtx(struct libnet_JoinCtx *r)
+{
+       const char *krb5_cc_env = NULL;
+
+       if (r->in.ads) {
+               ads_destroy(&r->in.ads);
+       }
+
+       krb5_cc_env = getenv(KRB5_ENV_CCNAME);
+       if (krb5_cc_env && StrCaseCmp(krb5_cc_env, "MEMORY:libnetjoin")) {
+               unsetenv(KRB5_ENV_CCNAME);
+       }
+
+       return 0;
+}
+
+/****************************************************************
+****************************************************************/
+
+static int libnet_destroy_UnjoinCtx(struct libnet_UnjoinCtx *r)
+{
+       const char *krb5_cc_env = NULL;
+
+       if (r->in.ads) {
+               ads_destroy(&r->in.ads);
+       }
+
+       krb5_cc_env = getenv(KRB5_ENV_CCNAME);
+       if (krb5_cc_env && StrCaseCmp(krb5_cc_env, "MEMORY:libnetjoin")) {
+               unsetenv(KRB5_ENV_CCNAME);
+       }
+
+       return 0;
+}
+
+/****************************************************************
+****************************************************************/
+
+WERROR libnet_init_JoinCtx(TALLOC_CTX *mem_ctx,
+                          struct libnet_JoinCtx **r)
+{
+       struct libnet_JoinCtx *ctx;
+       const char *krb5_cc_env = NULL;
+
+       ctx = talloc_zero(mem_ctx, struct libnet_JoinCtx);
+       if (!ctx) {
+               return WERR_NOMEM;
+       }
+
+       talloc_set_destructor(ctx, libnet_destroy_JoinCtx);
+
+       ctx->in.machine_name = talloc_strdup(mem_ctx, global_myname());
+       W_ERROR_HAVE_NO_MEMORY(ctx->in.machine_name);
+
+       krb5_cc_env = getenv(KRB5_ENV_CCNAME);
+       if (!krb5_cc_env || (strlen(krb5_cc_env) == 0)) {
+               krb5_cc_env = talloc_strdup(mem_ctx, "MEMORY:libnetjoin");
+               W_ERROR_HAVE_NO_MEMORY(krb5_cc_env);
+               setenv(KRB5_ENV_CCNAME, krb5_cc_env, 1);
+       }
+
+       ctx->in.secure_channel_type = SEC_CHAN_WKSTA;
+
+       *r = ctx;
+
+       return WERR_OK;
+}
+
+/****************************************************************
+****************************************************************/
+
+WERROR libnet_init_UnjoinCtx(TALLOC_CTX *mem_ctx,
+                            struct libnet_UnjoinCtx **r)
+{
+       struct libnet_UnjoinCtx *ctx;
+       const char *krb5_cc_env = NULL;
+
+       ctx = talloc_zero(mem_ctx, struct libnet_UnjoinCtx);
+       if (!ctx) {
+               return WERR_NOMEM;
+       }
+
+       talloc_set_destructor(ctx, libnet_destroy_UnjoinCtx);
+
+       ctx->in.machine_name = talloc_strdup(mem_ctx, global_myname());
+       W_ERROR_HAVE_NO_MEMORY(ctx->in.machine_name);
+
+       krb5_cc_env = getenv(KRB5_ENV_CCNAME);
+       if (!krb5_cc_env || (strlen(krb5_cc_env) == 0)) {
+               krb5_cc_env = talloc_strdup(mem_ctx, "MEMORY:libnetjoin");
+               W_ERROR_HAVE_NO_MEMORY(krb5_cc_env);
+               setenv(KRB5_ENV_CCNAME, krb5_cc_env, 1);
+       }
+
+       *r = ctx;
+
+       return WERR_OK;
+}
+
+/****************************************************************
+****************************************************************/
+
+static WERROR libnet_join_check_config(TALLOC_CTX *mem_ctx,
+                                      struct libnet_JoinCtx *r)
+{
+       bool valid_security = false;
+       bool valid_workgroup = false;
+       bool valid_realm = false;
+
+       /* check if configuration is already set correctly */
+
+       valid_workgroup = strequal(lp_workgroup(), r->out.netbios_domain_name);
+
+       switch (r->out.domain_is_ad) {
+               case false:
+                       valid_security = (lp_security() == SEC_DOMAIN);
+                       if (valid_workgroup && valid_security) {
+                               /* nothing to be done */
+                               return WERR_OK;
+                       }
+                       break;
+               case true:
+                       valid_realm = strequal(lp_realm(), r->out.dns_domain_name);
+                       switch (lp_security()) {
+                       case SEC_DOMAIN:
+                       case SEC_ADS:
+                               valid_security = true;
+                       }
+
+                       if (valid_workgroup && valid_realm && valid_security) {
+                               /* nothing to be done */
+                               return WERR_OK;
+                       }
+                       break;
+       }
+
+       /* check if we are supposed to manipulate configuration */
+
+       if (!r->in.modify_config) {
+
+               char *wrong_conf = talloc_strdup(mem_ctx, "");
+
+               if (!valid_workgroup) {
+                       wrong_conf = talloc_asprintf_append(wrong_conf,
+                               "\"workgroup\" set to '%s', should be '%s'",
+                               lp_workgroup(), r->out.netbios_domain_name);
+                       W_ERROR_HAVE_NO_MEMORY(wrong_conf);
+               }
+
+               if (!valid_realm) {
+                       wrong_conf = talloc_asprintf_append(wrong_conf,
+                               "\"realm\" set to '%s', should be '%s'",
+                               lp_realm(), r->out.dns_domain_name);
+                       W_ERROR_HAVE_NO_MEMORY(wrong_conf);
+               }
+
+               if (!valid_security) {
+                       const char *sec = NULL;
+                       switch (lp_security()) {
+                       case SEC_SHARE: sec = "share"; break;
+                       case SEC_USER:  sec = "user"; break;
+                       case SEC_DOMAIN: sec = "domain"; break;
+                       case SEC_ADS: sec = "ads"; break;
+                       }
+                       wrong_conf = talloc_asprintf_append(wrong_conf,
+                               "\"security\" set to '%s', should be %s",
+                               sec, r->out.domain_is_ad ?
+                               "either 'domain' or 'ads'" : "'domain'");
+                       W_ERROR_HAVE_NO_MEMORY(wrong_conf);
+               }
+
+               libnet_join_set_error_string(mem_ctx, r,
+                       "Invalid configuration (%s) and configuration modification "
+                       "was not requested", wrong_conf);
+               return WERR_CAN_NOT_COMPLETE;
+       }
+
+       /* check if we are able to manipulate configuration */
+
+       if (!lp_config_backend_is_registry()) {
+               libnet_join_set_error_string(mem_ctx, r,
+                       "Configuration manipulation requested but not "
+                       "supported by backend");
+               return WERR_NOT_SUPPORTED;
+       }
+
+       return WERR_OK;
+}
+
+/****************************************************************
+****************************************************************/
+
+static WERROR libnet_DomainJoin(TALLOC_CTX *mem_ctx,
+                               struct libnet_JoinCtx *r)
+{
+       NTSTATUS status;
+       WERROR werr;
+       struct cli_state *cli = NULL;
+#ifdef WITH_ADS
+       ADS_STATUS ads_status;
+#endif /* WITH_ADS */
+
+       if (!r->in.dc_name) {
+               struct netr_DsRGetDCNameInfo *info;
+               const char *dc;
+               status = dsgetdcname(mem_ctx,
+                                    r->in.msg_ctx,
+                                    r->in.domain_name,
+                                    NULL,
+                                    NULL,
+                                    DS_FORCE_REDISCOVERY |
+                                    DS_DIRECTORY_SERVICE_REQUIRED |
+                                    DS_WRITABLE_REQUIRED |
+                                    DS_RETURN_DNS_NAME,
+                                    &info);
+               if (!NT_STATUS_IS_OK(status)) {
+                       libnet_join_set_error_string(mem_ctx, r,
+                               "failed to find DC for domain %s",
+                               r->in.domain_name,
+                               get_friendly_nt_error_msg(status));
+                       return WERR_DCNOTFOUND;
+               }
+
+               dc = strip_hostname(info->dc_unc);
+               r->in.dc_name = talloc_strdup(mem_ctx, dc);
+               W_ERROR_HAVE_NO_MEMORY(r->in.dc_name);
+       }
+
+       status = libnet_join_lookup_dc_rpc(mem_ctx, r, &cli);
+       if (!NT_STATUS_IS_OK(status)) {
+               libnet_join_set_error_string(mem_ctx, r,
+                       "failed to lookup DC info for domain '%s' over rpc: %s",
+                       r->in.domain_name, get_friendly_nt_error_msg(status));
+               return ntstatus_to_werror(status);
+       }
+
+       werr = libnet_join_check_config(mem_ctx, r);
+       if (!W_ERROR_IS_OK(werr)) {
+               goto done;
+       }
+
+#ifdef WITH_ADS
+       if (r->out.domain_is_ad && r->in.account_ou &&
+           !(r->in.join_flags & WKSSVC_JOIN_FLAGS_JOIN_UNSECURE)) {
+
+               ads_status = libnet_join_connect_ads(mem_ctx, r);
+               if (!ADS_ERR_OK(ads_status)) {
+                       return WERR_DEFAULT_JOIN_REQUIRED;
+               }
+
+               ads_status = libnet_join_precreate_machine_acct(mem_ctx, r);
+               if (!ADS_ERR_OK(ads_status)) {
+                       libnet_join_set_error_string(mem_ctx, r,
+                               "failed to precreate account in ou %s: %s",
+                               r->in.account_ou,
+                               ads_errstr(ads_status));
+                       return WERR_DEFAULT_JOIN_REQUIRED;
+               }
+
+               r->in.join_flags &= ~WKSSVC_JOIN_FLAGS_ACCOUNT_CREATE;
+       }
+#endif /* WITH_ADS */
+
+       if ((r->in.join_flags & WKSSVC_JOIN_FLAGS_JOIN_UNSECURE) &&
+           (r->in.join_flags & WKSSVC_JOIN_FLAGS_MACHINE_PWD_PASSED)) {
+               status = libnet_join_joindomain_rpc_unsecure(mem_ctx, r, cli);
+       } else {
+               status = libnet_join_joindomain_rpc(mem_ctx, r, cli);
+       }
+       if (!NT_STATUS_IS_OK(status)) {
+               libnet_join_set_error_string(mem_ctx, r,
+                       "failed to join domain '%s' over rpc: %s",
+                       r->in.domain_name, get_friendly_nt_error_msg(status));
+               if (NT_STATUS_EQUAL(status, NT_STATUS_USER_EXISTS)) {
+                       return WERR_SETUP_ALREADY_JOINED;
+               }
+               werr = ntstatus_to_werror(status);
+               goto done;
+       }
+
+       if (!libnet_join_joindomain_store_secrets(mem_ctx, r)) {
+               werr = WERR_SETUP_NOT_JOINED;
+               goto done;
+       }
+
+       werr = WERR_OK;
+
+ done:
+       if (cli) {
+               cli_shutdown(cli);
+       }
+
+       return werr;
+}
+
+/****************************************************************
+****************************************************************/
+
+static WERROR libnet_join_rollback(TALLOC_CTX *mem_ctx,
+                                  struct libnet_JoinCtx *r)
+{
+       WERROR werr;
+       struct libnet_UnjoinCtx *u = NULL;
+
+       werr = libnet_init_UnjoinCtx(mem_ctx, &u);
+       if (!W_ERROR_IS_OK(werr)) {
+               return werr;
+       }
+
+       u->in.debug             = r->in.debug;
+       u->in.dc_name           = r->in.dc_name;
+       u->in.domain_name       = r->in.domain_name;
+       u->in.admin_account     = r->in.admin_account;
+       u->in.admin_password    = r->in.admin_password;
+       u->in.modify_config     = r->in.modify_config;
+       u->in.unjoin_flags      = WKSSVC_JOIN_FLAGS_JOIN_TYPE |
+                                 WKSSVC_JOIN_FLAGS_ACCOUNT_DELETE;
+
+       werr = libnet_Unjoin(mem_ctx, u);
+       TALLOC_FREE(u);
+
+       return werr;
+}
+
+/****************************************************************
+****************************************************************/
+
+WERROR libnet_Join(TALLOC_CTX *mem_ctx,
+                  struct libnet_JoinCtx *r)
+{
+       WERROR werr;
+
+       if (r->in.debug) {
+               LIBNET_JOIN_IN_DUMP_CTX(mem_ctx, r);
+       }
+
+       werr = libnet_join_pre_processing(mem_ctx, r);
+       if (!W_ERROR_IS_OK(werr)) {
+               goto done;
+       }
+
+       if (r->in.join_flags & WKSSVC_JOIN_FLAGS_JOIN_TYPE) {
+               werr = libnet_DomainJoin(mem_ctx, r);
+               if (!W_ERROR_IS_OK(werr)) {
+                       goto done;
+               }
+       }
+
+       werr = libnet_join_post_processing(mem_ctx, r);
+       if (!W_ERROR_IS_OK(werr)) {
+               goto done;
+       }
+
+       if (r->in.join_flags & WKSSVC_JOIN_FLAGS_JOIN_TYPE) {
+               werr = libnet_join_post_verify(mem_ctx, r);
+               if (!W_ERROR_IS_OK(werr)) {
+                       libnet_join_rollback(mem_ctx, r);
+               }
+       }
+
+ done:
+       r->out.result = werr;
+
+       if (r->in.debug) {
+               LIBNET_JOIN_OUT_DUMP_CTX(mem_ctx, r);
+       }
+       return werr;
+}
+
+/****************************************************************
+****************************************************************/
+
+static WERROR libnet_DomainUnjoin(TALLOC_CTX *mem_ctx,
+                                 struct libnet_UnjoinCtx *r)
+{
+       NTSTATUS status;
+
+       if (!r->in.domain_sid) {
+               struct dom_sid sid;
+               if (!secrets_fetch_domain_sid(lp_workgroup(), &sid)) {
+                       libnet_unjoin_set_error_string(mem_ctx, r,
+                               "Unable to fetch domain sid: are we joined?");
+                       return WERR_SETUP_NOT_JOINED;
+               }
+               r->in.domain_sid = sid_dup_talloc(mem_ctx, &sid);
+               W_ERROR_HAVE_NO_MEMORY(r->in.domain_sid);
+       }
+
+       if (!(r->in.unjoin_flags & WKSSVC_JOIN_FLAGS_ACCOUNT_DELETE) && 
+           !r->in.delete_machine_account) {
+               libnet_join_unjoindomain_remove_secrets(mem_ctx, r);
+               return WERR_OK;
+       }
+
+       if (!r->in.dc_name) {
+               struct netr_DsRGetDCNameInfo *info;
+               const char *dc;
+               status = dsgetdcname(mem_ctx,
+                                    r->in.msg_ctx,
+                                    r->in.domain_name,
+                                    NULL,
+                                    NULL,
+                                    DS_DIRECTORY_SERVICE_REQUIRED |
+                                    DS_WRITABLE_REQUIRED |
+                                    DS_RETURN_DNS_NAME,
+                                    &info);
+               if (!NT_STATUS_IS_OK(status)) {
+                       libnet_unjoin_set_error_string(mem_ctx, r,
+                               "failed to find DC for domain %s",
+                               r->in.domain_name,
+                               get_friendly_nt_error_msg(status));
+                       return WERR_DCNOTFOUND;
+               }
+
+               dc = strip_hostname(info->dc_unc);
+               r->in.dc_name = talloc_strdup(mem_ctx, dc);
+               W_ERROR_HAVE_NO_MEMORY(r->in.dc_name);
+       }
+
+#ifdef WITH_ADS
+       /* for net ads leave, try to delete the account.  If it works, 
+          no sense in disabling.  If it fails, we can still try to 
+          disable it. jmcd */
+          
+       if (r->in.delete_machine_account) {
+               ADS_STATUS ads_status;
+               ads_status = libnet_unjoin_connect_ads(mem_ctx, r);
+               if (ADS_ERR_OK(ads_status)) {
+                       /* dirty hack */
+                       r->out.dns_domain_name = 
+                               talloc_strdup(mem_ctx,
+                                             r->in.ads->server.realm);
+                       ads_status = 
+                               libnet_unjoin_remove_machine_acct(mem_ctx, r);
+               }
+               if (!ADS_ERR_OK(ads_status)) {
+                       libnet_unjoin_set_error_string(mem_ctx, r,
+                               "failed to remove machine account from AD: %s",
+                               ads_errstr(ads_status));
+               } else {
+                       r->out.deleted_machine_account = true;
+                       W_ERROR_HAVE_NO_MEMORY(r->out.dns_domain_name);
+                       libnet_join_unjoindomain_remove_secrets(mem_ctx, r);
+                       return WERR_OK;
+               }
+       }
+#endif /* WITH_ADS */
+
+       /* The WKSSVC_JOIN_FLAGS_ACCOUNT_DELETE flag really means 
+          "disable".  */
+       if (r->in.unjoin_flags & WKSSVC_JOIN_FLAGS_ACCOUNT_DELETE) {
+               status = libnet_join_unjoindomain_rpc(mem_ctx, r);
+               if (!NT_STATUS_IS_OK(status)) {
+                       libnet_unjoin_set_error_string(mem_ctx, r,
+                               "failed to disable machine account via rpc: %s",
+                               get_friendly_nt_error_msg(status));
+                       if (NT_STATUS_EQUAL(status, NT_STATUS_NO_SUCH_USER)) {
+                               return WERR_SETUP_NOT_JOINED;
+                       }
+                       return ntstatus_to_werror(status);
+               }
+               
+               r->out.disabled_machine_account = true;
+       }
+
+       /* If disable succeeded or was not requested at all, we 
+          should be getting rid of our end of things */
+
+       libnet_join_unjoindomain_remove_secrets(mem_ctx, r);
+
+       return WERR_OK;
+}
+
+/****************************************************************
+****************************************************************/
+
+static WERROR libnet_unjoin_pre_processing(TALLOC_CTX *mem_ctx,
+                                          struct libnet_UnjoinCtx *r)
+{
+       if (!r->in.domain_name) {
+               libnet_unjoin_set_error_string(mem_ctx, r,
+                       "No domain name defined");
+               return WERR_INVALID_PARAM;
+       }
+
+       if (!libnet_parse_domain_dc(mem_ctx, r->in.domain_name,
+                                   &r->in.domain_name,
+                                   &r->in.dc_name)) {
+               libnet_unjoin_set_error_string(mem_ctx, r,
+                       "Failed to parse domain name");
+               return WERR_INVALID_PARAM;
+       }
+
+       if (IS_DC) {
+               return WERR_SETUP_DOMAIN_CONTROLLER;
+       }
+
+       if (!secrets_init()) {
+               libnet_unjoin_set_error_string(mem_ctx, r,
+                       "Unable to open secrets database");
+               return WERR_CAN_NOT_COMPLETE;
+       }
+
+       return WERR_OK;
+}
+
+/****************************************************************
+****************************************************************/
+
+static WERROR libnet_unjoin_post_processing(TALLOC_CTX *mem_ctx,
+                                           struct libnet_UnjoinCtx *r)
+{
+       saf_delete(r->out.netbios_domain_name);
+       saf_delete(r->out.dns_domain_name);
+
+       return libnet_unjoin_config(r);
+}
+
+/****************************************************************
+****************************************************************/
+
+WERROR libnet_Unjoin(TALLOC_CTX *mem_ctx,
+                    struct libnet_UnjoinCtx *r)
+{
+       WERROR werr;
+
+       if (r->in.debug) {
+               LIBNET_UNJOIN_IN_DUMP_CTX(mem_ctx, r);
+       }
+
+       werr = libnet_unjoin_pre_processing(mem_ctx, r);
+       if (!W_ERROR_IS_OK(werr)) {
+               goto done;
+       }
+
+       if (r->in.unjoin_flags & WKSSVC_JOIN_FLAGS_JOIN_TYPE) {
+               werr = libnet_DomainUnjoin(mem_ctx, r);
+               if (!W_ERROR_IS_OK(werr)) {
+                       libnet_unjoin_config(r);
+                       goto done;
+               }
+       }
+
+       werr = libnet_unjoin_post_processing(mem_ctx, r);
+       if (!W_ERROR_IS_OK(werr)) {
+               goto done;
+       }
+
+ done:
+       r->out.result = werr;
+
+       if (r->in.debug) {
+               LIBNET_UNJOIN_OUT_DUMP_CTX(mem_ctx, r);
+       }
+
+       return werr;
+}
index 134922392ca0b2b4bdc9c28b5694e4861a941887..faba1e73ad3b8e5bb1814bf453dea73edd408dd5 100644 (file)
@@ -24,7 +24,7 @@
 #include "libnet/libnet_keytab.h"
 #include "libnet/libnet_samsync.h"
 
-#if defined(HAVE_ADS) && defined(ENCTYPE_ARCFOUR_HMAC)
+#if defined(HAVE_ADS)
 
 /****************************************************************
 ****************************************************************/
@@ -296,7 +296,7 @@ static NTSTATUS close_keytab(TALLOC_CTX *mem_ctx,
        return NT_STATUS_NOT_SUPPORTED;
 }
 
-#endif /* defined(HAVE_ADS) && defined(ENCTYPE_ARCFOUR_HMAC) */
+#endif /* defined(HAVE_ADS) */
 
 const struct samsync_ops libnet_samsync_keytab_ops = {
        .startup                = init_keytab,
diff --git a/source3/libnet/libnet_samsync_keytab.c.orig b/source3/libnet/libnet_samsync_keytab.c.orig
new file mode 100644 (file)
index 0000000..1349223
--- /dev/null
@@ -0,0 +1,305 @@
+/*
+   Unix SMB/CIFS implementation.
+   dump the remote SAM using rpc samsync operations
+
+   Copyright (C) Guenther Deschner 2008.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "smb_krb5.h"
+#include "ads.h"
+#include "libnet/libnet_keytab.h"
+#include "libnet/libnet_samsync.h"
+
+#if defined(HAVE_ADS) && defined(ENCTYPE_ARCFOUR_HMAC)
+
+/****************************************************************
+****************************************************************/
+
+static NTSTATUS keytab_ad_connect(TALLOC_CTX *mem_ctx,
+                                 const char *domain_name,
+                                 const char *username,
+                                 const char *password,
+                                 struct libnet_keytab_context *ctx)
+{
+       NTSTATUS status;
+       ADS_STATUS ad_status;
+       ADS_STRUCT *ads;
+       struct netr_DsRGetDCNameInfo *info = NULL;
+       const char *dc;
+
+       status = dsgetdcname(mem_ctx, NULL, domain_name, NULL, NULL, 0, &info);
+       if (!NT_STATUS_IS_OK(status)) {
+               return status;
+       }
+
+       dc = strip_hostname(info->dc_unc);
+
+       ads = ads_init(NULL, domain_name, dc);
+       NT_STATUS_HAVE_NO_MEMORY(ads);
+
+       if (getenv(KRB5_ENV_CCNAME) == NULL) {
+               setenv(KRB5_ENV_CCNAME, "MEMORY:libnet_samsync_keytab", 1);
+       }
+
+       ads->auth.user_name = SMB_STRDUP(username);
+       ads->auth.password = SMB_STRDUP(password);
+
+       ad_status = ads_connect_user_creds(ads);
+       if (!ADS_ERR_OK(ad_status)) {
+               return NT_STATUS_UNSUCCESSFUL;
+       }
+
+       ctx->ads = ads;
+
+       ctx->dns_domain_name = talloc_strdup_upper(mem_ctx, ads->config.realm);
+       NT_STATUS_HAVE_NO_MEMORY(ctx->dns_domain_name);
+
+       return NT_STATUS_OK;
+}
+
+/****************************************************************
+****************************************************************/
+
+static NTSTATUS fetch_sam_entry_keytab(TALLOC_CTX *mem_ctx,
+                                      enum netr_SamDatabaseID database_id,
+                                      uint32_t rid,
+                                      struct netr_DELTA_USER *r,
+                                      struct libnet_keytab_context *ctx)
+{
+       NTSTATUS status;
+       uint32_t kvno = 0;
+       DATA_BLOB blob;
+
+       if (memcmp(r->ntpassword.hash, ctx->zero_buf, 16) == 0) {
+               return NT_STATUS_OK;
+       }
+
+       kvno = ads_get_kvno(ctx->ads, r->account_name.string);
+       blob = data_blob_const(r->ntpassword.hash, 16);
+
+       status = libnet_keytab_add_to_keytab_entries(mem_ctx, ctx,
+                                                    kvno,
+                                                    r->account_name.string,
+                                                    NULL,
+                                                    ENCTYPE_ARCFOUR_HMAC,
+                                                    blob);
+       if (!NT_STATUS_IS_OK(status)) {
+               return status;
+       }
+
+       return NT_STATUS_OK;
+}
+
+/****************************************************************
+****************************************************************/
+
+static NTSTATUS init_keytab(TALLOC_CTX *mem_ctx,
+                           struct samsync_context *ctx,
+                           enum netr_SamDatabaseID database_id,
+                           uint64_t *sequence_num)
+{
+       krb5_error_code ret = 0;
+       NTSTATUS status;
+       struct libnet_keytab_context *keytab_ctx = NULL;
+       struct libnet_keytab_entry *entry;
+       uint64_t old_sequence_num = 0;
+       const char *principal = NULL;
+
+       ret = libnet_keytab_init(mem_ctx, ctx->output_filename, &keytab_ctx);
+       if (ret) {
+               return krb5_to_nt_status(ret);
+       }
+
+       keytab_ctx->clean_old_entries = ctx->clean_old_entries;
+       ctx->private_data = keytab_ctx;
+
+       status = keytab_ad_connect(mem_ctx,
+                                  ctx->domain_name,
+                                  ctx->username,
+                                  ctx->password,
+                                  keytab_ctx);
+       if (!NT_STATUS_IS_OK(status)) {
+               TALLOC_FREE(keytab_ctx);
+               return status;
+       }
+
+       principal = talloc_asprintf(mem_ctx, "SEQUENCE_NUM@%s",
+                                   keytab_ctx->dns_domain_name);
+       NT_STATUS_HAVE_NO_MEMORY(principal);
+
+       entry = libnet_keytab_search(keytab_ctx, principal, 0, ENCTYPE_NULL,
+                                    mem_ctx);
+       if (entry && (entry->password.length == 8)) {
+               old_sequence_num = BVAL(entry->password.data, 0);
+       }
+
+       if (sequence_num) {
+               *sequence_num = old_sequence_num;
+       }
+
+       return status;
+}
+
+/****************************************************************
+****************************************************************/
+
+static NTSTATUS fetch_sam_entries_keytab(TALLOC_CTX *mem_ctx,
+                                        enum netr_SamDatabaseID database_id,
+                                        struct netr_DELTA_ENUM_ARRAY *r,
+                                        uint64_t *sequence_num,
+                                        struct samsync_context *ctx)
+{
+       struct libnet_keytab_context *keytab_ctx =
+               (struct libnet_keytab_context *)ctx->private_data;
+
+       NTSTATUS status = NT_STATUS_OK;
+       int i;
+
+       for (i = 0; i < r->num_deltas; i++) {
+
+               switch (r->delta_enum[i].delta_type) {
+               case NETR_DELTA_USER:
+                       break;
+               case NETR_DELTA_DOMAIN:
+                       if (sequence_num) {
+                               *sequence_num =
+                                       r->delta_enum[i].delta_union.domain->sequence_num;
+                       }
+                       continue;
+               case NETR_DELTA_MODIFY_COUNT:
+                       if (sequence_num) {
+                               *sequence_num =
+                                       *r->delta_enum[i].delta_union.modified_count;
+                       }
+                       continue;
+               default:
+                       continue;
+               }
+
+               status = fetch_sam_entry_keytab(mem_ctx, database_id,
+                                               r->delta_enum[i].delta_id_union.rid,
+                                               r->delta_enum[i].delta_union.user,
+                                               keytab_ctx);
+               if (!NT_STATUS_IS_OK(status)) {
+                       goto out;
+               }
+       }
+ out:
+       return status;
+}
+
+/****************************************************************
+****************************************************************/
+
+static NTSTATUS close_keytab(TALLOC_CTX *mem_ctx,
+                            struct samsync_context *ctx,
+                            enum netr_SamDatabaseID database_id,
+                            uint64_t sequence_num)
+{
+       struct libnet_keytab_context *keytab_ctx =
+               (struct libnet_keytab_context *)ctx->private_data;
+       krb5_error_code ret;
+       NTSTATUS status;
+       struct libnet_keytab_entry *entry;
+       uint64_t old_sequence_num = 0;
+       const char *principal = NULL;
+
+       principal = talloc_asprintf(mem_ctx, "SEQUENCE_NUM@%s",
+                                   keytab_ctx->dns_domain_name);
+       NT_STATUS_HAVE_NO_MEMORY(principal);
+
+
+       entry = libnet_keytab_search(keytab_ctx, principal, 0, ENCTYPE_NULL,
+                                    mem_ctx);
+       if (entry && (entry->password.length == 8)) {
+               old_sequence_num = BVAL(entry->password.data, 0);
+       }
+
+
+       if (sequence_num > old_sequence_num) {
+               DATA_BLOB blob;
+               blob = data_blob_talloc_zero(mem_ctx, 8);
+               SBVAL(blob.data, 0, sequence_num);
+
+               status = libnet_keytab_add_to_keytab_entries(mem_ctx, keytab_ctx,
+                                                            0,
+                                                            "SEQUENCE_NUM",
+                                                            NULL,
+                                                            ENCTYPE_NULL,
+                                                            blob);
+               if (!NT_STATUS_IS_OK(status)) {
+                       goto done;
+               }
+       }
+
+       ret = libnet_keytab_add(keytab_ctx);
+       if (ret) {
+               status = krb5_to_nt_status(ret);
+               ctx->error_message = talloc_asprintf(ctx,
+                       "Failed to add entries to keytab %s: %s",
+                       keytab_ctx->keytab_name, error_message(ret));
+               TALLOC_FREE(keytab_ctx);
+               return status;
+       }
+
+       ctx->result_message = talloc_asprintf(ctx,
+               "Vampired %d accounts to keytab %s",
+               keytab_ctx->count,
+               keytab_ctx->keytab_name);
+
+       status = NT_STATUS_OK;
+
+ done:
+       TALLOC_FREE(keytab_ctx);
+
+       return status;
+}
+
+#else
+
+static NTSTATUS init_keytab(TALLOC_CTX *mem_ctx,
+                           struct samsync_context *ctx,
+                           enum netr_SamDatabaseID database_id,
+                           uint64_t *sequence_num)
+{
+       return NT_STATUS_NOT_SUPPORTED;
+}
+
+static NTSTATUS fetch_sam_entries_keytab(TALLOC_CTX *mem_ctx,
+                                        enum netr_SamDatabaseID database_id,
+                                        struct netr_DELTA_ENUM_ARRAY *r,
+                                        uint64_t *sequence_num,
+                                        struct samsync_context *ctx)
+{
+       return NT_STATUS_NOT_SUPPORTED;
+}
+
+static NTSTATUS close_keytab(TALLOC_CTX *mem_ctx,
+                            struct samsync_context *ctx,
+                            enum netr_SamDatabaseID database_id,
+                            uint64_t sequence_num)
+{
+       return NT_STATUS_NOT_SUPPORTED;
+}
+
+#endif /* defined(HAVE_ADS) && defined(ENCTYPE_ARCFOUR_HMAC) */
+
+const struct samsync_ops libnet_samsync_keytab_ops = {
+       .startup                = init_keytab,
+       .process_objects        = fetch_sam_entries_keytab,
+       .finish                 = close_keytab
+};
index 4eb43cfff1d1f005241ef4a5f6999d82e0a2c633..344fdacb969c02d6a9e14cb2f7cd15417df9281e 100644 (file)
@@ -964,9 +964,7 @@ int cli_krb5_get_ticket(TALLOC_CTX *mem_ctx,
        krb5_ccache ccdef = NULL;
        krb5_auth_context auth_context = NULL;
        krb5_enctype enc_types[] = {
-#ifdef ENCTYPE_ARCFOUR_HMAC
                ENCTYPE_ARCFOUR_HMAC,
-#endif
                ENCTYPE_DES_CBC_MD5,
                ENCTYPE_DES_CBC_CRC,
                ENCTYPE_NULL};