diff options
Diffstat (limited to 'source3')
-rw-r--r-- | source3/configure.in | 20 | ||||
-rw-r--r-- | source3/include/smb_krb5.h | 2 | ||||
-rw-r--r-- | source3/libads/kerberos_keytab.c | 9 | ||||
-rw-r--r-- | source3/libads/kerberos_verify.c | 2 | ||||
-rw-r--r-- | source3/libnet/libnet_dssync_keytab.c | 4 | ||||
-rw-r--r-- | source3/libnet/libnet_join.c | 6 | ||||
-rw-r--r-- | source3/libnet/libnet_join.c.orig | 2187 | ||||
-rw-r--r-- | source3/libnet/libnet_samsync_keytab.c | 4 | ||||
-rw-r--r-- | source3/libnet/libnet_samsync_keytab.c.orig | 305 | ||||
-rw-r--r-- | source3/libsmb/clikrb5.c | 2 |
10 files changed, 2520 insertions, 21 deletions
diff --git a/source3/configure.in b/source3/configure.in index 6b736712d8..b4564eec08 100644 --- a/source3/configure.in +++ b/source3/configure.in @@ -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 diff --git a/source3/include/smb_krb5.h b/source3/include/smb_krb5.h index 35cc047868..0a6ba79038 100644 --- a/source3/include/smb_krb5.h +++ b/source3/include/smb_krb5.h @@ -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 diff --git a/source3/libads/kerberos_keytab.c b/source3/libads/kerberos_keytab.c index a874901caf..386ce83ea7 100644 --- a/source3/libads/kerberos_keytab.c +++ b/source3/libads/kerberos_keytab.c @@ -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) { diff --git a/source3/libads/kerberos_verify.c b/source3/libads/kerberos_verify.c index 887dac0502..7eda7fd8dd 100644 --- a/source3/libads/kerberos_verify.c +++ b/source3/libads/kerberos_verify.c @@ -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 diff --git a/source3/libnet/libnet_dssync_keytab.c b/source3/libnet/libnet_dssync_keytab.c index b0c745d110..96fb9c994b 100644 --- a/source3/libnet/libnet_dssync_keytab.c +++ b/source3/libnet/libnet_dssync_keytab.c @@ -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, diff --git a/source3/libnet/libnet_join.c b/source3/libnet/libnet_join.c index c710f9e1b1..7d83dc3253 100644 --- a/source3/libnet/libnet_join.c +++ b/source3/libnet/libnet_join.c @@ -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 index 0000000000..c710f9e1b1 --- /dev/null +++ b/source3/libnet/libnet_join.c.orig @@ -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; +} diff --git a/source3/libnet/libnet_samsync_keytab.c b/source3/libnet/libnet_samsync_keytab.c index 134922392c..faba1e73ad 100644 --- a/source3/libnet/libnet_samsync_keytab.c +++ b/source3/libnet/libnet_samsync_keytab.c @@ -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 index 0000000000..134922392c --- /dev/null +++ b/source3/libnet/libnet_samsync_keytab.c.orig @@ -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 +}; diff --git a/source3/libsmb/clikrb5.c b/source3/libsmb/clikrb5.c index 4eb43cfff1..344fdacb96 100644 --- a/source3/libsmb/clikrb5.c +++ b/source3/libsmb/clikrb5.c @@ -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}; |