format-subunit: Hide reason if it is None.
[Samba/eduardoll.git] / source3 / libnet / libnet_join.c
blob736a9191900c4afd570d1655e2a3bbaf3edf5e5f
1 /*
2 * Unix SMB/CIFS implementation.
3 * libnet Join Support
4 * Copyright (C) Gerald (Jerry) Carter 2006
5 * Copyright (C) Guenther Deschner 2007-2008
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 3 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, see <http://www.gnu.org/licenses/>.
21 #include "includes.h"
22 #include "libnet/libnet.h"
23 #include "libcli/auth/libcli_auth.h"
24 #include "../librpc/gen_ndr/cli_samr.h"
25 #include "../librpc/gen_ndr/cli_lsa.h"
27 /****************************************************************
28 ****************************************************************/
30 #define LIBNET_JOIN_DUMP_CTX(ctx, r, f) \
31 do { \
32 char *str = NULL; \
33 str = NDR_PRINT_FUNCTION_STRING(ctx, libnet_JoinCtx, f, r); \
34 DEBUG(1,("libnet_Join:\n%s", str)); \
35 TALLOC_FREE(str); \
36 } while (0)
38 #define LIBNET_JOIN_IN_DUMP_CTX(ctx, r) \
39 LIBNET_JOIN_DUMP_CTX(ctx, r, NDR_IN | NDR_SET_VALUES)
40 #define LIBNET_JOIN_OUT_DUMP_CTX(ctx, r) \
41 LIBNET_JOIN_DUMP_CTX(ctx, r, NDR_OUT)
43 #define LIBNET_UNJOIN_DUMP_CTX(ctx, r, f) \
44 do { \
45 char *str = NULL; \
46 str = NDR_PRINT_FUNCTION_STRING(ctx, libnet_UnjoinCtx, f, r); \
47 DEBUG(1,("libnet_Unjoin:\n%s", str)); \
48 TALLOC_FREE(str); \
49 } while (0)
51 #define LIBNET_UNJOIN_IN_DUMP_CTX(ctx, r) \
52 LIBNET_UNJOIN_DUMP_CTX(ctx, r, NDR_IN | NDR_SET_VALUES)
53 #define LIBNET_UNJOIN_OUT_DUMP_CTX(ctx, r) \
54 LIBNET_UNJOIN_DUMP_CTX(ctx, r, NDR_OUT)
56 /****************************************************************
57 ****************************************************************/
59 static void libnet_join_set_error_string(TALLOC_CTX *mem_ctx,
60 struct libnet_JoinCtx *r,
61 const char *format, ...)
63 va_list args;
65 if (r->out.error_string) {
66 return;
69 va_start(args, format);
70 r->out.error_string = talloc_vasprintf(mem_ctx, format, args);
71 va_end(args);
74 /****************************************************************
75 ****************************************************************/
77 static void libnet_unjoin_set_error_string(TALLOC_CTX *mem_ctx,
78 struct libnet_UnjoinCtx *r,
79 const char *format, ...)
81 va_list args;
83 if (r->out.error_string) {
84 return;
87 va_start(args, format);
88 r->out.error_string = talloc_vasprintf(mem_ctx, format, args);
89 va_end(args);
92 #ifdef WITH_ADS
94 /****************************************************************
95 ****************************************************************/
97 static ADS_STATUS libnet_connect_ads(const char *dns_domain_name,
98 const char *netbios_domain_name,
99 const char *dc_name,
100 const char *user_name,
101 const char *password,
102 ADS_STRUCT **ads)
104 ADS_STATUS status;
105 ADS_STRUCT *my_ads = NULL;
107 my_ads = ads_init(dns_domain_name,
108 netbios_domain_name,
109 dc_name);
110 if (!my_ads) {
111 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
114 if (user_name) {
115 SAFE_FREE(my_ads->auth.user_name);
116 my_ads->auth.user_name = SMB_STRDUP(user_name);
119 if (password) {
120 SAFE_FREE(my_ads->auth.password);
121 my_ads->auth.password = SMB_STRDUP(password);
124 status = ads_connect_user_creds(my_ads);
125 if (!ADS_ERR_OK(status)) {
126 ads_destroy(&my_ads);
127 return status;
130 *ads = my_ads;
131 return ADS_SUCCESS;
134 /****************************************************************
135 ****************************************************************/
137 static ADS_STATUS libnet_join_connect_ads(TALLOC_CTX *mem_ctx,
138 struct libnet_JoinCtx *r)
140 ADS_STATUS status;
142 status = libnet_connect_ads(r->out.dns_domain_name,
143 r->out.netbios_domain_name,
144 r->in.dc_name,
145 r->in.admin_account,
146 r->in.admin_password,
147 &r->in.ads);
148 if (!ADS_ERR_OK(status)) {
149 libnet_join_set_error_string(mem_ctx, r,
150 "failed to connect to AD: %s",
151 ads_errstr(status));
152 return status;
155 if (!r->out.netbios_domain_name) {
156 r->out.netbios_domain_name = talloc_strdup(mem_ctx,
157 r->in.ads->server.workgroup);
158 ADS_ERROR_HAVE_NO_MEMORY(r->out.netbios_domain_name);
161 if (!r->out.dns_domain_name) {
162 r->out.dns_domain_name = talloc_strdup(mem_ctx,
163 r->in.ads->config.realm);
164 ADS_ERROR_HAVE_NO_MEMORY(r->out.dns_domain_name);
167 r->out.domain_is_ad = true;
169 return ADS_SUCCESS;
172 /****************************************************************
173 ****************************************************************/
175 static ADS_STATUS libnet_unjoin_connect_ads(TALLOC_CTX *mem_ctx,
176 struct libnet_UnjoinCtx *r)
178 ADS_STATUS status;
180 status = libnet_connect_ads(r->in.domain_name,
181 r->in.domain_name,
182 r->in.dc_name,
183 r->in.admin_account,
184 r->in.admin_password,
185 &r->in.ads);
186 if (!ADS_ERR_OK(status)) {
187 libnet_unjoin_set_error_string(mem_ctx, r,
188 "failed to connect to AD: %s",
189 ads_errstr(status));
192 return status;
195 /****************************************************************
196 join a domain using ADS (LDAP mods)
197 ****************************************************************/
199 static ADS_STATUS libnet_join_precreate_machine_acct(TALLOC_CTX *mem_ctx,
200 struct libnet_JoinCtx *r)
202 ADS_STATUS status;
203 LDAPMessage *res = NULL;
204 const char *attrs[] = { "dn", NULL };
205 bool moved = false;
207 status = ads_check_ou_dn(mem_ctx, r->in.ads, &r->in.account_ou);
208 if (!ADS_ERR_OK(status)) {
209 return status;
212 status = ads_search_dn(r->in.ads, &res, r->in.account_ou, attrs);
213 if (!ADS_ERR_OK(status)) {
214 return status;
217 if (ads_count_replies(r->in.ads, res) != 1) {
218 ads_msgfree(r->in.ads, res);
219 return ADS_ERROR_LDAP(LDAP_NO_SUCH_OBJECT);
222 ads_msgfree(r->in.ads, res);
224 /* Attempt to create the machine account and bail if this fails.
225 Assume that the admin wants exactly what they requested */
227 status = ads_create_machine_acct(r->in.ads,
228 r->in.machine_name,
229 r->in.account_ou);
231 if (ADS_ERR_OK(status)) {
232 DEBUG(1,("machine account creation created\n"));
233 return status;
234 } else if ((status.error_type == ENUM_ADS_ERROR_LDAP) &&
235 (status.err.rc == LDAP_ALREADY_EXISTS)) {
236 status = ADS_SUCCESS;
239 if (!ADS_ERR_OK(status)) {
240 DEBUG(1,("machine account creation failed\n"));
241 return status;
244 status = ads_move_machine_acct(r->in.ads,
245 r->in.machine_name,
246 r->in.account_ou,
247 &moved);
248 if (!ADS_ERR_OK(status)) {
249 DEBUG(1,("failure to locate/move pre-existing "
250 "machine account\n"));
251 return status;
254 DEBUG(1,("The machine account %s the specified OU.\n",
255 moved ? "was moved into" : "already exists in"));
257 return status;
260 /****************************************************************
261 ****************************************************************/
263 static ADS_STATUS libnet_unjoin_remove_machine_acct(TALLOC_CTX *mem_ctx,
264 struct libnet_UnjoinCtx *r)
266 ADS_STATUS status;
268 if (!r->in.ads) {
269 status = libnet_unjoin_connect_ads(mem_ctx, r);
270 if (!ADS_ERR_OK(status)) {
271 libnet_unjoin_set_error_string(mem_ctx, r,
272 "failed to connect to AD: %s",
273 ads_errstr(status));
274 return status;
278 status = ads_leave_realm(r->in.ads, r->in.machine_name);
279 if (!ADS_ERR_OK(status)) {
280 libnet_unjoin_set_error_string(mem_ctx, r,
281 "failed to leave realm: %s",
282 ads_errstr(status));
283 return status;
286 return ADS_SUCCESS;
289 /****************************************************************
290 ****************************************************************/
292 static ADS_STATUS libnet_join_find_machine_acct(TALLOC_CTX *mem_ctx,
293 struct libnet_JoinCtx *r)
295 ADS_STATUS status;
296 LDAPMessage *res = NULL;
297 char *dn = NULL;
299 if (!r->in.machine_name) {
300 return ADS_ERROR(LDAP_NO_MEMORY);
303 status = ads_find_machine_acct(r->in.ads,
304 &res,
305 r->in.machine_name);
306 if (!ADS_ERR_OK(status)) {
307 return status;
310 if (ads_count_replies(r->in.ads, res) != 1) {
311 status = ADS_ERROR_LDAP(LDAP_NO_MEMORY);
312 goto done;
315 dn = ads_get_dn(r->in.ads, mem_ctx, res);
316 if (!dn) {
317 status = ADS_ERROR_LDAP(LDAP_NO_MEMORY);
318 goto done;
321 r->out.dn = talloc_strdup(mem_ctx, dn);
322 if (!r->out.dn) {
323 status = ADS_ERROR_LDAP(LDAP_NO_MEMORY);
324 goto done;
327 done:
328 ads_msgfree(r->in.ads, res);
329 TALLOC_FREE(dn);
331 return status;
334 /****************************************************************
335 Set a machines dNSHostName and servicePrincipalName attributes
336 ****************************************************************/
338 static ADS_STATUS libnet_join_set_machine_spn(TALLOC_CTX *mem_ctx,
339 struct libnet_JoinCtx *r)
341 ADS_STATUS status;
342 ADS_MODLIST mods;
343 fstring my_fqdn;
344 const char *spn_array[3] = {NULL, NULL, NULL};
345 char *spn = NULL;
347 /* Find our DN */
349 status = libnet_join_find_machine_acct(mem_ctx, r);
350 if (!ADS_ERR_OK(status)) {
351 return status;
354 /* Windows only creates HOST/shortname & HOST/fqdn. */
356 spn = talloc_asprintf(mem_ctx, "HOST/%s", r->in.machine_name);
357 if (!spn) {
358 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
360 strupper_m(spn);
361 spn_array[0] = spn;
363 if (!name_to_fqdn(my_fqdn, r->in.machine_name)
364 || (strchr(my_fqdn, '.') == NULL)) {
365 fstr_sprintf(my_fqdn, "%s.%s", r->in.machine_name,
366 r->out.dns_domain_name);
369 strlower_m(my_fqdn);
371 if (!strequal(my_fqdn, r->in.machine_name)) {
372 spn = talloc_asprintf(mem_ctx, "HOST/%s", my_fqdn);
373 if (!spn) {
374 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
376 spn_array[1] = spn;
379 mods = ads_init_mods(mem_ctx);
380 if (!mods) {
381 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
384 /* fields of primary importance */
386 status = ads_mod_str(mem_ctx, &mods, "dNSHostName", my_fqdn);
387 if (!ADS_ERR_OK(status)) {
388 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
391 status = ads_mod_strlist(mem_ctx, &mods, "servicePrincipalName",
392 spn_array);
393 if (!ADS_ERR_OK(status)) {
394 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
397 return ads_gen_mod(r->in.ads, r->out.dn, mods);
400 /****************************************************************
401 ****************************************************************/
403 static ADS_STATUS libnet_join_set_machine_upn(TALLOC_CTX *mem_ctx,
404 struct libnet_JoinCtx *r)
406 ADS_STATUS status;
407 ADS_MODLIST mods;
409 if (!r->in.create_upn) {
410 return ADS_SUCCESS;
413 /* Find our DN */
415 status = libnet_join_find_machine_acct(mem_ctx, r);
416 if (!ADS_ERR_OK(status)) {
417 return status;
420 if (!r->in.upn) {
421 r->in.upn = talloc_asprintf(mem_ctx,
422 "host/%s@%s",
423 r->in.machine_name,
424 r->out.dns_domain_name);
425 if (!r->in.upn) {
426 return ADS_ERROR(LDAP_NO_MEMORY);
430 /* now do the mods */
432 mods = ads_init_mods(mem_ctx);
433 if (!mods) {
434 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
437 /* fields of primary importance */
439 status = ads_mod_str(mem_ctx, &mods, "userPrincipalName", r->in.upn);
440 if (!ADS_ERR_OK(status)) {
441 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
444 return ads_gen_mod(r->in.ads, r->out.dn, mods);
448 /****************************************************************
449 ****************************************************************/
451 static ADS_STATUS libnet_join_set_os_attributes(TALLOC_CTX *mem_ctx,
452 struct libnet_JoinCtx *r)
454 ADS_STATUS status;
455 ADS_MODLIST mods;
456 char *os_sp = NULL;
458 if (!r->in.os_name || !r->in.os_version ) {
459 return ADS_SUCCESS;
462 /* Find our DN */
464 status = libnet_join_find_machine_acct(mem_ctx, r);
465 if (!ADS_ERR_OK(status)) {
466 return status;
469 /* now do the mods */
471 mods = ads_init_mods(mem_ctx);
472 if (!mods) {
473 return ADS_ERROR(LDAP_NO_MEMORY);
476 os_sp = talloc_asprintf(mem_ctx, "Samba %s", samba_version_string());
477 if (!os_sp) {
478 return ADS_ERROR(LDAP_NO_MEMORY);
481 /* fields of primary importance */
483 status = ads_mod_str(mem_ctx, &mods, "operatingSystem",
484 r->in.os_name);
485 if (!ADS_ERR_OK(status)) {
486 return status;
489 status = ads_mod_str(mem_ctx, &mods, "operatingSystemVersion",
490 r->in.os_version);
491 if (!ADS_ERR_OK(status)) {
492 return status;
495 status = ads_mod_str(mem_ctx, &mods, "operatingSystemServicePack",
496 os_sp);
497 if (!ADS_ERR_OK(status)) {
498 return status;
501 return ads_gen_mod(r->in.ads, r->out.dn, mods);
504 /****************************************************************
505 ****************************************************************/
507 static bool libnet_join_create_keytab(TALLOC_CTX *mem_ctx,
508 struct libnet_JoinCtx *r)
510 if (!USE_SYSTEM_KEYTAB) {
511 return true;
514 if (ads_keytab_create_default(r->in.ads) != 0) {
515 return false;
518 return true;
521 /****************************************************************
522 ****************************************************************/
524 static bool libnet_join_derive_salting_principal(TALLOC_CTX *mem_ctx,
525 struct libnet_JoinCtx *r)
527 uint32_t domain_func;
528 ADS_STATUS status;
529 const char *salt = NULL;
530 char *std_salt = NULL;
532 status = ads_domain_func_level(r->in.ads, &domain_func);
533 if (!ADS_ERR_OK(status)) {
534 libnet_join_set_error_string(mem_ctx, r,
535 "failed to determine domain functional level: %s",
536 ads_errstr(status));
537 return false;
540 /* go ahead and setup the default salt */
542 std_salt = kerberos_standard_des_salt();
543 if (!std_salt) {
544 libnet_join_set_error_string(mem_ctx, r,
545 "failed to obtain standard DES salt");
546 return false;
549 salt = talloc_strdup(mem_ctx, std_salt);
550 if (!salt) {
551 return false;
554 SAFE_FREE(std_salt);
556 /* if it's a Windows functional domain, we have to look for the UPN */
558 if (domain_func == DS_DOMAIN_FUNCTION_2000) {
559 char *upn;
561 upn = ads_get_upn(r->in.ads, mem_ctx,
562 r->in.machine_name);
563 if (upn) {
564 salt = talloc_strdup(mem_ctx, upn);
565 if (!salt) {
566 return false;
571 return kerberos_secrets_store_des_salt(salt);
574 /****************************************************************
575 ****************************************************************/
577 static ADS_STATUS libnet_join_post_processing_ads(TALLOC_CTX *mem_ctx,
578 struct libnet_JoinCtx *r)
580 ADS_STATUS status;
582 if (!r->in.ads) {
583 status = libnet_join_connect_ads(mem_ctx, r);
584 if (!ADS_ERR_OK(status)) {
585 return status;
589 status = libnet_join_set_machine_spn(mem_ctx, r);
590 if (!ADS_ERR_OK(status)) {
591 libnet_join_set_error_string(mem_ctx, r,
592 "failed to set machine spn: %s",
593 ads_errstr(status));
594 return status;
597 status = libnet_join_set_os_attributes(mem_ctx, r);
598 if (!ADS_ERR_OK(status)) {
599 libnet_join_set_error_string(mem_ctx, r,
600 "failed to set machine os attributes: %s",
601 ads_errstr(status));
602 return status;
605 status = libnet_join_set_machine_upn(mem_ctx, r);
606 if (!ADS_ERR_OK(status)) {
607 libnet_join_set_error_string(mem_ctx, r,
608 "failed to set machine upn: %s",
609 ads_errstr(status));
610 return status;
613 if (!libnet_join_derive_salting_principal(mem_ctx, r)) {
614 return ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL);
617 if (!libnet_join_create_keytab(mem_ctx, r)) {
618 libnet_join_set_error_string(mem_ctx, r,
619 "failed to create kerberos keytab");
620 return ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL);
623 return ADS_SUCCESS;
625 #endif /* WITH_ADS */
627 /****************************************************************
628 Store the machine password and domain SID
629 ****************************************************************/
631 static bool libnet_join_joindomain_store_secrets(TALLOC_CTX *mem_ctx,
632 struct libnet_JoinCtx *r)
634 if (!secrets_store_domain_sid(r->out.netbios_domain_name,
635 r->out.domain_sid))
637 DEBUG(1,("Failed to save domain sid\n"));
638 return false;
641 if (!secrets_store_machine_password(r->in.machine_password,
642 r->out.netbios_domain_name,
643 r->in.secure_channel_type))
645 DEBUG(1,("Failed to save machine password\n"));
646 return false;
649 return true;
652 /****************************************************************
653 Connect dc's IPC$ share
654 ****************************************************************/
656 static NTSTATUS libnet_join_connect_dc_ipc(const char *dc,
657 const char *user,
658 const char *pass,
659 bool use_kerberos,
660 struct cli_state **cli)
662 int flags = 0;
664 if (use_kerberos) {
665 flags |= CLI_FULL_CONNECTION_USE_KERBEROS;
668 if (use_kerberos && pass) {
669 flags |= CLI_FULL_CONNECTION_FALLBACK_AFTER_KERBEROS;
672 return cli_full_connection(cli, NULL,
674 NULL, 0,
675 "IPC$", "IPC",
676 user,
677 NULL,
678 pass,
679 flags,
680 Undefined, NULL);
683 /****************************************************************
684 Lookup domain dc's info
685 ****************************************************************/
687 static NTSTATUS libnet_join_lookup_dc_rpc(TALLOC_CTX *mem_ctx,
688 struct libnet_JoinCtx *r,
689 struct cli_state **cli)
691 struct rpc_pipe_client *pipe_hnd = NULL;
692 struct policy_handle lsa_pol;
693 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
694 union lsa_PolicyInformation *info = NULL;
696 status = libnet_join_connect_dc_ipc(r->in.dc_name,
697 r->in.admin_account,
698 r->in.admin_password,
699 r->in.use_kerberos,
700 cli);
701 if (!NT_STATUS_IS_OK(status)) {
702 goto done;
705 status = cli_rpc_pipe_open_noauth(*cli, &ndr_table_lsarpc.syntax_id,
706 &pipe_hnd);
707 if (!NT_STATUS_IS_OK(status)) {
708 DEBUG(0,("Error connecting to LSA pipe. Error was %s\n",
709 nt_errstr(status)));
710 goto done;
713 status = rpccli_lsa_open_policy(pipe_hnd, mem_ctx, true,
714 SEC_FLAG_MAXIMUM_ALLOWED, &lsa_pol);
715 if (!NT_STATUS_IS_OK(status)) {
716 goto done;
719 status = rpccli_lsa_QueryInfoPolicy2(pipe_hnd, mem_ctx,
720 &lsa_pol,
721 LSA_POLICY_INFO_DNS,
722 &info);
723 if (NT_STATUS_IS_OK(status)) {
724 r->out.domain_is_ad = true;
725 r->out.netbios_domain_name = info->dns.name.string;
726 r->out.dns_domain_name = info->dns.dns_domain.string;
727 r->out.forest_name = info->dns.dns_forest.string;
728 r->out.domain_sid = sid_dup_talloc(mem_ctx, info->dns.sid);
729 NT_STATUS_HAVE_NO_MEMORY(r->out.domain_sid);
732 if (!NT_STATUS_IS_OK(status)) {
733 status = rpccli_lsa_QueryInfoPolicy(pipe_hnd, mem_ctx,
734 &lsa_pol,
735 LSA_POLICY_INFO_ACCOUNT_DOMAIN,
736 &info);
737 if (!NT_STATUS_IS_OK(status)) {
738 goto done;
741 r->out.netbios_domain_name = info->account_domain.name.string;
742 r->out.domain_sid = sid_dup_talloc(mem_ctx, info->account_domain.sid);
743 NT_STATUS_HAVE_NO_MEMORY(r->out.domain_sid);
746 rpccli_lsa_Close(pipe_hnd, mem_ctx, &lsa_pol);
747 TALLOC_FREE(pipe_hnd);
749 done:
750 return status;
753 /****************************************************************
754 Do the domain join unsecure
755 ****************************************************************/
757 static NTSTATUS libnet_join_joindomain_rpc_unsecure(TALLOC_CTX *mem_ctx,
758 struct libnet_JoinCtx *r,
759 struct cli_state *cli)
761 struct rpc_pipe_client *pipe_hnd = NULL;
762 unsigned char orig_trust_passwd_hash[16];
763 unsigned char new_trust_passwd_hash[16];
764 fstring trust_passwd;
765 NTSTATUS status;
767 status = cli_rpc_pipe_open_noauth(cli, &ndr_table_netlogon.syntax_id,
768 &pipe_hnd);
769 if (!NT_STATUS_IS_OK(status)) {
770 return status;
773 if (!r->in.machine_password) {
774 r->in.machine_password = generate_random_str(mem_ctx, DEFAULT_TRUST_ACCOUNT_PASSWORD_LENGTH);
775 NT_STATUS_HAVE_NO_MEMORY(r->in.machine_password);
778 E_md4hash(r->in.machine_password, new_trust_passwd_hash);
780 /* according to WKSSVC_JOIN_FLAGS_MACHINE_PWD_PASSED */
781 fstrcpy(trust_passwd, r->in.admin_password);
782 strlower_m(trust_passwd);
785 * Machine names can be 15 characters, but the max length on
786 * a password is 14. --jerry
789 trust_passwd[14] = '\0';
791 E_md4hash(trust_passwd, orig_trust_passwd_hash);
793 status = rpccli_netlogon_set_trust_password(pipe_hnd, mem_ctx,
794 r->in.machine_name,
795 orig_trust_passwd_hash,
796 r->in.machine_password,
797 new_trust_passwd_hash,
798 r->in.secure_channel_type);
800 return status;
803 /****************************************************************
804 Do the domain join
805 ****************************************************************/
807 static NTSTATUS libnet_join_joindomain_rpc(TALLOC_CTX *mem_ctx,
808 struct libnet_JoinCtx *r,
809 struct cli_state *cli)
811 struct rpc_pipe_client *pipe_hnd = NULL;
812 struct policy_handle sam_pol, domain_pol, user_pol;
813 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
814 char *acct_name;
815 struct lsa_String lsa_acct_name;
816 uint32_t user_rid;
817 uint32_t acct_flags = ACB_WSTRUST;
818 struct samr_Ids user_rids;
819 struct samr_Ids name_types;
820 union samr_UserInfo user_info;
822 struct samr_CryptPassword crypt_pwd;
823 struct samr_CryptPasswordEx crypt_pwd_ex;
825 ZERO_STRUCT(sam_pol);
826 ZERO_STRUCT(domain_pol);
827 ZERO_STRUCT(user_pol);
829 switch (r->in.secure_channel_type) {
830 case SEC_CHAN_WKSTA:
831 acct_flags = ACB_WSTRUST;
832 break;
833 case SEC_CHAN_BDC:
834 acct_flags = ACB_SVRTRUST;
835 break;
836 default:
837 return NT_STATUS_INVALID_PARAMETER;
840 if (!r->in.machine_password) {
841 r->in.machine_password = generate_random_str(mem_ctx, DEFAULT_TRUST_ACCOUNT_PASSWORD_LENGTH);
842 NT_STATUS_HAVE_NO_MEMORY(r->in.machine_password);
845 /* Open the domain */
847 status = cli_rpc_pipe_open_noauth(cli, &ndr_table_samr.syntax_id,
848 &pipe_hnd);
849 if (!NT_STATUS_IS_OK(status)) {
850 DEBUG(0,("Error connecting to SAM pipe. Error was %s\n",
851 nt_errstr(status)));
852 goto done;
855 status = rpccli_samr_Connect2(pipe_hnd, mem_ctx,
856 pipe_hnd->desthost,
857 SAMR_ACCESS_ENUM_DOMAINS
858 | SAMR_ACCESS_LOOKUP_DOMAIN,
859 &sam_pol);
860 if (!NT_STATUS_IS_OK(status)) {
861 goto done;
864 status = rpccli_samr_OpenDomain(pipe_hnd, mem_ctx,
865 &sam_pol,
866 SAMR_DOMAIN_ACCESS_LOOKUP_INFO_1
867 | SAMR_DOMAIN_ACCESS_CREATE_USER
868 | SAMR_DOMAIN_ACCESS_OPEN_ACCOUNT,
869 r->out.domain_sid,
870 &domain_pol);
871 if (!NT_STATUS_IS_OK(status)) {
872 goto done;
875 /* Create domain user */
877 acct_name = talloc_asprintf(mem_ctx, "%s$", r->in.machine_name);
878 strlower_m(acct_name);
880 init_lsa_String(&lsa_acct_name, acct_name);
882 if (r->in.join_flags & WKSSVC_JOIN_FLAGS_ACCOUNT_CREATE) {
883 uint32_t access_desired =
884 SEC_GENERIC_READ | SEC_GENERIC_WRITE | SEC_GENERIC_EXECUTE |
885 SEC_STD_WRITE_DAC | SEC_STD_DELETE |
886 SAMR_USER_ACCESS_SET_PASSWORD |
887 SAMR_USER_ACCESS_GET_ATTRIBUTES |
888 SAMR_USER_ACCESS_SET_ATTRIBUTES;
889 uint32_t access_granted = 0;
891 DEBUG(10,("Creating account with desired access mask: %d\n",
892 access_desired));
894 status = rpccli_samr_CreateUser2(pipe_hnd, mem_ctx,
895 &domain_pol,
896 &lsa_acct_name,
897 acct_flags,
898 access_desired,
899 &user_pol,
900 &access_granted,
901 &user_rid);
902 if (!NT_STATUS_IS_OK(status) &&
903 !NT_STATUS_EQUAL(status, NT_STATUS_USER_EXISTS)) {
905 DEBUG(10,("Creation of workstation account failed: %s\n",
906 nt_errstr(status)));
908 /* If NT_STATUS_ACCESS_DENIED then we have a valid
909 username/password combo but the user does not have
910 administrator access. */
912 if (NT_STATUS_EQUAL(status, NT_STATUS_ACCESS_DENIED)) {
913 libnet_join_set_error_string(mem_ctx, r,
914 "User specified does not have "
915 "administrator privileges");
918 goto done;
921 if (NT_STATUS_EQUAL(status, NT_STATUS_USER_EXISTS)) {
922 if (!(r->in.join_flags &
923 WKSSVC_JOIN_FLAGS_DOMAIN_JOIN_IF_JOINED)) {
924 goto done;
928 /* We *must* do this.... don't ask... */
930 if (NT_STATUS_IS_OK(status)) {
931 rpccli_samr_Close(pipe_hnd, mem_ctx, &user_pol);
935 status = rpccli_samr_LookupNames(pipe_hnd, mem_ctx,
936 &domain_pol,
938 &lsa_acct_name,
939 &user_rids,
940 &name_types);
941 if (!NT_STATUS_IS_OK(status)) {
942 goto done;
945 if (name_types.ids[0] != SID_NAME_USER) {
946 DEBUG(0,("%s is not a user account (type=%d)\n",
947 acct_name, name_types.ids[0]));
948 status = NT_STATUS_INVALID_WORKSTATION;
949 goto done;
952 user_rid = user_rids.ids[0];
954 /* Open handle on user */
956 status = rpccli_samr_OpenUser(pipe_hnd, mem_ctx,
957 &domain_pol,
958 SEC_FLAG_MAXIMUM_ALLOWED,
959 user_rid,
960 &user_pol);
961 if (!NT_STATUS_IS_OK(status)) {
962 goto done;
965 /* Fill in the additional account flags now */
967 acct_flags |= ACB_PWNOEXP;
968 if (r->out.domain_is_ad) {
969 #if !defined(ENCTYPE_ARCFOUR_HMAC)
970 acct_flags |= ACB_USE_DES_KEY_ONLY;
971 #endif
975 /* Set account flags on machine account */
976 ZERO_STRUCT(user_info.info16);
977 user_info.info16.acct_flags = acct_flags;
979 status = rpccli_samr_SetUserInfo(pipe_hnd, mem_ctx,
980 &user_pol,
982 &user_info);
984 if (!NT_STATUS_IS_OK(status)) {
986 rpccli_samr_DeleteUser(pipe_hnd, mem_ctx,
987 &user_pol);
989 libnet_join_set_error_string(mem_ctx, r,
990 "Failed to set account flags for machine account (%s)\n",
991 nt_errstr(status));
992 goto done;
995 /* Set password on machine account - first try level 26 */
997 init_samr_CryptPasswordEx(r->in.machine_password,
998 &cli->user_session_key,
999 &crypt_pwd_ex);
1001 user_info.info26.password = crypt_pwd_ex;
1002 user_info.info26.password_expired = PASS_DONT_CHANGE_AT_NEXT_LOGON;
1004 status = rpccli_samr_SetUserInfo2(pipe_hnd, mem_ctx,
1005 &user_pol,
1007 &user_info);
1009 if (NT_STATUS_EQUAL(status, NT_STATUS(DCERPC_FAULT_INVALID_TAG))) {
1011 /* retry with level 24 */
1013 init_samr_CryptPassword(r->in.machine_password,
1014 &cli->user_session_key,
1015 &crypt_pwd);
1017 user_info.info24.password = crypt_pwd;
1018 user_info.info24.password_expired = PASS_DONT_CHANGE_AT_NEXT_LOGON;
1020 status = rpccli_samr_SetUserInfo2(pipe_hnd, mem_ctx,
1021 &user_pol,
1023 &user_info);
1026 if (!NT_STATUS_IS_OK(status)) {
1028 rpccli_samr_DeleteUser(pipe_hnd, mem_ctx,
1029 &user_pol);
1031 libnet_join_set_error_string(mem_ctx, r,
1032 "Failed to set password for machine account (%s)\n",
1033 nt_errstr(status));
1034 goto done;
1037 status = NT_STATUS_OK;
1039 done:
1040 if (!pipe_hnd) {
1041 return status;
1044 if (is_valid_policy_hnd(&sam_pol)) {
1045 rpccli_samr_Close(pipe_hnd, mem_ctx, &sam_pol);
1047 if (is_valid_policy_hnd(&domain_pol)) {
1048 rpccli_samr_Close(pipe_hnd, mem_ctx, &domain_pol);
1050 if (is_valid_policy_hnd(&user_pol)) {
1051 rpccli_samr_Close(pipe_hnd, mem_ctx, &user_pol);
1053 TALLOC_FREE(pipe_hnd);
1055 return status;
1058 /****************************************************************
1059 ****************************************************************/
1061 NTSTATUS libnet_join_ok(const char *netbios_domain_name,
1062 const char *machine_name,
1063 const char *dc_name)
1065 uint32_t neg_flags = NETLOGON_NEG_AUTH2_ADS_FLAGS;
1066 struct cli_state *cli = NULL;
1067 struct rpc_pipe_client *pipe_hnd = NULL;
1068 struct rpc_pipe_client *netlogon_pipe = NULL;
1069 NTSTATUS status;
1070 char *machine_password = NULL;
1071 char *machine_account = NULL;
1073 if (!dc_name) {
1074 return NT_STATUS_INVALID_PARAMETER;
1077 if (!secrets_init()) {
1078 return NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
1081 machine_password = secrets_fetch_machine_password(netbios_domain_name,
1082 NULL, NULL);
1083 if (!machine_password) {
1084 return NT_STATUS_NO_TRUST_LSA_SECRET;
1087 if (asprintf(&machine_account, "%s$", machine_name) == -1) {
1088 SAFE_FREE(machine_password);
1089 return NT_STATUS_NO_MEMORY;
1092 status = cli_full_connection(&cli, NULL,
1093 dc_name,
1094 NULL, 0,
1095 "IPC$", "IPC",
1096 machine_account,
1097 NULL,
1098 machine_password,
1100 Undefined, NULL);
1101 free(machine_account);
1102 free(machine_password);
1104 if (!NT_STATUS_IS_OK(status)) {
1105 status = cli_full_connection(&cli, NULL,
1106 dc_name,
1107 NULL, 0,
1108 "IPC$", "IPC",
1110 NULL,
1113 Undefined, NULL);
1116 if (!NT_STATUS_IS_OK(status)) {
1117 return status;
1120 status = get_schannel_session_key(cli, netbios_domain_name,
1121 &neg_flags, &netlogon_pipe);
1122 if (!NT_STATUS_IS_OK(status)) {
1123 if (NT_STATUS_EQUAL(status, NT_STATUS_INVALID_NETWORK_RESPONSE)) {
1124 cli_shutdown(cli);
1125 return NT_STATUS_OK;
1128 DEBUG(0,("libnet_join_ok: failed to get schannel session "
1129 "key from server %s for domain %s. Error was %s\n",
1130 cli->desthost, netbios_domain_name, nt_errstr(status)));
1131 cli_shutdown(cli);
1132 return status;
1135 if (!lp_client_schannel()) {
1136 cli_shutdown(cli);
1137 return NT_STATUS_OK;
1140 status = cli_rpc_pipe_open_schannel_with_key(
1141 cli, &ndr_table_netlogon.syntax_id, NCACN_NP,
1142 DCERPC_AUTH_LEVEL_PRIVACY,
1143 netbios_domain_name, &netlogon_pipe->dc, &pipe_hnd);
1145 cli_shutdown(cli);
1147 if (!NT_STATUS_IS_OK(status)) {
1148 DEBUG(0,("libnet_join_ok: failed to open schannel session "
1149 "on netlogon pipe to server %s for domain %s. "
1150 "Error was %s\n",
1151 cli->desthost, netbios_domain_name, nt_errstr(status)));
1152 return status;
1155 return NT_STATUS_OK;
1158 /****************************************************************
1159 ****************************************************************/
1161 static WERROR libnet_join_post_verify(TALLOC_CTX *mem_ctx,
1162 struct libnet_JoinCtx *r)
1164 NTSTATUS status;
1166 status = libnet_join_ok(r->out.netbios_domain_name,
1167 r->in.machine_name,
1168 r->in.dc_name);
1169 if (!NT_STATUS_IS_OK(status)) {
1170 libnet_join_set_error_string(mem_ctx, r,
1171 "failed to verify domain membership after joining: %s",
1172 get_friendly_nt_error_msg(status));
1173 return WERR_SETUP_NOT_JOINED;
1176 return WERR_OK;
1179 /****************************************************************
1180 ****************************************************************/
1182 static bool libnet_join_unjoindomain_remove_secrets(TALLOC_CTX *mem_ctx,
1183 struct libnet_UnjoinCtx *r)
1185 if (!secrets_delete_machine_password_ex(lp_workgroup())) {
1186 return false;
1189 if (!secrets_delete_domain_sid(lp_workgroup())) {
1190 return false;
1193 return true;
1196 /****************************************************************
1197 ****************************************************************/
1199 static NTSTATUS libnet_join_unjoindomain_rpc(TALLOC_CTX *mem_ctx,
1200 struct libnet_UnjoinCtx *r)
1202 struct cli_state *cli = NULL;
1203 struct rpc_pipe_client *pipe_hnd = NULL;
1204 struct policy_handle sam_pol, domain_pol, user_pol;
1205 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
1206 char *acct_name;
1207 uint32_t user_rid;
1208 struct lsa_String lsa_acct_name;
1209 struct samr_Ids user_rids;
1210 struct samr_Ids name_types;
1211 union samr_UserInfo *info = NULL;
1213 ZERO_STRUCT(sam_pol);
1214 ZERO_STRUCT(domain_pol);
1215 ZERO_STRUCT(user_pol);
1217 status = libnet_join_connect_dc_ipc(r->in.dc_name,
1218 r->in.admin_account,
1219 r->in.admin_password,
1220 r->in.use_kerberos,
1221 &cli);
1222 if (!NT_STATUS_IS_OK(status)) {
1223 goto done;
1226 /* Open the domain */
1228 status = cli_rpc_pipe_open_noauth(cli, &ndr_table_samr.syntax_id,
1229 &pipe_hnd);
1230 if (!NT_STATUS_IS_OK(status)) {
1231 DEBUG(0,("Error connecting to SAM pipe. Error was %s\n",
1232 nt_errstr(status)));
1233 goto done;
1236 status = rpccli_samr_Connect2(pipe_hnd, mem_ctx,
1237 pipe_hnd->desthost,
1238 SEC_FLAG_MAXIMUM_ALLOWED,
1239 &sam_pol);
1240 if (!NT_STATUS_IS_OK(status)) {
1241 goto done;
1244 status = rpccli_samr_OpenDomain(pipe_hnd, mem_ctx,
1245 &sam_pol,
1246 SEC_FLAG_MAXIMUM_ALLOWED,
1247 r->in.domain_sid,
1248 &domain_pol);
1249 if (!NT_STATUS_IS_OK(status)) {
1250 goto done;
1253 /* Create domain user */
1255 acct_name = talloc_asprintf(mem_ctx, "%s$", r->in.machine_name);
1256 strlower_m(acct_name);
1258 init_lsa_String(&lsa_acct_name, acct_name);
1260 status = rpccli_samr_LookupNames(pipe_hnd, mem_ctx,
1261 &domain_pol,
1263 &lsa_acct_name,
1264 &user_rids,
1265 &name_types);
1267 if (!NT_STATUS_IS_OK(status)) {
1268 goto done;
1271 if (name_types.ids[0] != SID_NAME_USER) {
1272 DEBUG(0, ("%s is not a user account (type=%d)\n", acct_name,
1273 name_types.ids[0]));
1274 status = NT_STATUS_INVALID_WORKSTATION;
1275 goto done;
1278 user_rid = user_rids.ids[0];
1280 /* Open handle on user */
1282 status = rpccli_samr_OpenUser(pipe_hnd, mem_ctx,
1283 &domain_pol,
1284 SEC_FLAG_MAXIMUM_ALLOWED,
1285 user_rid,
1286 &user_pol);
1287 if (!NT_STATUS_IS_OK(status)) {
1288 goto done;
1291 /* Get user info */
1293 status = rpccli_samr_QueryUserInfo(pipe_hnd, mem_ctx,
1294 &user_pol,
1296 &info);
1297 if (!NT_STATUS_IS_OK(status)) {
1298 rpccli_samr_Close(pipe_hnd, mem_ctx, &user_pol);
1299 goto done;
1302 /* now disable and setuser info */
1304 info->info16.acct_flags |= ACB_DISABLED;
1306 status = rpccli_samr_SetUserInfo(pipe_hnd, mem_ctx,
1307 &user_pol,
1309 info);
1311 rpccli_samr_Close(pipe_hnd, mem_ctx, &user_pol);
1313 done:
1314 if (pipe_hnd) {
1315 if (is_valid_policy_hnd(&domain_pol)) {
1316 rpccli_samr_Close(pipe_hnd, mem_ctx, &domain_pol);
1318 if (is_valid_policy_hnd(&sam_pol)) {
1319 rpccli_samr_Close(pipe_hnd, mem_ctx, &sam_pol);
1321 TALLOC_FREE(pipe_hnd);
1324 if (cli) {
1325 cli_shutdown(cli);
1328 return status;
1331 /****************************************************************
1332 ****************************************************************/
1334 static WERROR do_join_modify_vals_config(struct libnet_JoinCtx *r)
1336 WERROR werr;
1337 struct smbconf_ctx *ctx;
1339 werr = smbconf_init_reg(r, &ctx, NULL);
1340 if (!W_ERROR_IS_OK(werr)) {
1341 goto done;
1344 if (!(r->in.join_flags & WKSSVC_JOIN_FLAGS_JOIN_TYPE)) {
1346 werr = smbconf_set_global_parameter(ctx, "security", "user");
1347 W_ERROR_NOT_OK_GOTO_DONE(werr);
1349 werr = smbconf_set_global_parameter(ctx, "workgroup",
1350 r->in.domain_name);
1352 smbconf_delete_global_parameter(ctx, "realm");
1353 goto done;
1356 werr = smbconf_set_global_parameter(ctx, "security", "domain");
1357 W_ERROR_NOT_OK_GOTO_DONE(werr);
1359 werr = smbconf_set_global_parameter(ctx, "workgroup",
1360 r->out.netbios_domain_name);
1361 W_ERROR_NOT_OK_GOTO_DONE(werr);
1363 if (r->out.domain_is_ad) {
1364 werr = smbconf_set_global_parameter(ctx, "security", "ads");
1365 W_ERROR_NOT_OK_GOTO_DONE(werr);
1367 werr = smbconf_set_global_parameter(ctx, "realm",
1368 r->out.dns_domain_name);
1369 W_ERROR_NOT_OK_GOTO_DONE(werr);
1372 done:
1373 smbconf_shutdown(ctx);
1374 return werr;
1377 /****************************************************************
1378 ****************************************************************/
1380 static WERROR do_unjoin_modify_vals_config(struct libnet_UnjoinCtx *r)
1382 WERROR werr = WERR_OK;
1383 struct smbconf_ctx *ctx;
1385 werr = smbconf_init_reg(r, &ctx, NULL);
1386 if (!W_ERROR_IS_OK(werr)) {
1387 goto done;
1390 if (r->in.unjoin_flags & WKSSVC_JOIN_FLAGS_JOIN_TYPE) {
1392 werr = smbconf_set_global_parameter(ctx, "security", "user");
1393 W_ERROR_NOT_OK_GOTO_DONE(werr);
1395 werr = smbconf_delete_global_parameter(ctx, "workgroup");
1396 W_ERROR_NOT_OK_GOTO_DONE(werr);
1398 smbconf_delete_global_parameter(ctx, "realm");
1401 done:
1402 smbconf_shutdown(ctx);
1403 return werr;
1406 /****************************************************************
1407 ****************************************************************/
1409 static WERROR do_JoinConfig(struct libnet_JoinCtx *r)
1411 WERROR werr;
1413 if (!W_ERROR_IS_OK(r->out.result)) {
1414 return r->out.result;
1417 if (!r->in.modify_config) {
1418 return WERR_OK;
1421 werr = do_join_modify_vals_config(r);
1422 if (!W_ERROR_IS_OK(werr)) {
1423 return werr;
1426 lp_load(get_dyn_CONFIGFILE(),true,false,false,true);
1428 r->out.modified_config = true;
1429 r->out.result = werr;
1431 return werr;
1434 /****************************************************************
1435 ****************************************************************/
1437 static WERROR libnet_unjoin_config(struct libnet_UnjoinCtx *r)
1439 WERROR werr;
1441 if (!W_ERROR_IS_OK(r->out.result)) {
1442 return r->out.result;
1445 if (!r->in.modify_config) {
1446 return WERR_OK;
1449 werr = do_unjoin_modify_vals_config(r);
1450 if (!W_ERROR_IS_OK(werr)) {
1451 return werr;
1454 lp_load(get_dyn_CONFIGFILE(),true,false,false,true);
1456 r->out.modified_config = true;
1457 r->out.result = werr;
1459 return werr;
1462 /****************************************************************
1463 ****************************************************************/
1465 static bool libnet_parse_domain_dc(TALLOC_CTX *mem_ctx,
1466 const char *domain_str,
1467 const char **domain_p,
1468 const char **dc_p)
1470 char *domain = NULL;
1471 char *dc = NULL;
1472 const char *p = NULL;
1474 if (!domain_str || !domain_p || !dc_p) {
1475 return false;
1478 p = strchr_m(domain_str, '\\');
1480 if (p != NULL) {
1481 domain = talloc_strndup(mem_ctx, domain_str,
1482 PTR_DIFF(p, domain_str));
1483 dc = talloc_strdup(mem_ctx, p+1);
1484 if (!dc) {
1485 return false;
1487 } else {
1488 domain = talloc_strdup(mem_ctx, domain_str);
1489 dc = NULL;
1491 if (!domain) {
1492 return false;
1495 *domain_p = domain;
1497 if (!*dc_p && dc) {
1498 *dc_p = dc;
1501 return true;
1504 /****************************************************************
1505 ****************************************************************/
1507 static WERROR libnet_join_pre_processing(TALLOC_CTX *mem_ctx,
1508 struct libnet_JoinCtx *r)
1510 if (!r->in.domain_name) {
1511 libnet_join_set_error_string(mem_ctx, r,
1512 "No domain name defined");
1513 return WERR_INVALID_PARAM;
1516 if (!libnet_parse_domain_dc(mem_ctx, r->in.domain_name,
1517 &r->in.domain_name,
1518 &r->in.dc_name)) {
1519 libnet_join_set_error_string(mem_ctx, r,
1520 "Failed to parse domain name");
1521 return WERR_INVALID_PARAM;
1524 if (IS_DC) {
1525 return WERR_SETUP_DOMAIN_CONTROLLER;
1528 if (!secrets_init()) {
1529 libnet_join_set_error_string(mem_ctx, r,
1530 "Unable to open secrets database");
1531 return WERR_CAN_NOT_COMPLETE;
1534 return WERR_OK;
1537 /****************************************************************
1538 ****************************************************************/
1540 static void libnet_join_add_dom_rids_to_builtins(struct dom_sid *domain_sid)
1542 NTSTATUS status;
1544 /* Try adding dom admins to builtin\admins. Only log failures. */
1545 status = create_builtin_administrators(domain_sid);
1546 if (NT_STATUS_EQUAL(status, NT_STATUS_PROTOCOL_UNREACHABLE)) {
1547 DEBUG(10,("Unable to auto-add domain administrators to "
1548 "BUILTIN\\Administrators during join because "
1549 "winbindd must be running."));
1550 } else if (!NT_STATUS_IS_OK(status)) {
1551 DEBUG(5, ("Failed to auto-add domain administrators to "
1552 "BUILTIN\\Administrators during join: %s\n",
1553 nt_errstr(status)));
1556 /* Try adding dom users to builtin\users. Only log failures. */
1557 status = create_builtin_users(domain_sid);
1558 if (NT_STATUS_EQUAL(status, NT_STATUS_PROTOCOL_UNREACHABLE)) {
1559 DEBUG(10,("Unable to auto-add domain users to BUILTIN\\users "
1560 "during join because winbindd must be running."));
1561 } else if (!NT_STATUS_IS_OK(status)) {
1562 DEBUG(5, ("Failed to auto-add domain administrators to "
1563 "BUILTIN\\Administrators during join: %s\n",
1564 nt_errstr(status)));
1568 /****************************************************************
1569 ****************************************************************/
1571 static WERROR libnet_join_post_processing(TALLOC_CTX *mem_ctx,
1572 struct libnet_JoinCtx *r)
1574 WERROR werr;
1576 if (!W_ERROR_IS_OK(r->out.result)) {
1577 return r->out.result;
1580 werr = do_JoinConfig(r);
1581 if (!W_ERROR_IS_OK(werr)) {
1582 return werr;
1585 if (!(r->in.join_flags & WKSSVC_JOIN_FLAGS_JOIN_TYPE)) {
1586 return WERR_OK;
1589 saf_join_store(r->out.netbios_domain_name, r->in.dc_name);
1590 if (r->out.dns_domain_name) {
1591 saf_join_store(r->out.dns_domain_name, r->in.dc_name);
1594 #ifdef WITH_ADS
1595 if (r->out.domain_is_ad &&
1596 !(r->in.join_flags & WKSSVC_JOIN_FLAGS_JOIN_UNSECURE)) {
1597 ADS_STATUS ads_status;
1599 ads_status = libnet_join_post_processing_ads(mem_ctx, r);
1600 if (!ADS_ERR_OK(ads_status)) {
1601 return WERR_GENERAL_FAILURE;
1604 #endif /* WITH_ADS */
1606 libnet_join_add_dom_rids_to_builtins(r->out.domain_sid);
1608 return WERR_OK;
1611 /****************************************************************
1612 ****************************************************************/
1614 static int libnet_destroy_JoinCtx(struct libnet_JoinCtx *r)
1616 const char *krb5_cc_env = NULL;
1618 if (r->in.ads) {
1619 ads_destroy(&r->in.ads);
1622 krb5_cc_env = getenv(KRB5_ENV_CCNAME);
1623 if (krb5_cc_env && StrCaseCmp(krb5_cc_env, "MEMORY:libnetjoin")) {
1624 unsetenv(KRB5_ENV_CCNAME);
1627 return 0;
1630 /****************************************************************
1631 ****************************************************************/
1633 static int libnet_destroy_UnjoinCtx(struct libnet_UnjoinCtx *r)
1635 const char *krb5_cc_env = NULL;
1637 if (r->in.ads) {
1638 ads_destroy(&r->in.ads);
1641 krb5_cc_env = getenv(KRB5_ENV_CCNAME);
1642 if (krb5_cc_env && StrCaseCmp(krb5_cc_env, "MEMORY:libnetjoin")) {
1643 unsetenv(KRB5_ENV_CCNAME);
1646 return 0;
1649 /****************************************************************
1650 ****************************************************************/
1652 WERROR libnet_init_JoinCtx(TALLOC_CTX *mem_ctx,
1653 struct libnet_JoinCtx **r)
1655 struct libnet_JoinCtx *ctx;
1656 const char *krb5_cc_env = NULL;
1658 ctx = talloc_zero(mem_ctx, struct libnet_JoinCtx);
1659 if (!ctx) {
1660 return WERR_NOMEM;
1663 talloc_set_destructor(ctx, libnet_destroy_JoinCtx);
1665 ctx->in.machine_name = talloc_strdup(mem_ctx, global_myname());
1666 W_ERROR_HAVE_NO_MEMORY(ctx->in.machine_name);
1668 krb5_cc_env = getenv(KRB5_ENV_CCNAME);
1669 if (!krb5_cc_env || (strlen(krb5_cc_env) == 0)) {
1670 krb5_cc_env = talloc_strdup(mem_ctx, "MEMORY:libnetjoin");
1671 W_ERROR_HAVE_NO_MEMORY(krb5_cc_env);
1672 setenv(KRB5_ENV_CCNAME, krb5_cc_env, 1);
1675 ctx->in.secure_channel_type = SEC_CHAN_WKSTA;
1677 *r = ctx;
1679 return WERR_OK;
1682 /****************************************************************
1683 ****************************************************************/
1685 WERROR libnet_init_UnjoinCtx(TALLOC_CTX *mem_ctx,
1686 struct libnet_UnjoinCtx **r)
1688 struct libnet_UnjoinCtx *ctx;
1689 const char *krb5_cc_env = NULL;
1691 ctx = talloc_zero(mem_ctx, struct libnet_UnjoinCtx);
1692 if (!ctx) {
1693 return WERR_NOMEM;
1696 talloc_set_destructor(ctx, libnet_destroy_UnjoinCtx);
1698 ctx->in.machine_name = talloc_strdup(mem_ctx, global_myname());
1699 W_ERROR_HAVE_NO_MEMORY(ctx->in.machine_name);
1701 krb5_cc_env = getenv(KRB5_ENV_CCNAME);
1702 if (!krb5_cc_env || (strlen(krb5_cc_env) == 0)) {
1703 krb5_cc_env = talloc_strdup(mem_ctx, "MEMORY:libnetjoin");
1704 W_ERROR_HAVE_NO_MEMORY(krb5_cc_env);
1705 setenv(KRB5_ENV_CCNAME, krb5_cc_env, 1);
1708 *r = ctx;
1710 return WERR_OK;
1713 /****************************************************************
1714 ****************************************************************/
1716 static WERROR libnet_join_check_config(TALLOC_CTX *mem_ctx,
1717 struct libnet_JoinCtx *r)
1719 bool valid_security = false;
1720 bool valid_workgroup = false;
1721 bool valid_realm = false;
1723 /* check if configuration is already set correctly */
1725 valid_workgroup = strequal(lp_workgroup(), r->out.netbios_domain_name);
1727 switch (r->out.domain_is_ad) {
1728 case false:
1729 valid_security = (lp_security() == SEC_DOMAIN);
1730 if (valid_workgroup && valid_security) {
1731 /* nothing to be done */
1732 return WERR_OK;
1734 break;
1735 case true:
1736 valid_realm = strequal(lp_realm(), r->out.dns_domain_name);
1737 switch (lp_security()) {
1738 case SEC_DOMAIN:
1739 case SEC_ADS:
1740 valid_security = true;
1743 if (valid_workgroup && valid_realm && valid_security) {
1744 /* nothing to be done */
1745 return WERR_OK;
1747 break;
1750 /* check if we are supposed to manipulate configuration */
1752 if (!r->in.modify_config) {
1754 char *wrong_conf = talloc_strdup(mem_ctx, "");
1756 if (!valid_workgroup) {
1757 wrong_conf = talloc_asprintf_append(wrong_conf,
1758 "\"workgroup\" set to '%s', should be '%s'",
1759 lp_workgroup(), r->out.netbios_domain_name);
1760 W_ERROR_HAVE_NO_MEMORY(wrong_conf);
1763 if (!valid_realm) {
1764 wrong_conf = talloc_asprintf_append(wrong_conf,
1765 "\"realm\" set to '%s', should be '%s'",
1766 lp_realm(), r->out.dns_domain_name);
1767 W_ERROR_HAVE_NO_MEMORY(wrong_conf);
1770 if (!valid_security) {
1771 const char *sec = NULL;
1772 switch (lp_security()) {
1773 case SEC_SHARE: sec = "share"; break;
1774 case SEC_USER: sec = "user"; break;
1775 case SEC_DOMAIN: sec = "domain"; break;
1776 case SEC_ADS: sec = "ads"; break;
1778 wrong_conf = talloc_asprintf_append(wrong_conf,
1779 "\"security\" set to '%s', should be %s",
1780 sec, r->out.domain_is_ad ?
1781 "either 'domain' or 'ads'" : "'domain'");
1782 W_ERROR_HAVE_NO_MEMORY(wrong_conf);
1785 libnet_join_set_error_string(mem_ctx, r,
1786 "Invalid configuration (%s) and configuration modification "
1787 "was not requested", wrong_conf);
1788 return WERR_CAN_NOT_COMPLETE;
1791 /* check if we are able to manipulate configuration */
1793 if (!lp_config_backend_is_registry()) {
1794 libnet_join_set_error_string(mem_ctx, r,
1795 "Configuration manipulation requested but not "
1796 "supported by backend");
1797 return WERR_NOT_SUPPORTED;
1800 return WERR_OK;
1803 /****************************************************************
1804 ****************************************************************/
1806 static WERROR libnet_DomainJoin(TALLOC_CTX *mem_ctx,
1807 struct libnet_JoinCtx *r)
1809 NTSTATUS status;
1810 WERROR werr;
1811 struct cli_state *cli = NULL;
1812 #ifdef WITH_ADS
1813 ADS_STATUS ads_status;
1814 #endif /* WITH_ADS */
1816 if (!r->in.dc_name) {
1817 struct netr_DsRGetDCNameInfo *info;
1818 const char *dc;
1819 status = dsgetdcname(mem_ctx,
1820 r->in.msg_ctx,
1821 r->in.domain_name,
1822 NULL,
1823 NULL,
1824 DS_FORCE_REDISCOVERY |
1825 DS_DIRECTORY_SERVICE_REQUIRED |
1826 DS_WRITABLE_REQUIRED |
1827 DS_RETURN_DNS_NAME,
1828 &info);
1829 if (!NT_STATUS_IS_OK(status)) {
1830 libnet_join_set_error_string(mem_ctx, r,
1831 "failed to find DC for domain %s",
1832 r->in.domain_name,
1833 get_friendly_nt_error_msg(status));
1834 return WERR_DCNOTFOUND;
1837 dc = strip_hostname(info->dc_unc);
1838 r->in.dc_name = talloc_strdup(mem_ctx, dc);
1839 W_ERROR_HAVE_NO_MEMORY(r->in.dc_name);
1842 status = libnet_join_lookup_dc_rpc(mem_ctx, r, &cli);
1843 if (!NT_STATUS_IS_OK(status)) {
1844 libnet_join_set_error_string(mem_ctx, r,
1845 "failed to lookup DC info for domain '%s' over rpc: %s",
1846 r->in.domain_name, get_friendly_nt_error_msg(status));
1847 return ntstatus_to_werror(status);
1850 werr = libnet_join_check_config(mem_ctx, r);
1851 if (!W_ERROR_IS_OK(werr)) {
1852 goto done;
1855 #ifdef WITH_ADS
1856 if (r->out.domain_is_ad && r->in.account_ou &&
1857 !(r->in.join_flags & WKSSVC_JOIN_FLAGS_JOIN_UNSECURE)) {
1859 ads_status = libnet_join_connect_ads(mem_ctx, r);
1860 if (!ADS_ERR_OK(ads_status)) {
1861 return WERR_DEFAULT_JOIN_REQUIRED;
1864 ads_status = libnet_join_precreate_machine_acct(mem_ctx, r);
1865 if (!ADS_ERR_OK(ads_status)) {
1866 libnet_join_set_error_string(mem_ctx, r,
1867 "failed to precreate account in ou %s: %s",
1868 r->in.account_ou,
1869 ads_errstr(ads_status));
1870 return WERR_DEFAULT_JOIN_REQUIRED;
1873 r->in.join_flags &= ~WKSSVC_JOIN_FLAGS_ACCOUNT_CREATE;
1875 #endif /* WITH_ADS */
1877 if ((r->in.join_flags & WKSSVC_JOIN_FLAGS_JOIN_UNSECURE) &&
1878 (r->in.join_flags & WKSSVC_JOIN_FLAGS_MACHINE_PWD_PASSED)) {
1879 status = libnet_join_joindomain_rpc_unsecure(mem_ctx, r, cli);
1880 } else {
1881 status = libnet_join_joindomain_rpc(mem_ctx, r, cli);
1883 if (!NT_STATUS_IS_OK(status)) {
1884 libnet_join_set_error_string(mem_ctx, r,
1885 "failed to join domain '%s' over rpc: %s",
1886 r->in.domain_name, get_friendly_nt_error_msg(status));
1887 if (NT_STATUS_EQUAL(status, NT_STATUS_USER_EXISTS)) {
1888 return WERR_SETUP_ALREADY_JOINED;
1890 werr = ntstatus_to_werror(status);
1891 goto done;
1894 if (!libnet_join_joindomain_store_secrets(mem_ctx, r)) {
1895 werr = WERR_SETUP_NOT_JOINED;
1896 goto done;
1899 werr = WERR_OK;
1901 done:
1902 if (cli) {
1903 cli_shutdown(cli);
1906 return werr;
1909 /****************************************************************
1910 ****************************************************************/
1912 static WERROR libnet_join_rollback(TALLOC_CTX *mem_ctx,
1913 struct libnet_JoinCtx *r)
1915 WERROR werr;
1916 struct libnet_UnjoinCtx *u = NULL;
1918 werr = libnet_init_UnjoinCtx(mem_ctx, &u);
1919 if (!W_ERROR_IS_OK(werr)) {
1920 return werr;
1923 u->in.debug = r->in.debug;
1924 u->in.dc_name = r->in.dc_name;
1925 u->in.domain_name = r->in.domain_name;
1926 u->in.admin_account = r->in.admin_account;
1927 u->in.admin_password = r->in.admin_password;
1928 u->in.modify_config = r->in.modify_config;
1929 u->in.unjoin_flags = WKSSVC_JOIN_FLAGS_JOIN_TYPE |
1930 WKSSVC_JOIN_FLAGS_ACCOUNT_DELETE;
1932 werr = libnet_Unjoin(mem_ctx, u);
1933 TALLOC_FREE(u);
1935 return werr;
1938 /****************************************************************
1939 ****************************************************************/
1941 WERROR libnet_Join(TALLOC_CTX *mem_ctx,
1942 struct libnet_JoinCtx *r)
1944 WERROR werr;
1946 if (r->in.debug) {
1947 LIBNET_JOIN_IN_DUMP_CTX(mem_ctx, r);
1950 werr = libnet_join_pre_processing(mem_ctx, r);
1951 if (!W_ERROR_IS_OK(werr)) {
1952 goto done;
1955 if (r->in.join_flags & WKSSVC_JOIN_FLAGS_JOIN_TYPE) {
1956 werr = libnet_DomainJoin(mem_ctx, r);
1957 if (!W_ERROR_IS_OK(werr)) {
1958 goto done;
1962 werr = libnet_join_post_processing(mem_ctx, r);
1963 if (!W_ERROR_IS_OK(werr)) {
1964 goto done;
1967 if (r->in.join_flags & WKSSVC_JOIN_FLAGS_JOIN_TYPE) {
1968 werr = libnet_join_post_verify(mem_ctx, r);
1969 if (!W_ERROR_IS_OK(werr)) {
1970 libnet_join_rollback(mem_ctx, r);
1974 done:
1975 r->out.result = werr;
1977 if (r->in.debug) {
1978 LIBNET_JOIN_OUT_DUMP_CTX(mem_ctx, r);
1980 return werr;
1983 /****************************************************************
1984 ****************************************************************/
1986 static WERROR libnet_DomainUnjoin(TALLOC_CTX *mem_ctx,
1987 struct libnet_UnjoinCtx *r)
1989 NTSTATUS status;
1991 if (!r->in.domain_sid) {
1992 struct dom_sid sid;
1993 if (!secrets_fetch_domain_sid(lp_workgroup(), &sid)) {
1994 libnet_unjoin_set_error_string(mem_ctx, r,
1995 "Unable to fetch domain sid: are we joined?");
1996 return WERR_SETUP_NOT_JOINED;
1998 r->in.domain_sid = sid_dup_talloc(mem_ctx, &sid);
1999 W_ERROR_HAVE_NO_MEMORY(r->in.domain_sid);
2002 if (!(r->in.unjoin_flags & WKSSVC_JOIN_FLAGS_ACCOUNT_DELETE) &&
2003 !r->in.delete_machine_account) {
2004 libnet_join_unjoindomain_remove_secrets(mem_ctx, r);
2005 return WERR_OK;
2008 if (!r->in.dc_name) {
2009 struct netr_DsRGetDCNameInfo *info;
2010 const char *dc;
2011 status = dsgetdcname(mem_ctx,
2012 r->in.msg_ctx,
2013 r->in.domain_name,
2014 NULL,
2015 NULL,
2016 DS_DIRECTORY_SERVICE_REQUIRED |
2017 DS_WRITABLE_REQUIRED |
2018 DS_RETURN_DNS_NAME,
2019 &info);
2020 if (!NT_STATUS_IS_OK(status)) {
2021 libnet_unjoin_set_error_string(mem_ctx, r,
2022 "failed to find DC for domain %s",
2023 r->in.domain_name,
2024 get_friendly_nt_error_msg(status));
2025 return WERR_DCNOTFOUND;
2028 dc = strip_hostname(info->dc_unc);
2029 r->in.dc_name = talloc_strdup(mem_ctx, dc);
2030 W_ERROR_HAVE_NO_MEMORY(r->in.dc_name);
2033 #ifdef WITH_ADS
2034 /* for net ads leave, try to delete the account. If it works,
2035 no sense in disabling. If it fails, we can still try to
2036 disable it. jmcd */
2038 if (r->in.delete_machine_account) {
2039 ADS_STATUS ads_status;
2040 ads_status = libnet_unjoin_connect_ads(mem_ctx, r);
2041 if (ADS_ERR_OK(ads_status)) {
2042 /* dirty hack */
2043 r->out.dns_domain_name =
2044 talloc_strdup(mem_ctx,
2045 r->in.ads->server.realm);
2046 ads_status =
2047 libnet_unjoin_remove_machine_acct(mem_ctx, r);
2049 if (!ADS_ERR_OK(ads_status)) {
2050 libnet_unjoin_set_error_string(mem_ctx, r,
2051 "failed to remove machine account from AD: %s",
2052 ads_errstr(ads_status));
2053 } else {
2054 r->out.deleted_machine_account = true;
2055 W_ERROR_HAVE_NO_MEMORY(r->out.dns_domain_name);
2056 libnet_join_unjoindomain_remove_secrets(mem_ctx, r);
2057 return WERR_OK;
2060 #endif /* WITH_ADS */
2062 /* The WKSSVC_JOIN_FLAGS_ACCOUNT_DELETE flag really means
2063 "disable". */
2064 if (r->in.unjoin_flags & WKSSVC_JOIN_FLAGS_ACCOUNT_DELETE) {
2065 status = libnet_join_unjoindomain_rpc(mem_ctx, r);
2066 if (!NT_STATUS_IS_OK(status)) {
2067 libnet_unjoin_set_error_string(mem_ctx, r,
2068 "failed to disable machine account via rpc: %s",
2069 get_friendly_nt_error_msg(status));
2070 if (NT_STATUS_EQUAL(status, NT_STATUS_NO_SUCH_USER)) {
2071 return WERR_SETUP_NOT_JOINED;
2073 return ntstatus_to_werror(status);
2076 r->out.disabled_machine_account = true;
2079 /* If disable succeeded or was not requested at all, we
2080 should be getting rid of our end of things */
2082 libnet_join_unjoindomain_remove_secrets(mem_ctx, r);
2084 return WERR_OK;
2087 /****************************************************************
2088 ****************************************************************/
2090 static WERROR libnet_unjoin_pre_processing(TALLOC_CTX *mem_ctx,
2091 struct libnet_UnjoinCtx *r)
2093 if (!r->in.domain_name) {
2094 libnet_unjoin_set_error_string(mem_ctx, r,
2095 "No domain name defined");
2096 return WERR_INVALID_PARAM;
2099 if (!libnet_parse_domain_dc(mem_ctx, r->in.domain_name,
2100 &r->in.domain_name,
2101 &r->in.dc_name)) {
2102 libnet_unjoin_set_error_string(mem_ctx, r,
2103 "Failed to parse domain name");
2104 return WERR_INVALID_PARAM;
2107 if (IS_DC) {
2108 return WERR_SETUP_DOMAIN_CONTROLLER;
2111 if (!secrets_init()) {
2112 libnet_unjoin_set_error_string(mem_ctx, r,
2113 "Unable to open secrets database");
2114 return WERR_CAN_NOT_COMPLETE;
2117 return WERR_OK;
2120 /****************************************************************
2121 ****************************************************************/
2123 static WERROR libnet_unjoin_post_processing(TALLOC_CTX *mem_ctx,
2124 struct libnet_UnjoinCtx *r)
2126 saf_delete(r->out.netbios_domain_name);
2127 saf_delete(r->out.dns_domain_name);
2129 return libnet_unjoin_config(r);
2132 /****************************************************************
2133 ****************************************************************/
2135 WERROR libnet_Unjoin(TALLOC_CTX *mem_ctx,
2136 struct libnet_UnjoinCtx *r)
2138 WERROR werr;
2140 if (r->in.debug) {
2141 LIBNET_UNJOIN_IN_DUMP_CTX(mem_ctx, r);
2144 werr = libnet_unjoin_pre_processing(mem_ctx, r);
2145 if (!W_ERROR_IS_OK(werr)) {
2146 goto done;
2149 if (r->in.unjoin_flags & WKSSVC_JOIN_FLAGS_JOIN_TYPE) {
2150 werr = libnet_DomainUnjoin(mem_ctx, r);
2151 if (!W_ERROR_IS_OK(werr)) {
2152 libnet_unjoin_config(r);
2153 goto done;
2157 werr = libnet_unjoin_post_processing(mem_ctx, r);
2158 if (!W_ERROR_IS_OK(werr)) {
2159 goto done;
2162 done:
2163 r->out.result = werr;
2165 if (r->in.debug) {
2166 LIBNET_UNJOIN_OUT_DUMP_CTX(mem_ctx, r);
2169 return werr;