7fd7259736b88ee0e4cc943f39e5c4f96badca9b
[samba.git] / source3 / libnet / libnet_join.c
1 /*
2  *  Unix SMB/CIFS implementation.
3  *  libnet Join Support
4  *  Copyright (C) Gerald (Jerry) Carter 2006
5  *  Copyright (C) Guenther Deschner 2007-2008
6  *
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.
11  *
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.
16  *
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/>.
19  */
20
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 "rpc_client/init_samr.h"
26 #include "../librpc/gen_ndr/cli_lsa.h"
27 #include "rpc_client/cli_lsarpc.h"
28 #include "../librpc/gen_ndr/cli_netlogon.h"
29 #include "rpc_client/cli_netlogon.h"
30 #include "lib/smbconf/smbconf.h"
31 #include "lib/smbconf/smbconf_reg.h"
32 #include "../libds/common/flags.h"
33
34
35 /****************************************************************
36 ****************************************************************/
37
38 #define LIBNET_JOIN_DUMP_CTX(ctx, r, f) \
39         do { \
40                 char *str = NULL; \
41                 str = NDR_PRINT_FUNCTION_STRING(ctx, libnet_JoinCtx, f, r); \
42                 DEBUG(1,("libnet_Join:\n%s", str)); \
43                 TALLOC_FREE(str); \
44         } while (0)
45
46 #define LIBNET_JOIN_IN_DUMP_CTX(ctx, r) \
47         LIBNET_JOIN_DUMP_CTX(ctx, r, NDR_IN | NDR_SET_VALUES)
48 #define LIBNET_JOIN_OUT_DUMP_CTX(ctx, r) \
49         LIBNET_JOIN_DUMP_CTX(ctx, r, NDR_OUT)
50
51 #define LIBNET_UNJOIN_DUMP_CTX(ctx, r, f) \
52         do { \
53                 char *str = NULL; \
54                 str = NDR_PRINT_FUNCTION_STRING(ctx, libnet_UnjoinCtx, f, r); \
55                 DEBUG(1,("libnet_Unjoin:\n%s", str)); \
56                 TALLOC_FREE(str); \
57         } while (0)
58
59 #define LIBNET_UNJOIN_IN_DUMP_CTX(ctx, r) \
60         LIBNET_UNJOIN_DUMP_CTX(ctx, r, NDR_IN | NDR_SET_VALUES)
61 #define LIBNET_UNJOIN_OUT_DUMP_CTX(ctx, r) \
62         LIBNET_UNJOIN_DUMP_CTX(ctx, r, NDR_OUT)
63
64 /****************************************************************
65 ****************************************************************/
66
67 static void libnet_join_set_error_string(TALLOC_CTX *mem_ctx,
68                                          struct libnet_JoinCtx *r,
69                                          const char *format, ...)
70 {
71         va_list args;
72
73         if (r->out.error_string) {
74                 return;
75         }
76
77         va_start(args, format);
78         r->out.error_string = talloc_vasprintf(mem_ctx, format, args);
79         va_end(args);
80 }
81
82 /****************************************************************
83 ****************************************************************/
84
85 static void libnet_unjoin_set_error_string(TALLOC_CTX *mem_ctx,
86                                            struct libnet_UnjoinCtx *r,
87                                            const char *format, ...)
88 {
89         va_list args;
90
91         if (r->out.error_string) {
92                 return;
93         }
94
95         va_start(args, format);
96         r->out.error_string = talloc_vasprintf(mem_ctx, format, args);
97         va_end(args);
98 }
99
100 #ifdef WITH_ADS
101
102 /****************************************************************
103 ****************************************************************/
104
105 static ADS_STATUS libnet_connect_ads(const char *dns_domain_name,
106                                      const char *netbios_domain_name,
107                                      const char *dc_name,
108                                      const char *user_name,
109                                      const char *password,
110                                      ADS_STRUCT **ads)
111 {
112         ADS_STATUS status;
113         ADS_STRUCT *my_ads = NULL;
114
115         my_ads = ads_init(dns_domain_name,
116                           netbios_domain_name,
117                           dc_name);
118         if (!my_ads) {
119                 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
120         }
121
122         if (user_name) {
123                 SAFE_FREE(my_ads->auth.user_name);
124                 my_ads->auth.user_name = SMB_STRDUP(user_name);
125         }
126
127         if (password) {
128                 SAFE_FREE(my_ads->auth.password);
129                 my_ads->auth.password = SMB_STRDUP(password);
130         }
131
132         status = ads_connect_user_creds(my_ads);
133         if (!ADS_ERR_OK(status)) {
134                 ads_destroy(&my_ads);
135                 return status;
136         }
137
138         *ads = my_ads;
139         return ADS_SUCCESS;
140 }
141
142 /****************************************************************
143 ****************************************************************/
144
145 static ADS_STATUS libnet_join_connect_ads(TALLOC_CTX *mem_ctx,
146                                           struct libnet_JoinCtx *r)
147 {
148         ADS_STATUS status;
149
150         status = libnet_connect_ads(r->out.dns_domain_name,
151                                     r->out.netbios_domain_name,
152                                     r->in.dc_name,
153                                     r->in.admin_account,
154                                     r->in.admin_password,
155                                     &r->in.ads);
156         if (!ADS_ERR_OK(status)) {
157                 libnet_join_set_error_string(mem_ctx, r,
158                         "failed to connect to AD: %s",
159                         ads_errstr(status));
160                 return status;
161         }
162
163         if (!r->out.netbios_domain_name) {
164                 r->out.netbios_domain_name = talloc_strdup(mem_ctx,
165                                                            r->in.ads->server.workgroup);
166                 ADS_ERROR_HAVE_NO_MEMORY(r->out.netbios_domain_name);
167         }
168
169         if (!r->out.dns_domain_name) {
170                 r->out.dns_domain_name = talloc_strdup(mem_ctx,
171                                                        r->in.ads->config.realm);
172                 ADS_ERROR_HAVE_NO_MEMORY(r->out.dns_domain_name);
173         }
174
175         r->out.domain_is_ad = true;
176
177         return ADS_SUCCESS;
178 }
179
180 /****************************************************************
181 ****************************************************************/
182
183 static ADS_STATUS libnet_unjoin_connect_ads(TALLOC_CTX *mem_ctx,
184                                             struct libnet_UnjoinCtx *r)
185 {
186         ADS_STATUS status;
187
188         status = libnet_connect_ads(r->in.domain_name,
189                                     r->in.domain_name,
190                                     r->in.dc_name,
191                                     r->in.admin_account,
192                                     r->in.admin_password,
193                                     &r->in.ads);
194         if (!ADS_ERR_OK(status)) {
195                 libnet_unjoin_set_error_string(mem_ctx, r,
196                         "failed to connect to AD: %s",
197                         ads_errstr(status));
198         }
199
200         return status;
201 }
202
203 /****************************************************************
204  join a domain using ADS (LDAP mods)
205 ****************************************************************/
206
207 static ADS_STATUS libnet_join_precreate_machine_acct(TALLOC_CTX *mem_ctx,
208                                                      struct libnet_JoinCtx *r)
209 {
210         ADS_STATUS status;
211         LDAPMessage *res = NULL;
212         const char *attrs[] = { "dn", NULL };
213         bool moved = false;
214
215         status = ads_check_ou_dn(mem_ctx, r->in.ads, &r->in.account_ou);
216         if (!ADS_ERR_OK(status)) {
217                 return status;
218         }
219
220         status = ads_search_dn(r->in.ads, &res, r->in.account_ou, attrs);
221         if (!ADS_ERR_OK(status)) {
222                 return status;
223         }
224
225         if (ads_count_replies(r->in.ads, res) != 1) {
226                 ads_msgfree(r->in.ads, res);
227                 return ADS_ERROR_LDAP(LDAP_NO_SUCH_OBJECT);
228         }
229
230         ads_msgfree(r->in.ads, res);
231
232         /* Attempt to create the machine account and bail if this fails.
233            Assume that the admin wants exactly what they requested */
234
235         status = ads_create_machine_acct(r->in.ads,
236                                          r->in.machine_name,
237                                          r->in.account_ou);
238
239         if (ADS_ERR_OK(status)) {
240                 DEBUG(1,("machine account creation created\n"));
241                 return status;
242         } else  if ((status.error_type == ENUM_ADS_ERROR_LDAP) &&
243                     (status.err.rc == LDAP_ALREADY_EXISTS)) {
244                 status = ADS_SUCCESS;
245         }
246
247         if (!ADS_ERR_OK(status)) {
248                 DEBUG(1,("machine account creation failed\n"));
249                 return status;
250         }
251
252         status = ads_move_machine_acct(r->in.ads,
253                                        r->in.machine_name,
254                                        r->in.account_ou,
255                                        &moved);
256         if (!ADS_ERR_OK(status)) {
257                 DEBUG(1,("failure to locate/move pre-existing "
258                         "machine account\n"));
259                 return status;
260         }
261
262         DEBUG(1,("The machine account %s the specified OU.\n",
263                 moved ? "was moved into" : "already exists in"));
264
265         return status;
266 }
267
268 /****************************************************************
269 ****************************************************************/
270
271 static ADS_STATUS libnet_unjoin_remove_machine_acct(TALLOC_CTX *mem_ctx,
272                                                     struct libnet_UnjoinCtx *r)
273 {
274         ADS_STATUS status;
275
276         if (!r->in.ads) {
277                 status = libnet_unjoin_connect_ads(mem_ctx, r);
278                 if (!ADS_ERR_OK(status)) {
279                         libnet_unjoin_set_error_string(mem_ctx, r,
280                                 "failed to connect to AD: %s",
281                                 ads_errstr(status));
282                         return status;
283                 }
284         }
285
286         status = ads_leave_realm(r->in.ads, r->in.machine_name);
287         if (!ADS_ERR_OK(status)) {
288                 libnet_unjoin_set_error_string(mem_ctx, r,
289                         "failed to leave realm: %s",
290                         ads_errstr(status));
291                 return status;
292         }
293
294         return ADS_SUCCESS;
295 }
296
297 /****************************************************************
298 ****************************************************************/
299
300 static ADS_STATUS libnet_join_find_machine_acct(TALLOC_CTX *mem_ctx,
301                                                 struct libnet_JoinCtx *r)
302 {
303         ADS_STATUS status;
304         LDAPMessage *res = NULL;
305         char *dn = NULL;
306
307         if (!r->in.machine_name) {
308                 return ADS_ERROR(LDAP_NO_MEMORY);
309         }
310
311         status = ads_find_machine_acct(r->in.ads,
312                                        &res,
313                                        r->in.machine_name);
314         if (!ADS_ERR_OK(status)) {
315                 return status;
316         }
317
318         if (ads_count_replies(r->in.ads, res) != 1) {
319                 status = ADS_ERROR_LDAP(LDAP_NO_MEMORY);
320                 goto done;
321         }
322
323         dn = ads_get_dn(r->in.ads, mem_ctx, res);
324         if (!dn) {
325                 status = ADS_ERROR_LDAP(LDAP_NO_MEMORY);
326                 goto done;
327         }
328
329         r->out.dn = talloc_strdup(mem_ctx, dn);
330         if (!r->out.dn) {
331                 status = ADS_ERROR_LDAP(LDAP_NO_MEMORY);
332                 goto done;
333         }
334
335  done:
336         ads_msgfree(r->in.ads, res);
337         TALLOC_FREE(dn);
338
339         return status;
340 }
341
342 /****************************************************************
343  Set a machines dNSHostName and servicePrincipalName attributes
344 ****************************************************************/
345
346 static ADS_STATUS libnet_join_set_machine_spn(TALLOC_CTX *mem_ctx,
347                                               struct libnet_JoinCtx *r)
348 {
349         ADS_STATUS status;
350         ADS_MODLIST mods;
351         fstring my_fqdn;
352         const char *spn_array[3] = {NULL, NULL, NULL};
353         char *spn = NULL;
354
355         /* Find our DN */
356
357         status = libnet_join_find_machine_acct(mem_ctx, r);
358         if (!ADS_ERR_OK(status)) {
359                 return status;
360         }
361
362         /* Windows only creates HOST/shortname & HOST/fqdn. */
363
364         spn = talloc_asprintf(mem_ctx, "HOST/%s", r->in.machine_name);
365         if (!spn) {
366                 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
367         }
368         strupper_m(spn);
369         spn_array[0] = spn;
370
371         if (!name_to_fqdn(my_fqdn, r->in.machine_name)
372             || (strchr(my_fqdn, '.') == NULL)) {
373                 fstr_sprintf(my_fqdn, "%s.%s", r->in.machine_name,
374                              r->out.dns_domain_name);
375         }
376
377         strlower_m(my_fqdn);
378
379         if (!strequal(my_fqdn, r->in.machine_name)) {
380                 spn = talloc_asprintf(mem_ctx, "HOST/%s", my_fqdn);
381                 if (!spn) {
382                         return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
383                 }
384                 spn_array[1] = spn;
385         }
386
387         mods = ads_init_mods(mem_ctx);
388         if (!mods) {
389                 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
390         }
391
392         /* fields of primary importance */
393
394         status = ads_mod_str(mem_ctx, &mods, "dNSHostName", my_fqdn);
395         if (!ADS_ERR_OK(status)) {
396                 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
397         }
398
399         status = ads_mod_strlist(mem_ctx, &mods, "servicePrincipalName",
400                                  spn_array);
401         if (!ADS_ERR_OK(status)) {
402                 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
403         }
404
405         return ads_gen_mod(r->in.ads, r->out.dn, mods);
406 }
407
408 /****************************************************************
409 ****************************************************************/
410
411 static ADS_STATUS libnet_join_set_machine_upn(TALLOC_CTX *mem_ctx,
412                                               struct libnet_JoinCtx *r)
413 {
414         ADS_STATUS status;
415         ADS_MODLIST mods;
416
417         if (!r->in.create_upn) {
418                 return ADS_SUCCESS;
419         }
420
421         /* Find our DN */
422
423         status = libnet_join_find_machine_acct(mem_ctx, r);
424         if (!ADS_ERR_OK(status)) {
425                 return status;
426         }
427
428         if (!r->in.upn) {
429                 r->in.upn = talloc_asprintf(mem_ctx,
430                                             "host/%s@%s",
431                                             r->in.machine_name,
432                                             r->out.dns_domain_name);
433                 if (!r->in.upn) {
434                         return ADS_ERROR(LDAP_NO_MEMORY);
435                 }
436         }
437
438         /* now do the mods */
439
440         mods = ads_init_mods(mem_ctx);
441         if (!mods) {
442                 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
443         }
444
445         /* fields of primary importance */
446
447         status = ads_mod_str(mem_ctx, &mods, "userPrincipalName", r->in.upn);
448         if (!ADS_ERR_OK(status)) {
449                 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
450         }
451
452         return ads_gen_mod(r->in.ads, r->out.dn, mods);
453 }
454
455
456 /****************************************************************
457 ****************************************************************/
458
459 static ADS_STATUS libnet_join_set_os_attributes(TALLOC_CTX *mem_ctx,
460                                                 struct libnet_JoinCtx *r)
461 {
462         ADS_STATUS status;
463         ADS_MODLIST mods;
464         char *os_sp = NULL;
465
466         if (!r->in.os_name || !r->in.os_version ) {
467                 return ADS_SUCCESS;
468         }
469
470         /* Find our DN */
471
472         status = libnet_join_find_machine_acct(mem_ctx, r);
473         if (!ADS_ERR_OK(status)) {
474                 return status;
475         }
476
477         /* now do the mods */
478
479         mods = ads_init_mods(mem_ctx);
480         if (!mods) {
481                 return ADS_ERROR(LDAP_NO_MEMORY);
482         }
483
484         os_sp = talloc_asprintf(mem_ctx, "Samba %s", samba_version_string());
485         if (!os_sp) {
486                 return ADS_ERROR(LDAP_NO_MEMORY);
487         }
488
489         /* fields of primary importance */
490
491         status = ads_mod_str(mem_ctx, &mods, "operatingSystem",
492                              r->in.os_name);
493         if (!ADS_ERR_OK(status)) {
494                 return status;
495         }
496
497         status = ads_mod_str(mem_ctx, &mods, "operatingSystemVersion",
498                              r->in.os_version);
499         if (!ADS_ERR_OK(status)) {
500                 return status;
501         }
502
503         status = ads_mod_str(mem_ctx, &mods, "operatingSystemServicePack",
504                              os_sp);
505         if (!ADS_ERR_OK(status)) {
506                 return status;
507         }
508
509         return ads_gen_mod(r->in.ads, r->out.dn, mods);
510 }
511
512 /****************************************************************
513 ****************************************************************/
514
515 static bool libnet_join_create_keytab(TALLOC_CTX *mem_ctx,
516                                       struct libnet_JoinCtx *r)
517 {
518         if (!USE_SYSTEM_KEYTAB) {
519                 return true;
520         }
521
522         if (ads_keytab_create_default(r->in.ads) != 0) {
523                 return false;
524         }
525
526         return true;
527 }
528
529 /****************************************************************
530 ****************************************************************/
531
532 static bool libnet_join_derive_salting_principal(TALLOC_CTX *mem_ctx,
533                                                  struct libnet_JoinCtx *r)
534 {
535         uint32_t domain_func;
536         ADS_STATUS status;
537         const char *salt = NULL;
538         char *std_salt = NULL;
539
540         status = ads_domain_func_level(r->in.ads, &domain_func);
541         if (!ADS_ERR_OK(status)) {
542                 libnet_join_set_error_string(mem_ctx, r,
543                         "failed to determine domain functional level: %s",
544                         ads_errstr(status));
545                 return false;
546         }
547
548         /* go ahead and setup the default salt */
549
550         std_salt = kerberos_standard_des_salt();
551         if (!std_salt) {
552                 libnet_join_set_error_string(mem_ctx, r,
553                         "failed to obtain standard DES salt");
554                 return false;
555         }
556
557         salt = talloc_strdup(mem_ctx, std_salt);
558         if (!salt) {
559                 return false;
560         }
561
562         SAFE_FREE(std_salt);
563
564         /* if it's a Windows functional domain, we have to look for the UPN */
565
566         if (domain_func == DS_DOMAIN_FUNCTION_2000) {
567                 char *upn;
568
569                 upn = ads_get_upn(r->in.ads, mem_ctx,
570                                   r->in.machine_name);
571                 if (upn) {
572                         salt = talloc_strdup(mem_ctx, upn);
573                         if (!salt) {
574                                 return false;
575                         }
576                 }
577         }
578
579         return kerberos_secrets_store_des_salt(salt);
580 }
581
582 /****************************************************************
583 ****************************************************************/
584
585 static ADS_STATUS libnet_join_post_processing_ads(TALLOC_CTX *mem_ctx,
586                                                   struct libnet_JoinCtx *r)
587 {
588         ADS_STATUS status;
589
590         if (!r->in.ads) {
591                 status = libnet_join_connect_ads(mem_ctx, r);
592                 if (!ADS_ERR_OK(status)) {
593                         return status;
594                 }
595         }
596
597         status = libnet_join_set_machine_spn(mem_ctx, r);
598         if (!ADS_ERR_OK(status)) {
599                 libnet_join_set_error_string(mem_ctx, r,
600                         "failed to set machine spn: %s",
601                         ads_errstr(status));
602                 return status;
603         }
604
605         status = libnet_join_set_os_attributes(mem_ctx, r);
606         if (!ADS_ERR_OK(status)) {
607                 libnet_join_set_error_string(mem_ctx, r,
608                         "failed to set machine os attributes: %s",
609                         ads_errstr(status));
610                 return status;
611         }
612
613         status = libnet_join_set_machine_upn(mem_ctx, r);
614         if (!ADS_ERR_OK(status)) {
615                 libnet_join_set_error_string(mem_ctx, r,
616                         "failed to set machine upn: %s",
617                         ads_errstr(status));
618                 return status;
619         }
620
621         if (!libnet_join_derive_salting_principal(mem_ctx, r)) {
622                 return ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL);
623         }
624
625         if (!libnet_join_create_keytab(mem_ctx, r)) {
626                 libnet_join_set_error_string(mem_ctx, r,
627                         "failed to create kerberos keytab");
628                 return ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL);
629         }
630
631         return ADS_SUCCESS;
632 }
633 #endif /* WITH_ADS */
634
635 /****************************************************************
636  Store the machine password and domain SID
637 ****************************************************************/
638
639 static bool libnet_join_joindomain_store_secrets(TALLOC_CTX *mem_ctx,
640                                                  struct libnet_JoinCtx *r)
641 {
642         if (!secrets_store_domain_sid(r->out.netbios_domain_name,
643                                       r->out.domain_sid))
644         {
645                 DEBUG(1,("Failed to save domain sid\n"));
646                 return false;
647         }
648
649         if (!secrets_store_machine_password(r->in.machine_password,
650                                             r->out.netbios_domain_name,
651                                             r->in.secure_channel_type))
652         {
653                 DEBUG(1,("Failed to save machine password\n"));
654                 return false;
655         }
656
657         return true;
658 }
659
660 /****************************************************************
661  Connect dc's IPC$ share
662 ****************************************************************/
663
664 static NTSTATUS libnet_join_connect_dc_ipc(const char *dc,
665                                            const char *user,
666                                            const char *pass,
667                                            bool use_kerberos,
668                                            struct cli_state **cli)
669 {
670         int flags = 0;
671
672         if (use_kerberos) {
673                 flags |= CLI_FULL_CONNECTION_USE_KERBEROS;
674         }
675
676         if (use_kerberos && pass) {
677                 flags |= CLI_FULL_CONNECTION_FALLBACK_AFTER_KERBEROS;
678         }
679
680         return cli_full_connection(cli, NULL,
681                                    dc,
682                                    NULL, 0,
683                                    "IPC$", "IPC",
684                                    user,
685                                    NULL,
686                                    pass,
687                                    flags,
688                                    Undefined, NULL);
689 }
690
691 /****************************************************************
692  Lookup domain dc's info
693 ****************************************************************/
694
695 static NTSTATUS libnet_join_lookup_dc_rpc(TALLOC_CTX *mem_ctx,
696                                           struct libnet_JoinCtx *r,
697                                           struct cli_state **cli)
698 {
699         struct rpc_pipe_client *pipe_hnd = NULL;
700         struct policy_handle lsa_pol;
701         NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
702         union lsa_PolicyInformation *info = NULL;
703
704         status = libnet_join_connect_dc_ipc(r->in.dc_name,
705                                             r->in.admin_account,
706                                             r->in.admin_password,
707                                             r->in.use_kerberos,
708                                             cli);
709         if (!NT_STATUS_IS_OK(status)) {
710                 goto done;
711         }
712
713         status = cli_rpc_pipe_open_noauth(*cli, &ndr_table_lsarpc.syntax_id,
714                                           &pipe_hnd);
715         if (!NT_STATUS_IS_OK(status)) {
716                 DEBUG(0,("Error connecting to LSA pipe. Error was %s\n",
717                         nt_errstr(status)));
718                 goto done;
719         }
720
721         status = rpccli_lsa_open_policy(pipe_hnd, mem_ctx, true,
722                                         SEC_FLAG_MAXIMUM_ALLOWED, &lsa_pol);
723         if (!NT_STATUS_IS_OK(status)) {
724                 goto done;
725         }
726
727         status = rpccli_lsa_QueryInfoPolicy2(pipe_hnd, mem_ctx,
728                                              &lsa_pol,
729                                              LSA_POLICY_INFO_DNS,
730                                              &info);
731         if (NT_STATUS_IS_OK(status)) {
732                 r->out.domain_is_ad = true;
733                 r->out.netbios_domain_name = info->dns.name.string;
734                 r->out.dns_domain_name = info->dns.dns_domain.string;
735                 r->out.forest_name = info->dns.dns_forest.string;
736                 r->out.domain_sid = sid_dup_talloc(mem_ctx, info->dns.sid);
737                 NT_STATUS_HAVE_NO_MEMORY(r->out.domain_sid);
738         }
739
740         if (!NT_STATUS_IS_OK(status)) {
741                 status = rpccli_lsa_QueryInfoPolicy(pipe_hnd, mem_ctx,
742                                                     &lsa_pol,
743                                                     LSA_POLICY_INFO_ACCOUNT_DOMAIN,
744                                                     &info);
745                 if (!NT_STATUS_IS_OK(status)) {
746                         goto done;
747                 }
748
749                 r->out.netbios_domain_name = info->account_domain.name.string;
750                 r->out.domain_sid = sid_dup_talloc(mem_ctx, info->account_domain.sid);
751                 NT_STATUS_HAVE_NO_MEMORY(r->out.domain_sid);
752         }
753
754         rpccli_lsa_Close(pipe_hnd, mem_ctx, &lsa_pol);
755         TALLOC_FREE(pipe_hnd);
756
757  done:
758         return status;
759 }
760
761 /****************************************************************
762  Do the domain join unsecure
763 ****************************************************************/
764
765 static NTSTATUS libnet_join_joindomain_rpc_unsecure(TALLOC_CTX *mem_ctx,
766                                                     struct libnet_JoinCtx *r,
767                                                     struct cli_state *cli)
768 {
769         struct rpc_pipe_client *pipe_hnd = NULL;
770         unsigned char orig_trust_passwd_hash[16];
771         unsigned char new_trust_passwd_hash[16];
772         fstring trust_passwd;
773         NTSTATUS status;
774
775         status = cli_rpc_pipe_open_noauth(cli, &ndr_table_netlogon.syntax_id,
776                                           &pipe_hnd);
777         if (!NT_STATUS_IS_OK(status)) {
778                 return status;
779         }
780
781         if (!r->in.machine_password) {
782                 r->in.machine_password = generate_random_str(mem_ctx, DEFAULT_TRUST_ACCOUNT_PASSWORD_LENGTH);
783                 NT_STATUS_HAVE_NO_MEMORY(r->in.machine_password);
784         }
785
786         E_md4hash(r->in.machine_password, new_trust_passwd_hash);
787
788         /* according to WKSSVC_JOIN_FLAGS_MACHINE_PWD_PASSED */
789         fstrcpy(trust_passwd, r->in.admin_password);
790         strlower_m(trust_passwd);
791
792         /*
793          * Machine names can be 15 characters, but the max length on
794          * a password is 14.  --jerry
795          */
796
797         trust_passwd[14] = '\0';
798
799         E_md4hash(trust_passwd, orig_trust_passwd_hash);
800
801         status = rpccli_netlogon_set_trust_password(pipe_hnd, mem_ctx,
802                                                     r->in.machine_name,
803                                                     orig_trust_passwd_hash,
804                                                     r->in.machine_password,
805                                                     new_trust_passwd_hash,
806                                                     r->in.secure_channel_type);
807
808         return status;
809 }
810
811 /****************************************************************
812  Do the domain join
813 ****************************************************************/
814
815 static NTSTATUS libnet_join_joindomain_rpc(TALLOC_CTX *mem_ctx,
816                                            struct libnet_JoinCtx *r,
817                                            struct cli_state *cli)
818 {
819         struct rpc_pipe_client *pipe_hnd = NULL;
820         struct policy_handle sam_pol, domain_pol, user_pol;
821         NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
822         char *acct_name;
823         struct lsa_String lsa_acct_name;
824         uint32_t user_rid;
825         uint32_t acct_flags = ACB_WSTRUST;
826         struct samr_Ids user_rids;
827         struct samr_Ids name_types;
828         union samr_UserInfo user_info;
829
830         struct samr_CryptPassword crypt_pwd;
831         struct samr_CryptPasswordEx crypt_pwd_ex;
832
833         ZERO_STRUCT(sam_pol);
834         ZERO_STRUCT(domain_pol);
835         ZERO_STRUCT(user_pol);
836
837         switch (r->in.secure_channel_type) {
838         case SEC_CHAN_WKSTA:
839                 acct_flags = ACB_WSTRUST;
840                 break;
841         case SEC_CHAN_BDC:
842                 acct_flags = ACB_SVRTRUST;
843                 break;
844         default:
845                 return NT_STATUS_INVALID_PARAMETER;
846         }
847
848         if (!r->in.machine_password) {
849                 r->in.machine_password = generate_random_str(mem_ctx, DEFAULT_TRUST_ACCOUNT_PASSWORD_LENGTH);
850                 NT_STATUS_HAVE_NO_MEMORY(r->in.machine_password);
851         }
852
853         /* Open the domain */
854
855         status = cli_rpc_pipe_open_noauth(cli, &ndr_table_samr.syntax_id,
856                                           &pipe_hnd);
857         if (!NT_STATUS_IS_OK(status)) {
858                 DEBUG(0,("Error connecting to SAM pipe. Error was %s\n",
859                         nt_errstr(status)));
860                 goto done;
861         }
862
863         status = rpccli_samr_Connect2(pipe_hnd, mem_ctx,
864                                       pipe_hnd->desthost,
865                                       SAMR_ACCESS_ENUM_DOMAINS
866                                       | SAMR_ACCESS_LOOKUP_DOMAIN,
867                                       &sam_pol);
868         if (!NT_STATUS_IS_OK(status)) {
869                 goto done;
870         }
871
872         status = rpccli_samr_OpenDomain(pipe_hnd, mem_ctx,
873                                         &sam_pol,
874                                         SAMR_DOMAIN_ACCESS_LOOKUP_INFO_1
875                                         | SAMR_DOMAIN_ACCESS_CREATE_USER
876                                         | SAMR_DOMAIN_ACCESS_OPEN_ACCOUNT,
877                                         r->out.domain_sid,
878                                         &domain_pol);
879         if (!NT_STATUS_IS_OK(status)) {
880                 goto done;
881         }
882
883         /* Create domain user */
884
885         acct_name = talloc_asprintf(mem_ctx, "%s$", r->in.machine_name);
886         strlower_m(acct_name);
887
888         init_lsa_String(&lsa_acct_name, acct_name);
889
890         if (r->in.join_flags & WKSSVC_JOIN_FLAGS_ACCOUNT_CREATE) {
891                 uint32_t access_desired =
892                         SEC_GENERIC_READ | SEC_GENERIC_WRITE | SEC_GENERIC_EXECUTE |
893                         SEC_STD_WRITE_DAC | SEC_STD_DELETE |
894                         SAMR_USER_ACCESS_SET_PASSWORD |
895                         SAMR_USER_ACCESS_GET_ATTRIBUTES |
896                         SAMR_USER_ACCESS_SET_ATTRIBUTES;
897                 uint32_t access_granted = 0;
898
899                 DEBUG(10,("Creating account with desired access mask: %d\n",
900                         access_desired));
901
902                 status = rpccli_samr_CreateUser2(pipe_hnd, mem_ctx,
903                                                  &domain_pol,
904                                                  &lsa_acct_name,
905                                                  acct_flags,
906                                                  access_desired,
907                                                  &user_pol,
908                                                  &access_granted,
909                                                  &user_rid);
910                 if (!NT_STATUS_IS_OK(status) &&
911                     !NT_STATUS_EQUAL(status, NT_STATUS_USER_EXISTS)) {
912
913                         DEBUG(10,("Creation of workstation account failed: %s\n",
914                                 nt_errstr(status)));
915
916                         /* If NT_STATUS_ACCESS_DENIED then we have a valid
917                            username/password combo but the user does not have
918                            administrator access. */
919
920                         if (NT_STATUS_EQUAL(status, NT_STATUS_ACCESS_DENIED)) {
921                                 libnet_join_set_error_string(mem_ctx, r,
922                                         "User specified does not have "
923                                         "administrator privileges");
924                         }
925
926                         goto done;
927                 }
928
929                 if (NT_STATUS_EQUAL(status, NT_STATUS_USER_EXISTS)) {
930                         if (!(r->in.join_flags &
931                               WKSSVC_JOIN_FLAGS_DOMAIN_JOIN_IF_JOINED)) {
932                                 goto done;
933                         }
934                 }
935
936                 /* We *must* do this.... don't ask... */
937
938                 if (NT_STATUS_IS_OK(status)) {
939                         rpccli_samr_Close(pipe_hnd, mem_ctx, &user_pol);
940                 }
941         }
942
943         status = rpccli_samr_LookupNames(pipe_hnd, mem_ctx,
944                                          &domain_pol,
945                                          1,
946                                          &lsa_acct_name,
947                                          &user_rids,
948                                          &name_types);
949         if (!NT_STATUS_IS_OK(status)) {
950                 goto done;
951         }
952
953         if (name_types.ids[0] != SID_NAME_USER) {
954                 DEBUG(0,("%s is not a user account (type=%d)\n",
955                         acct_name, name_types.ids[0]));
956                 status = NT_STATUS_INVALID_WORKSTATION;
957                 goto done;
958         }
959
960         user_rid = user_rids.ids[0];
961
962         /* Open handle on user */
963
964         status = rpccli_samr_OpenUser(pipe_hnd, mem_ctx,
965                                       &domain_pol,
966                                       SEC_FLAG_MAXIMUM_ALLOWED,
967                                       user_rid,
968                                       &user_pol);
969         if (!NT_STATUS_IS_OK(status)) {
970                 goto done;
971         }
972
973         /* Fill in the additional account flags now */
974
975         acct_flags |= ACB_PWNOEXP;
976         if (r->out.domain_is_ad) {
977 #if !defined(ENCTYPE_ARCFOUR_HMAC)
978                 acct_flags |= ACB_USE_DES_KEY_ONLY;
979 #endif
980                 ;;
981         }
982
983         /* Set account flags on machine account */
984         ZERO_STRUCT(user_info.info16);
985         user_info.info16.acct_flags = acct_flags;
986
987         status = rpccli_samr_SetUserInfo(pipe_hnd, mem_ctx,
988                                          &user_pol,
989                                          16,
990                                          &user_info);
991
992         if (!NT_STATUS_IS_OK(status)) {
993
994                 rpccli_samr_DeleteUser(pipe_hnd, mem_ctx,
995                                        &user_pol);
996
997                 libnet_join_set_error_string(mem_ctx, r,
998                         "Failed to set account flags for machine account (%s)\n",
999                         nt_errstr(status));
1000                 goto done;
1001         }
1002
1003         /* Set password on machine account - first try level 26 */
1004
1005         init_samr_CryptPasswordEx(r->in.machine_password,
1006                                   &cli->user_session_key,
1007                                   &crypt_pwd_ex);
1008
1009         user_info.info26.password = crypt_pwd_ex;
1010         user_info.info26.password_expired = PASS_DONT_CHANGE_AT_NEXT_LOGON;
1011
1012         status = rpccli_samr_SetUserInfo2(pipe_hnd, mem_ctx,
1013                                           &user_pol,
1014                                           26,
1015                                           &user_info);
1016
1017         if (NT_STATUS_EQUAL(status, NT_STATUS(DCERPC_FAULT_INVALID_TAG))) {
1018
1019                 /* retry with level 24 */
1020
1021                 init_samr_CryptPassword(r->in.machine_password,
1022                                         &cli->user_session_key,
1023                                         &crypt_pwd);
1024
1025                 user_info.info24.password = crypt_pwd;
1026                 user_info.info24.password_expired = PASS_DONT_CHANGE_AT_NEXT_LOGON;
1027
1028                 status = rpccli_samr_SetUserInfo2(pipe_hnd, mem_ctx,
1029                                                   &user_pol,
1030                                                   24,
1031                                                   &user_info);
1032         }
1033
1034         if (!NT_STATUS_IS_OK(status)) {
1035
1036                 rpccli_samr_DeleteUser(pipe_hnd, mem_ctx,
1037                                        &user_pol);
1038
1039                 libnet_join_set_error_string(mem_ctx, r,
1040                         "Failed to set password for machine account (%s)\n",
1041                         nt_errstr(status));
1042                 goto done;
1043         }
1044
1045         status = NT_STATUS_OK;
1046
1047  done:
1048         if (!pipe_hnd) {
1049                 return status;
1050         }
1051
1052         if (is_valid_policy_hnd(&sam_pol)) {
1053                 rpccli_samr_Close(pipe_hnd, mem_ctx, &sam_pol);
1054         }
1055         if (is_valid_policy_hnd(&domain_pol)) {
1056                 rpccli_samr_Close(pipe_hnd, mem_ctx, &domain_pol);
1057         }
1058         if (is_valid_policy_hnd(&user_pol)) {
1059                 rpccli_samr_Close(pipe_hnd, mem_ctx, &user_pol);
1060         }
1061         TALLOC_FREE(pipe_hnd);
1062
1063         return status;
1064 }
1065
1066 /****************************************************************
1067 ****************************************************************/
1068
1069 NTSTATUS libnet_join_ok(const char *netbios_domain_name,
1070                         const char *machine_name,
1071                         const char *dc_name)
1072 {
1073         uint32_t neg_flags = NETLOGON_NEG_AUTH2_ADS_FLAGS;
1074         struct cli_state *cli = NULL;
1075         struct rpc_pipe_client *pipe_hnd = NULL;
1076         struct rpc_pipe_client *netlogon_pipe = NULL;
1077         NTSTATUS status;
1078         char *machine_password = NULL;
1079         char *machine_account = NULL;
1080
1081         if (!dc_name) {
1082                 return NT_STATUS_INVALID_PARAMETER;
1083         }
1084
1085         if (!secrets_init()) {
1086                 return NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
1087         }
1088
1089         machine_password = secrets_fetch_machine_password(netbios_domain_name,
1090                                                           NULL, NULL);
1091         if (!machine_password) {
1092                 return NT_STATUS_NO_TRUST_LSA_SECRET;
1093         }
1094
1095         if (asprintf(&machine_account, "%s$", machine_name) == -1) {
1096                 SAFE_FREE(machine_password);
1097                 return NT_STATUS_NO_MEMORY;
1098         }
1099
1100         status = cli_full_connection(&cli, NULL,
1101                                      dc_name,
1102                                      NULL, 0,
1103                                      "IPC$", "IPC",
1104                                      machine_account,
1105                                      NULL,
1106                                      machine_password,
1107                                      0,
1108                                      Undefined, NULL);
1109         free(machine_account);
1110         free(machine_password);
1111
1112         if (!NT_STATUS_IS_OK(status)) {
1113                 status = cli_full_connection(&cli, NULL,
1114                                              dc_name,
1115                                              NULL, 0,
1116                                              "IPC$", "IPC",
1117                                              "",
1118                                              NULL,
1119                                              "",
1120                                              0,
1121                                              Undefined, NULL);
1122         }
1123
1124         if (!NT_STATUS_IS_OK(status)) {
1125                 return status;
1126         }
1127
1128         status = get_schannel_session_key(cli, netbios_domain_name,
1129                                           &neg_flags, &netlogon_pipe);
1130         if (!NT_STATUS_IS_OK(status)) {
1131                 if (NT_STATUS_EQUAL(status, NT_STATUS_INVALID_NETWORK_RESPONSE)) {
1132                         cli_shutdown(cli);
1133                         return NT_STATUS_OK;
1134                 }
1135
1136                 DEBUG(0,("libnet_join_ok: failed to get schannel session "
1137                         "key from server %s for domain %s. Error was %s\n",
1138                 cli->desthost, netbios_domain_name, nt_errstr(status)));
1139                 cli_shutdown(cli);
1140                 return status;
1141         }
1142
1143         if (!lp_client_schannel()) {
1144                 cli_shutdown(cli);
1145                 return NT_STATUS_OK;
1146         }
1147
1148         status = cli_rpc_pipe_open_schannel_with_key(
1149                 cli, &ndr_table_netlogon.syntax_id, NCACN_NP,
1150                 DCERPC_AUTH_LEVEL_PRIVACY,
1151                 netbios_domain_name, &netlogon_pipe->dc, &pipe_hnd);
1152
1153         cli_shutdown(cli);
1154
1155         if (!NT_STATUS_IS_OK(status)) {
1156                 DEBUG(0,("libnet_join_ok: failed to open schannel session "
1157                         "on netlogon pipe to server %s for domain %s. "
1158                         "Error was %s\n",
1159                         cli->desthost, netbios_domain_name, nt_errstr(status)));
1160                 return status;
1161         }
1162
1163         return NT_STATUS_OK;
1164 }
1165
1166 /****************************************************************
1167 ****************************************************************/
1168
1169 static WERROR libnet_join_post_verify(TALLOC_CTX *mem_ctx,
1170                                       struct libnet_JoinCtx *r)
1171 {
1172         NTSTATUS status;
1173
1174         status = libnet_join_ok(r->out.netbios_domain_name,
1175                                 r->in.machine_name,
1176                                 r->in.dc_name);
1177         if (!NT_STATUS_IS_OK(status)) {
1178                 libnet_join_set_error_string(mem_ctx, r,
1179                         "failed to verify domain membership after joining: %s",
1180                         get_friendly_nt_error_msg(status));
1181                 return WERR_SETUP_NOT_JOINED;
1182         }
1183
1184         return WERR_OK;
1185 }
1186
1187 /****************************************************************
1188 ****************************************************************/
1189
1190 static bool libnet_join_unjoindomain_remove_secrets(TALLOC_CTX *mem_ctx,
1191                                                     struct libnet_UnjoinCtx *r)
1192 {
1193         if (!secrets_delete_machine_password_ex(lp_workgroup())) {
1194                 return false;
1195         }
1196
1197         if (!secrets_delete_domain_sid(lp_workgroup())) {
1198                 return false;
1199         }
1200
1201         return true;
1202 }
1203
1204 /****************************************************************
1205 ****************************************************************/
1206
1207 static NTSTATUS libnet_join_unjoindomain_rpc(TALLOC_CTX *mem_ctx,
1208                                              struct libnet_UnjoinCtx *r)
1209 {
1210         struct cli_state *cli = NULL;
1211         struct rpc_pipe_client *pipe_hnd = NULL;
1212         struct policy_handle sam_pol, domain_pol, user_pol;
1213         NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
1214         char *acct_name;
1215         uint32_t user_rid;
1216         struct lsa_String lsa_acct_name;
1217         struct samr_Ids user_rids;
1218         struct samr_Ids name_types;
1219         union samr_UserInfo *info = NULL;
1220
1221         ZERO_STRUCT(sam_pol);
1222         ZERO_STRUCT(domain_pol);
1223         ZERO_STRUCT(user_pol);
1224
1225         status = libnet_join_connect_dc_ipc(r->in.dc_name,
1226                                             r->in.admin_account,
1227                                             r->in.admin_password,
1228                                             r->in.use_kerberos,
1229                                             &cli);
1230         if (!NT_STATUS_IS_OK(status)) {
1231                 goto done;
1232         }
1233
1234         /* Open the domain */
1235
1236         status = cli_rpc_pipe_open_noauth(cli, &ndr_table_samr.syntax_id,
1237                                           &pipe_hnd);
1238         if (!NT_STATUS_IS_OK(status)) {
1239                 DEBUG(0,("Error connecting to SAM pipe. Error was %s\n",
1240                         nt_errstr(status)));
1241                 goto done;
1242         }
1243
1244         status = rpccli_samr_Connect2(pipe_hnd, mem_ctx,
1245                                       pipe_hnd->desthost,
1246                                       SEC_FLAG_MAXIMUM_ALLOWED,
1247                                       &sam_pol);
1248         if (!NT_STATUS_IS_OK(status)) {
1249                 goto done;
1250         }
1251
1252         status = rpccli_samr_OpenDomain(pipe_hnd, mem_ctx,
1253                                         &sam_pol,
1254                                         SEC_FLAG_MAXIMUM_ALLOWED,
1255                                         r->in.domain_sid,
1256                                         &domain_pol);
1257         if (!NT_STATUS_IS_OK(status)) {
1258                 goto done;
1259         }
1260
1261         /* Create domain user */
1262
1263         acct_name = talloc_asprintf(mem_ctx, "%s$", r->in.machine_name);
1264         strlower_m(acct_name);
1265
1266         init_lsa_String(&lsa_acct_name, acct_name);
1267
1268         status = rpccli_samr_LookupNames(pipe_hnd, mem_ctx,
1269                                          &domain_pol,
1270                                          1,
1271                                          &lsa_acct_name,
1272                                          &user_rids,
1273                                          &name_types);
1274
1275         if (!NT_STATUS_IS_OK(status)) {
1276                 goto done;
1277         }
1278
1279         if (name_types.ids[0] != SID_NAME_USER) {
1280                 DEBUG(0, ("%s is not a user account (type=%d)\n", acct_name,
1281                         name_types.ids[0]));
1282                 status = NT_STATUS_INVALID_WORKSTATION;
1283                 goto done;
1284         }
1285
1286         user_rid = user_rids.ids[0];
1287
1288         /* Open handle on user */
1289
1290         status = rpccli_samr_OpenUser(pipe_hnd, mem_ctx,
1291                                       &domain_pol,
1292                                       SEC_FLAG_MAXIMUM_ALLOWED,
1293                                       user_rid,
1294                                       &user_pol);
1295         if (!NT_STATUS_IS_OK(status)) {
1296                 goto done;
1297         }
1298
1299         /* Get user info */
1300
1301         status = rpccli_samr_QueryUserInfo(pipe_hnd, mem_ctx,
1302                                            &user_pol,
1303                                            16,
1304                                            &info);
1305         if (!NT_STATUS_IS_OK(status)) {
1306                 rpccli_samr_Close(pipe_hnd, mem_ctx, &user_pol);
1307                 goto done;
1308         }
1309
1310         /* now disable and setuser info */
1311
1312         info->info16.acct_flags |= ACB_DISABLED;
1313
1314         status = rpccli_samr_SetUserInfo(pipe_hnd, mem_ctx,
1315                                          &user_pol,
1316                                          16,
1317                                          info);
1318
1319         rpccli_samr_Close(pipe_hnd, mem_ctx, &user_pol);
1320
1321 done:
1322         if (pipe_hnd) {
1323                 if (is_valid_policy_hnd(&domain_pol)) {
1324                         rpccli_samr_Close(pipe_hnd, mem_ctx, &domain_pol);
1325                 }
1326                 if (is_valid_policy_hnd(&sam_pol)) {
1327                         rpccli_samr_Close(pipe_hnd, mem_ctx, &sam_pol);
1328                 }
1329                 TALLOC_FREE(pipe_hnd);
1330         }
1331
1332         if (cli) {
1333                 cli_shutdown(cli);
1334         }
1335
1336         return status;
1337 }
1338
1339 /****************************************************************
1340 ****************************************************************/
1341
1342 static WERROR do_join_modify_vals_config(struct libnet_JoinCtx *r)
1343 {
1344         WERROR werr;
1345         struct smbconf_ctx *ctx;
1346
1347         werr = smbconf_init_reg(r, &ctx, NULL);
1348         if (!W_ERROR_IS_OK(werr)) {
1349                 goto done;
1350         }
1351
1352         if (!(r->in.join_flags & WKSSVC_JOIN_FLAGS_JOIN_TYPE)) {
1353
1354                 werr = smbconf_set_global_parameter(ctx, "security", "user");
1355                 W_ERROR_NOT_OK_GOTO_DONE(werr);
1356
1357                 werr = smbconf_set_global_parameter(ctx, "workgroup",
1358                                                     r->in.domain_name);
1359
1360                 smbconf_delete_global_parameter(ctx, "realm");
1361                 goto done;
1362         }
1363
1364         werr = smbconf_set_global_parameter(ctx, "security", "domain");
1365         W_ERROR_NOT_OK_GOTO_DONE(werr);
1366
1367         werr = smbconf_set_global_parameter(ctx, "workgroup",
1368                                             r->out.netbios_domain_name);
1369         W_ERROR_NOT_OK_GOTO_DONE(werr);
1370
1371         if (r->out.domain_is_ad) {
1372                 werr = smbconf_set_global_parameter(ctx, "security", "ads");
1373                 W_ERROR_NOT_OK_GOTO_DONE(werr);
1374
1375                 werr = smbconf_set_global_parameter(ctx, "realm",
1376                                                     r->out.dns_domain_name);
1377                 W_ERROR_NOT_OK_GOTO_DONE(werr);
1378         }
1379
1380  done:
1381         smbconf_shutdown(ctx);
1382         return werr;
1383 }
1384
1385 /****************************************************************
1386 ****************************************************************/
1387
1388 static WERROR do_unjoin_modify_vals_config(struct libnet_UnjoinCtx *r)
1389 {
1390         WERROR werr = WERR_OK;
1391         struct smbconf_ctx *ctx;
1392
1393         werr = smbconf_init_reg(r, &ctx, NULL);
1394         if (!W_ERROR_IS_OK(werr)) {
1395                 goto done;
1396         }
1397
1398         if (r->in.unjoin_flags & WKSSVC_JOIN_FLAGS_JOIN_TYPE) {
1399
1400                 werr = smbconf_set_global_parameter(ctx, "security", "user");
1401                 W_ERROR_NOT_OK_GOTO_DONE(werr);
1402
1403                 werr = smbconf_delete_global_parameter(ctx, "workgroup");
1404                 W_ERROR_NOT_OK_GOTO_DONE(werr);
1405
1406                 smbconf_delete_global_parameter(ctx, "realm");
1407         }
1408
1409  done:
1410         smbconf_shutdown(ctx);
1411         return werr;
1412 }
1413
1414 /****************************************************************
1415 ****************************************************************/
1416
1417 static WERROR do_JoinConfig(struct libnet_JoinCtx *r)
1418 {
1419         WERROR werr;
1420
1421         if (!W_ERROR_IS_OK(r->out.result)) {
1422                 return r->out.result;
1423         }
1424
1425         if (!r->in.modify_config) {
1426                 return WERR_OK;
1427         }
1428
1429         werr = do_join_modify_vals_config(r);
1430         if (!W_ERROR_IS_OK(werr)) {
1431                 return werr;
1432         }
1433
1434         lp_load(get_dyn_CONFIGFILE(),true,false,false,true);
1435
1436         r->out.modified_config = true;
1437         r->out.result = werr;
1438
1439         return werr;
1440 }
1441
1442 /****************************************************************
1443 ****************************************************************/
1444
1445 static WERROR libnet_unjoin_config(struct libnet_UnjoinCtx *r)
1446 {
1447         WERROR werr;
1448
1449         if (!W_ERROR_IS_OK(r->out.result)) {
1450                 return r->out.result;
1451         }
1452
1453         if (!r->in.modify_config) {
1454                 return WERR_OK;
1455         }
1456
1457         werr = do_unjoin_modify_vals_config(r);
1458         if (!W_ERROR_IS_OK(werr)) {
1459                 return werr;
1460         }
1461
1462         lp_load(get_dyn_CONFIGFILE(),true,false,false,true);
1463
1464         r->out.modified_config = true;
1465         r->out.result = werr;
1466
1467         return werr;
1468 }
1469
1470 /****************************************************************
1471 ****************************************************************/
1472
1473 static bool libnet_parse_domain_dc(TALLOC_CTX *mem_ctx,
1474                                    const char *domain_str,
1475                                    const char **domain_p,
1476                                    const char **dc_p)
1477 {
1478         char *domain = NULL;
1479         char *dc = NULL;
1480         const char *p = NULL;
1481
1482         if (!domain_str || !domain_p || !dc_p) {
1483                 return false;
1484         }
1485
1486         p = strchr_m(domain_str, '\\');
1487
1488         if (p != NULL) {
1489                 domain = talloc_strndup(mem_ctx, domain_str,
1490                                          PTR_DIFF(p, domain_str));
1491                 dc = talloc_strdup(mem_ctx, p+1);
1492                 if (!dc) {
1493                         return false;
1494                 }
1495         } else {
1496                 domain = talloc_strdup(mem_ctx, domain_str);
1497                 dc = NULL;
1498         }
1499         if (!domain) {
1500                 return false;
1501         }
1502
1503         *domain_p = domain;
1504
1505         if (!*dc_p && dc) {
1506                 *dc_p = dc;
1507         }
1508
1509         return true;
1510 }
1511
1512 /****************************************************************
1513 ****************************************************************/
1514
1515 static WERROR libnet_join_pre_processing(TALLOC_CTX *mem_ctx,
1516                                          struct libnet_JoinCtx *r)
1517 {
1518         if (!r->in.domain_name) {
1519                 libnet_join_set_error_string(mem_ctx, r,
1520                         "No domain name defined");
1521                 return WERR_INVALID_PARAM;
1522         }
1523
1524         if (!libnet_parse_domain_dc(mem_ctx, r->in.domain_name,
1525                                     &r->in.domain_name,
1526                                     &r->in.dc_name)) {
1527                 libnet_join_set_error_string(mem_ctx, r,
1528                         "Failed to parse domain name");
1529                 return WERR_INVALID_PARAM;
1530         }
1531
1532         if (IS_DC) {
1533                 return WERR_SETUP_DOMAIN_CONTROLLER;
1534         }
1535
1536         if (!secrets_init()) {
1537                 libnet_join_set_error_string(mem_ctx, r,
1538                         "Unable to open secrets database");
1539                 return WERR_CAN_NOT_COMPLETE;
1540         }
1541
1542         return WERR_OK;
1543 }
1544
1545 /****************************************************************
1546 ****************************************************************/
1547
1548 static void libnet_join_add_dom_rids_to_builtins(struct dom_sid *domain_sid)
1549 {
1550         NTSTATUS status;
1551
1552         /* Try adding dom admins to builtin\admins. Only log failures. */
1553         status = create_builtin_administrators(domain_sid);
1554         if (NT_STATUS_EQUAL(status, NT_STATUS_PROTOCOL_UNREACHABLE)) {
1555                 DEBUG(10,("Unable to auto-add domain administrators to "
1556                           "BUILTIN\\Administrators during join because "
1557                           "winbindd must be running."));
1558         } else if (!NT_STATUS_IS_OK(status)) {
1559                 DEBUG(5, ("Failed to auto-add domain administrators to "
1560                           "BUILTIN\\Administrators during join: %s\n",
1561                           nt_errstr(status)));
1562         }
1563
1564         /* Try adding dom users to builtin\users. Only log failures. */
1565         status = create_builtin_users(domain_sid);
1566         if (NT_STATUS_EQUAL(status, NT_STATUS_PROTOCOL_UNREACHABLE)) {
1567                 DEBUG(10,("Unable to auto-add domain users to BUILTIN\\users "
1568                           "during join because winbindd must be running."));
1569         } else if (!NT_STATUS_IS_OK(status)) {
1570                 DEBUG(5, ("Failed to auto-add domain administrators to "
1571                           "BUILTIN\\Administrators during join: %s\n",
1572                           nt_errstr(status)));
1573         }
1574 }
1575
1576 /****************************************************************
1577 ****************************************************************/
1578
1579 static WERROR libnet_join_post_processing(TALLOC_CTX *mem_ctx,
1580                                           struct libnet_JoinCtx *r)
1581 {
1582         WERROR werr;
1583
1584         if (!W_ERROR_IS_OK(r->out.result)) {
1585                 return r->out.result;
1586         }
1587
1588         werr = do_JoinConfig(r);
1589         if (!W_ERROR_IS_OK(werr)) {
1590                 return werr;
1591         }
1592
1593         if (!(r->in.join_flags & WKSSVC_JOIN_FLAGS_JOIN_TYPE)) {
1594                 return WERR_OK;
1595         }
1596
1597         saf_join_store(r->out.netbios_domain_name, r->in.dc_name);
1598         if (r->out.dns_domain_name) {
1599                 saf_join_store(r->out.dns_domain_name, r->in.dc_name);
1600         }
1601
1602 #ifdef WITH_ADS
1603         if (r->out.domain_is_ad &&
1604             !(r->in.join_flags & WKSSVC_JOIN_FLAGS_JOIN_UNSECURE)) {
1605                 ADS_STATUS ads_status;
1606
1607                 ads_status  = libnet_join_post_processing_ads(mem_ctx, r);
1608                 if (!ADS_ERR_OK(ads_status)) {
1609                         return WERR_GENERAL_FAILURE;
1610                 }
1611         }
1612 #endif /* WITH_ADS */
1613
1614         libnet_join_add_dom_rids_to_builtins(r->out.domain_sid);
1615
1616         return WERR_OK;
1617 }
1618
1619 /****************************************************************
1620 ****************************************************************/
1621
1622 static int libnet_destroy_JoinCtx(struct libnet_JoinCtx *r)
1623 {
1624         const char *krb5_cc_env = NULL;
1625
1626         if (r->in.ads) {
1627                 ads_destroy(&r->in.ads);
1628         }
1629
1630         krb5_cc_env = getenv(KRB5_ENV_CCNAME);
1631         if (krb5_cc_env && StrCaseCmp(krb5_cc_env, "MEMORY:libnetjoin")) {
1632                 unsetenv(KRB5_ENV_CCNAME);
1633         }
1634
1635         return 0;
1636 }
1637
1638 /****************************************************************
1639 ****************************************************************/
1640
1641 static int libnet_destroy_UnjoinCtx(struct libnet_UnjoinCtx *r)
1642 {
1643         const char *krb5_cc_env = NULL;
1644
1645         if (r->in.ads) {
1646                 ads_destroy(&r->in.ads);
1647         }
1648
1649         krb5_cc_env = getenv(KRB5_ENV_CCNAME);
1650         if (krb5_cc_env && StrCaseCmp(krb5_cc_env, "MEMORY:libnetjoin")) {
1651                 unsetenv(KRB5_ENV_CCNAME);
1652         }
1653
1654         return 0;
1655 }
1656
1657 /****************************************************************
1658 ****************************************************************/
1659
1660 WERROR libnet_init_JoinCtx(TALLOC_CTX *mem_ctx,
1661                            struct libnet_JoinCtx **r)
1662 {
1663         struct libnet_JoinCtx *ctx;
1664         const char *krb5_cc_env = NULL;
1665
1666         ctx = talloc_zero(mem_ctx, struct libnet_JoinCtx);
1667         if (!ctx) {
1668                 return WERR_NOMEM;
1669         }
1670
1671         talloc_set_destructor(ctx, libnet_destroy_JoinCtx);
1672
1673         ctx->in.machine_name = talloc_strdup(mem_ctx, global_myname());
1674         W_ERROR_HAVE_NO_MEMORY(ctx->in.machine_name);
1675
1676         krb5_cc_env = getenv(KRB5_ENV_CCNAME);
1677         if (!krb5_cc_env || (strlen(krb5_cc_env) == 0)) {
1678                 krb5_cc_env = talloc_strdup(mem_ctx, "MEMORY:libnetjoin");
1679                 W_ERROR_HAVE_NO_MEMORY(krb5_cc_env);
1680                 setenv(KRB5_ENV_CCNAME, krb5_cc_env, 1);
1681         }
1682
1683         ctx->in.secure_channel_type = SEC_CHAN_WKSTA;
1684
1685         *r = ctx;
1686
1687         return WERR_OK;
1688 }
1689
1690 /****************************************************************
1691 ****************************************************************/
1692
1693 WERROR libnet_init_UnjoinCtx(TALLOC_CTX *mem_ctx,
1694                              struct libnet_UnjoinCtx **r)
1695 {
1696         struct libnet_UnjoinCtx *ctx;
1697         const char *krb5_cc_env = NULL;
1698
1699         ctx = talloc_zero(mem_ctx, struct libnet_UnjoinCtx);
1700         if (!ctx) {
1701                 return WERR_NOMEM;
1702         }
1703
1704         talloc_set_destructor(ctx, libnet_destroy_UnjoinCtx);
1705
1706         ctx->in.machine_name = talloc_strdup(mem_ctx, global_myname());
1707         W_ERROR_HAVE_NO_MEMORY(ctx->in.machine_name);
1708
1709         krb5_cc_env = getenv(KRB5_ENV_CCNAME);
1710         if (!krb5_cc_env || (strlen(krb5_cc_env) == 0)) {
1711                 krb5_cc_env = talloc_strdup(mem_ctx, "MEMORY:libnetjoin");
1712                 W_ERROR_HAVE_NO_MEMORY(krb5_cc_env);
1713                 setenv(KRB5_ENV_CCNAME, krb5_cc_env, 1);
1714         }
1715
1716         *r = ctx;
1717
1718         return WERR_OK;
1719 }
1720
1721 /****************************************************************
1722 ****************************************************************/
1723
1724 static WERROR libnet_join_check_config(TALLOC_CTX *mem_ctx,
1725                                        struct libnet_JoinCtx *r)
1726 {
1727         bool valid_security = false;
1728         bool valid_workgroup = false;
1729         bool valid_realm = false;
1730
1731         /* check if configuration is already set correctly */
1732
1733         valid_workgroup = strequal(lp_workgroup(), r->out.netbios_domain_name);
1734
1735         switch (r->out.domain_is_ad) {
1736                 case false:
1737                         valid_security = (lp_security() == SEC_DOMAIN);
1738                         if (valid_workgroup && valid_security) {
1739                                 /* nothing to be done */
1740                                 return WERR_OK;
1741                         }
1742                         break;
1743                 case true:
1744                         valid_realm = strequal(lp_realm(), r->out.dns_domain_name);
1745                         switch (lp_security()) {
1746                         case SEC_DOMAIN:
1747                         case SEC_ADS:
1748                                 valid_security = true;
1749                         }
1750
1751                         if (valid_workgroup && valid_realm && valid_security) {
1752                                 /* nothing to be done */
1753                                 return WERR_OK;
1754                         }
1755                         break;
1756         }
1757
1758         /* check if we are supposed to manipulate configuration */
1759
1760         if (!r->in.modify_config) {
1761
1762                 char *wrong_conf = talloc_strdup(mem_ctx, "");
1763
1764                 if (!valid_workgroup) {
1765                         wrong_conf = talloc_asprintf_append(wrong_conf,
1766                                 "\"workgroup\" set to '%s', should be '%s'",
1767                                 lp_workgroup(), r->out.netbios_domain_name);
1768                         W_ERROR_HAVE_NO_MEMORY(wrong_conf);
1769                 }
1770
1771                 if (!valid_realm) {
1772                         wrong_conf = talloc_asprintf_append(wrong_conf,
1773                                 "\"realm\" set to '%s', should be '%s'",
1774                                 lp_realm(), r->out.dns_domain_name);
1775                         W_ERROR_HAVE_NO_MEMORY(wrong_conf);
1776                 }
1777
1778                 if (!valid_security) {
1779                         const char *sec = NULL;
1780                         switch (lp_security()) {
1781                         case SEC_SHARE: sec = "share"; break;
1782                         case SEC_USER:  sec = "user"; break;
1783                         case SEC_DOMAIN: sec = "domain"; break;
1784                         case SEC_ADS: sec = "ads"; break;
1785                         }
1786                         wrong_conf = talloc_asprintf_append(wrong_conf,
1787                                 "\"security\" set to '%s', should be %s",
1788                                 sec, r->out.domain_is_ad ?
1789                                 "either 'domain' or 'ads'" : "'domain'");
1790                         W_ERROR_HAVE_NO_MEMORY(wrong_conf);
1791                 }
1792
1793                 libnet_join_set_error_string(mem_ctx, r,
1794                         "Invalid configuration (%s) and configuration modification "
1795                         "was not requested", wrong_conf);
1796                 return WERR_CAN_NOT_COMPLETE;
1797         }
1798
1799         /* check if we are able to manipulate configuration */
1800
1801         if (!lp_config_backend_is_registry()) {
1802                 libnet_join_set_error_string(mem_ctx, r,
1803                         "Configuration manipulation requested but not "
1804                         "supported by backend");
1805                 return WERR_NOT_SUPPORTED;
1806         }
1807
1808         return WERR_OK;
1809 }
1810
1811 /****************************************************************
1812 ****************************************************************/
1813
1814 static WERROR libnet_DomainJoin(TALLOC_CTX *mem_ctx,
1815                                 struct libnet_JoinCtx *r)
1816 {
1817         NTSTATUS status;
1818         WERROR werr;
1819         struct cli_state *cli = NULL;
1820 #ifdef WITH_ADS
1821         ADS_STATUS ads_status;
1822 #endif /* WITH_ADS */
1823
1824         if (!r->in.dc_name) {
1825                 struct netr_DsRGetDCNameInfo *info;
1826                 const char *dc;
1827                 status = dsgetdcname(mem_ctx,
1828                                      r->in.msg_ctx,
1829                                      r->in.domain_name,
1830                                      NULL,
1831                                      NULL,
1832                                      DS_FORCE_REDISCOVERY |
1833                                      DS_DIRECTORY_SERVICE_REQUIRED |
1834                                      DS_WRITABLE_REQUIRED |
1835                                      DS_RETURN_DNS_NAME,
1836                                      &info);
1837                 if (!NT_STATUS_IS_OK(status)) {
1838                         libnet_join_set_error_string(mem_ctx, r,
1839                                 "failed to find DC for domain %s",
1840                                 r->in.domain_name,
1841                                 get_friendly_nt_error_msg(status));
1842                         return WERR_DCNOTFOUND;
1843                 }
1844
1845                 dc = strip_hostname(info->dc_unc);
1846                 r->in.dc_name = talloc_strdup(mem_ctx, dc);
1847                 W_ERROR_HAVE_NO_MEMORY(r->in.dc_name);
1848         }
1849
1850         status = libnet_join_lookup_dc_rpc(mem_ctx, r, &cli);
1851         if (!NT_STATUS_IS_OK(status)) {
1852                 libnet_join_set_error_string(mem_ctx, r,
1853                         "failed to lookup DC info for domain '%s' over rpc: %s",
1854                         r->in.domain_name, get_friendly_nt_error_msg(status));
1855                 return ntstatus_to_werror(status);
1856         }
1857
1858         werr = libnet_join_check_config(mem_ctx, r);
1859         if (!W_ERROR_IS_OK(werr)) {
1860                 goto done;
1861         }
1862
1863 #ifdef WITH_ADS
1864         if (r->out.domain_is_ad && r->in.account_ou &&
1865             !(r->in.join_flags & WKSSVC_JOIN_FLAGS_JOIN_UNSECURE)) {
1866
1867                 ads_status = libnet_join_connect_ads(mem_ctx, r);
1868                 if (!ADS_ERR_OK(ads_status)) {
1869                         return WERR_DEFAULT_JOIN_REQUIRED;
1870                 }
1871
1872                 ads_status = libnet_join_precreate_machine_acct(mem_ctx, r);
1873                 if (!ADS_ERR_OK(ads_status)) {
1874                         libnet_join_set_error_string(mem_ctx, r,
1875                                 "failed to precreate account in ou %s: %s",
1876                                 r->in.account_ou,
1877                                 ads_errstr(ads_status));
1878                         return WERR_DEFAULT_JOIN_REQUIRED;
1879                 }
1880
1881                 r->in.join_flags &= ~WKSSVC_JOIN_FLAGS_ACCOUNT_CREATE;
1882         }
1883 #endif /* WITH_ADS */
1884
1885         if ((r->in.join_flags & WKSSVC_JOIN_FLAGS_JOIN_UNSECURE) &&
1886             (r->in.join_flags & WKSSVC_JOIN_FLAGS_MACHINE_PWD_PASSED)) {
1887                 status = libnet_join_joindomain_rpc_unsecure(mem_ctx, r, cli);
1888         } else {
1889                 status = libnet_join_joindomain_rpc(mem_ctx, r, cli);
1890         }
1891         if (!NT_STATUS_IS_OK(status)) {
1892                 libnet_join_set_error_string(mem_ctx, r,
1893                         "failed to join domain '%s' over rpc: %s",
1894                         r->in.domain_name, get_friendly_nt_error_msg(status));
1895                 if (NT_STATUS_EQUAL(status, NT_STATUS_USER_EXISTS)) {
1896                         return WERR_SETUP_ALREADY_JOINED;
1897                 }
1898                 werr = ntstatus_to_werror(status);
1899                 goto done;
1900         }
1901
1902         if (!libnet_join_joindomain_store_secrets(mem_ctx, r)) {
1903                 werr = WERR_SETUP_NOT_JOINED;
1904                 goto done;
1905         }
1906
1907         werr = WERR_OK;
1908
1909  done:
1910         if (cli) {
1911                 cli_shutdown(cli);
1912         }
1913
1914         return werr;
1915 }
1916
1917 /****************************************************************
1918 ****************************************************************/
1919
1920 static WERROR libnet_join_rollback(TALLOC_CTX *mem_ctx,
1921                                    struct libnet_JoinCtx *r)
1922 {
1923         WERROR werr;
1924         struct libnet_UnjoinCtx *u = NULL;
1925
1926         werr = libnet_init_UnjoinCtx(mem_ctx, &u);
1927         if (!W_ERROR_IS_OK(werr)) {
1928                 return werr;
1929         }
1930
1931         u->in.debug             = r->in.debug;
1932         u->in.dc_name           = r->in.dc_name;
1933         u->in.domain_name       = r->in.domain_name;
1934         u->in.admin_account     = r->in.admin_account;
1935         u->in.admin_password    = r->in.admin_password;
1936         u->in.modify_config     = r->in.modify_config;
1937         u->in.unjoin_flags      = WKSSVC_JOIN_FLAGS_JOIN_TYPE |
1938                                   WKSSVC_JOIN_FLAGS_ACCOUNT_DELETE;
1939
1940         werr = libnet_Unjoin(mem_ctx, u);
1941         TALLOC_FREE(u);
1942
1943         return werr;
1944 }
1945
1946 /****************************************************************
1947 ****************************************************************/
1948
1949 WERROR libnet_Join(TALLOC_CTX *mem_ctx,
1950                    struct libnet_JoinCtx *r)
1951 {
1952         WERROR werr;
1953
1954         if (r->in.debug) {
1955                 LIBNET_JOIN_IN_DUMP_CTX(mem_ctx, r);
1956         }
1957
1958         werr = libnet_join_pre_processing(mem_ctx, r);
1959         if (!W_ERROR_IS_OK(werr)) {
1960                 goto done;
1961         }
1962
1963         if (r->in.join_flags & WKSSVC_JOIN_FLAGS_JOIN_TYPE) {
1964                 werr = libnet_DomainJoin(mem_ctx, r);
1965                 if (!W_ERROR_IS_OK(werr)) {
1966                         goto done;
1967                 }
1968         }
1969
1970         werr = libnet_join_post_processing(mem_ctx, r);
1971         if (!W_ERROR_IS_OK(werr)) {
1972                 goto done;
1973         }
1974
1975         if (r->in.join_flags & WKSSVC_JOIN_FLAGS_JOIN_TYPE) {
1976                 werr = libnet_join_post_verify(mem_ctx, r);
1977                 if (!W_ERROR_IS_OK(werr)) {
1978                         libnet_join_rollback(mem_ctx, r);
1979                 }
1980         }
1981
1982  done:
1983         r->out.result = werr;
1984
1985         if (r->in.debug) {
1986                 LIBNET_JOIN_OUT_DUMP_CTX(mem_ctx, r);
1987         }
1988         return werr;
1989 }
1990
1991 /****************************************************************
1992 ****************************************************************/
1993
1994 static WERROR libnet_DomainUnjoin(TALLOC_CTX *mem_ctx,
1995                                   struct libnet_UnjoinCtx *r)
1996 {
1997         NTSTATUS status;
1998
1999         if (!r->in.domain_sid) {
2000                 struct dom_sid sid;
2001                 if (!secrets_fetch_domain_sid(lp_workgroup(), &sid)) {
2002                         libnet_unjoin_set_error_string(mem_ctx, r,
2003                                 "Unable to fetch domain sid: are we joined?");
2004                         return WERR_SETUP_NOT_JOINED;
2005                 }
2006                 r->in.domain_sid = sid_dup_talloc(mem_ctx, &sid);
2007                 W_ERROR_HAVE_NO_MEMORY(r->in.domain_sid);
2008         }
2009
2010         if (!(r->in.unjoin_flags & WKSSVC_JOIN_FLAGS_ACCOUNT_DELETE) && 
2011             !r->in.delete_machine_account) {
2012                 libnet_join_unjoindomain_remove_secrets(mem_ctx, r);
2013                 return WERR_OK;
2014         }
2015
2016         if (!r->in.dc_name) {
2017                 struct netr_DsRGetDCNameInfo *info;
2018                 const char *dc;
2019                 status = dsgetdcname(mem_ctx,
2020                                      r->in.msg_ctx,
2021                                      r->in.domain_name,
2022                                      NULL,
2023                                      NULL,
2024                                      DS_DIRECTORY_SERVICE_REQUIRED |
2025                                      DS_WRITABLE_REQUIRED |
2026                                      DS_RETURN_DNS_NAME,
2027                                      &info);
2028                 if (!NT_STATUS_IS_OK(status)) {
2029                         libnet_unjoin_set_error_string(mem_ctx, r,
2030                                 "failed to find DC for domain %s",
2031                                 r->in.domain_name,
2032                                 get_friendly_nt_error_msg(status));
2033                         return WERR_DCNOTFOUND;
2034                 }
2035
2036                 dc = strip_hostname(info->dc_unc);
2037                 r->in.dc_name = talloc_strdup(mem_ctx, dc);
2038                 W_ERROR_HAVE_NO_MEMORY(r->in.dc_name);
2039         }
2040
2041 #ifdef WITH_ADS
2042         /* for net ads leave, try to delete the account.  If it works, 
2043            no sense in disabling.  If it fails, we can still try to 
2044            disable it. jmcd */
2045            
2046         if (r->in.delete_machine_account) {
2047                 ADS_STATUS ads_status;
2048                 ads_status = libnet_unjoin_connect_ads(mem_ctx, r);
2049                 if (ADS_ERR_OK(ads_status)) {
2050                         /* dirty hack */
2051                         r->out.dns_domain_name = 
2052                                 talloc_strdup(mem_ctx,
2053                                               r->in.ads->server.realm);
2054                         ads_status = 
2055                                 libnet_unjoin_remove_machine_acct(mem_ctx, r);
2056                 }
2057                 if (!ADS_ERR_OK(ads_status)) {
2058                         libnet_unjoin_set_error_string(mem_ctx, r,
2059                                 "failed to remove machine account from AD: %s",
2060                                 ads_errstr(ads_status));
2061                 } else {
2062                         r->out.deleted_machine_account = true;
2063                         W_ERROR_HAVE_NO_MEMORY(r->out.dns_domain_name);
2064                         libnet_join_unjoindomain_remove_secrets(mem_ctx, r);
2065                         return WERR_OK;
2066                 }
2067         }
2068 #endif /* WITH_ADS */
2069
2070         /* The WKSSVC_JOIN_FLAGS_ACCOUNT_DELETE flag really means 
2071            "disable".  */
2072         if (r->in.unjoin_flags & WKSSVC_JOIN_FLAGS_ACCOUNT_DELETE) {
2073                 status = libnet_join_unjoindomain_rpc(mem_ctx, r);
2074                 if (!NT_STATUS_IS_OK(status)) {
2075                         libnet_unjoin_set_error_string(mem_ctx, r,
2076                                 "failed to disable machine account via rpc: %s",
2077                                 get_friendly_nt_error_msg(status));
2078                         if (NT_STATUS_EQUAL(status, NT_STATUS_NO_SUCH_USER)) {
2079                                 return WERR_SETUP_NOT_JOINED;
2080                         }
2081                         return ntstatus_to_werror(status);
2082                 }
2083                 
2084                 r->out.disabled_machine_account = true;
2085         }
2086
2087         /* If disable succeeded or was not requested at all, we 
2088            should be getting rid of our end of things */
2089
2090         libnet_join_unjoindomain_remove_secrets(mem_ctx, r);
2091
2092         return WERR_OK;
2093 }
2094
2095 /****************************************************************
2096 ****************************************************************/
2097
2098 static WERROR libnet_unjoin_pre_processing(TALLOC_CTX *mem_ctx,
2099                                            struct libnet_UnjoinCtx *r)
2100 {
2101         if (!r->in.domain_name) {
2102                 libnet_unjoin_set_error_string(mem_ctx, r,
2103                         "No domain name defined");
2104                 return WERR_INVALID_PARAM;
2105         }
2106
2107         if (!libnet_parse_domain_dc(mem_ctx, r->in.domain_name,
2108                                     &r->in.domain_name,
2109                                     &r->in.dc_name)) {
2110                 libnet_unjoin_set_error_string(mem_ctx, r,
2111                         "Failed to parse domain name");
2112                 return WERR_INVALID_PARAM;
2113         }
2114
2115         if (IS_DC) {
2116                 return WERR_SETUP_DOMAIN_CONTROLLER;
2117         }
2118
2119         if (!secrets_init()) {
2120                 libnet_unjoin_set_error_string(mem_ctx, r,
2121                         "Unable to open secrets database");
2122                 return WERR_CAN_NOT_COMPLETE;
2123         }
2124
2125         return WERR_OK;
2126 }
2127
2128 /****************************************************************
2129 ****************************************************************/
2130
2131 static WERROR libnet_unjoin_post_processing(TALLOC_CTX *mem_ctx,
2132                                             struct libnet_UnjoinCtx *r)
2133 {
2134         saf_delete(r->out.netbios_domain_name);
2135         saf_delete(r->out.dns_domain_name);
2136
2137         return libnet_unjoin_config(r);
2138 }
2139
2140 /****************************************************************
2141 ****************************************************************/
2142
2143 WERROR libnet_Unjoin(TALLOC_CTX *mem_ctx,
2144                      struct libnet_UnjoinCtx *r)
2145 {
2146         WERROR werr;
2147
2148         if (r->in.debug) {
2149                 LIBNET_UNJOIN_IN_DUMP_CTX(mem_ctx, r);
2150         }
2151
2152         werr = libnet_unjoin_pre_processing(mem_ctx, r);
2153         if (!W_ERROR_IS_OK(werr)) {
2154                 goto done;
2155         }
2156
2157         if (r->in.unjoin_flags & WKSSVC_JOIN_FLAGS_JOIN_TYPE) {
2158                 werr = libnet_DomainUnjoin(mem_ctx, r);
2159                 if (!W_ERROR_IS_OK(werr)) {
2160                         libnet_unjoin_config(r);
2161                         goto done;
2162                 }
2163         }
2164
2165         werr = libnet_unjoin_post_processing(mem_ctx, r);
2166         if (!W_ERROR_IS_OK(werr)) {
2167                 goto done;
2168         }
2169
2170  done:
2171         r->out.result = werr;
2172
2173         if (r->in.debug) {
2174                 LIBNET_UNJOIN_OUT_DUMP_CTX(mem_ctx, r);
2175         }
2176
2177         return werr;
2178 }