f855a57f32927b8f7aee585350218194cf15174e
[jra/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
24 /****************************************************************
25 ****************************************************************/
26
27 #define LIBNET_JOIN_DUMP_CTX(ctx, r, f) \
28         do { \
29                 char *str = NULL; \
30                 str = NDR_PRINT_FUNCTION_STRING(ctx, libnet_JoinCtx, f, r); \
31                 DEBUG(1,("libnet_Join:\n%s", str)); \
32                 talloc_free(str); \
33         } while (0)
34
35 #define LIBNET_JOIN_IN_DUMP_CTX(ctx, r) \
36         LIBNET_JOIN_DUMP_CTX(ctx, r, NDR_IN | NDR_SET_VALUES)
37 #define LIBNET_JOIN_OUT_DUMP_CTX(ctx, r) \
38         LIBNET_JOIN_DUMP_CTX(ctx, r, NDR_OUT)
39
40 #define LIBNET_UNJOIN_DUMP_CTX(ctx, r, f) \
41         do { \
42                 char *str = NULL; \
43                 str = NDR_PRINT_FUNCTION_STRING(ctx, libnet_UnjoinCtx, f, r); \
44                 DEBUG(1,("libnet_Unjoin:\n%s", str)); \
45                 talloc_free(str); \
46         } while (0)
47
48 #define LIBNET_UNJOIN_IN_DUMP_CTX(ctx, r) \
49         LIBNET_UNJOIN_DUMP_CTX(ctx, r, NDR_IN | NDR_SET_VALUES)
50 #define LIBNET_UNJOIN_OUT_DUMP_CTX(ctx, r) \
51         LIBNET_UNJOIN_DUMP_CTX(ctx, r, NDR_OUT)
52
53 static void init_lsa_String(struct lsa_String *name, const char *s)
54 {
55         name->string = s;
56 }
57
58 /****************************************************************
59 ****************************************************************/
60
61 static void libnet_join_set_error_string(TALLOC_CTX *mem_ctx,
62                                          struct libnet_JoinCtx *r,
63                                          const char *format, ...)
64 {
65         va_list args;
66
67         if (r->out.error_string) {
68                 return;
69         }
70
71         va_start(args, format);
72         r->out.error_string = talloc_vasprintf(mem_ctx, format, args);
73         va_end(args);
74 }
75
76 /****************************************************************
77 ****************************************************************/
78
79 static void libnet_unjoin_set_error_string(TALLOC_CTX *mem_ctx,
80                                            struct libnet_UnjoinCtx *r,
81                                            const char *format, ...)
82 {
83         va_list args;
84
85         if (r->out.error_string) {
86                 return;
87         }
88
89         va_start(args, format);
90         r->out.error_string = talloc_vasprintf(mem_ctx, format, args);
91         va_end(args);
92 }
93
94 #ifdef WITH_ADS
95
96 /****************************************************************
97 ****************************************************************/
98
99 static ADS_STATUS libnet_connect_ads(const char *dns_domain_name,
100                                      const char *netbios_domain_name,
101                                      const char *dc_name,
102                                      const char *user_name,
103                                      const char *password,
104                                      ADS_STRUCT **ads)
105 {
106         ADS_STATUS status;
107         ADS_STRUCT *my_ads = NULL;
108
109         my_ads = ads_init(dns_domain_name,
110                           netbios_domain_name,
111                           dc_name);
112         if (!my_ads) {
113                 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
114         }
115
116         if (user_name) {
117                 SAFE_FREE(my_ads->auth.user_name);
118                 my_ads->auth.user_name = SMB_STRDUP(user_name);
119         }
120
121         if (password) {
122                 SAFE_FREE(my_ads->auth.password);
123                 my_ads->auth.password = SMB_STRDUP(password);
124         }
125
126         status = ads_connect(my_ads);
127         if (!ADS_ERR_OK(status)) {
128                 ads_destroy(&my_ads);
129                 return status;
130         }
131
132         *ads = my_ads;
133         return ADS_SUCCESS;
134 }
135
136 /****************************************************************
137 ****************************************************************/
138
139 static ADS_STATUS libnet_join_connect_ads(TALLOC_CTX *mem_ctx,
140                                           struct libnet_JoinCtx *r)
141 {
142         ADS_STATUS status;
143
144         status = libnet_connect_ads(r->in.domain_name,
145                                     r->in.domain_name,
146                                     r->in.dc_name,
147                                     r->in.admin_account,
148                                     r->in.admin_password,
149                                     &r->in.ads);
150         if (!ADS_ERR_OK(status)) {
151                 libnet_join_set_error_string(mem_ctx, r,
152                         "failed to connect to AD: %s",
153                         ads_errstr(status));
154         }
155
156         return status;
157 }
158
159 /****************************************************************
160 ****************************************************************/
161
162 static ADS_STATUS libnet_unjoin_connect_ads(TALLOC_CTX *mem_ctx,
163                                             struct libnet_UnjoinCtx *r)
164 {
165         ADS_STATUS status;
166
167         status = libnet_connect_ads(r->in.domain_name,
168                                     r->in.domain_name,
169                                     r->in.dc_name,
170                                     r->in.admin_account,
171                                     r->in.admin_password,
172                                     &r->in.ads);
173         if (!ADS_ERR_OK(status)) {
174                 libnet_unjoin_set_error_string(mem_ctx, r,
175                         "failed to connect to AD: %s",
176                         ads_errstr(status));
177         }
178
179         return status;
180 }
181
182 /****************************************************************
183 ****************************************************************/
184
185 static ADS_STATUS libnet_join_precreate_machine_acct(TALLOC_CTX *mem_ctx,
186                                                      struct libnet_JoinCtx *r)
187 {
188         ADS_STATUS status;
189         LDAPMessage *res = NULL;
190         const char *attrs[] = { "dn", NULL };
191
192         status = ads_search_dn(r->in.ads, &res, r->in.account_ou, attrs);
193         if (!ADS_ERR_OK(status)) {
194                 return status;
195         }
196
197         if (ads_count_replies(r->in.ads, res) != 1) {
198                 ads_msgfree(r->in.ads, res);
199                 return ADS_ERROR_LDAP(LDAP_NO_SUCH_OBJECT);
200         }
201
202         status = ads_create_machine_acct(r->in.ads,
203                                          r->in.machine_name,
204                                          r->in.account_ou);
205         ads_msgfree(r->in.ads, res);
206
207         if ((status.error_type == ENUM_ADS_ERROR_LDAP) &&
208             (status.err.rc == LDAP_ALREADY_EXISTS)) {
209                 status = ADS_SUCCESS;
210         }
211
212         return status;
213 }
214
215 /****************************************************************
216 ****************************************************************/
217
218 static ADS_STATUS libnet_unjoin_remove_machine_acct(TALLOC_CTX *mem_ctx,
219                                                     struct libnet_UnjoinCtx *r)
220 {
221         ADS_STATUS status;
222
223         if (!r->in.ads) {
224                 status = libnet_unjoin_connect_ads(mem_ctx, r);
225                 if (!ADS_ERR_OK(status)) {
226                         return status;
227                 }
228         }
229
230         status = ads_leave_realm(r->in.ads, r->in.machine_name);
231         if (!ADS_ERR_OK(status)) {
232                 libnet_unjoin_set_error_string(mem_ctx, r,
233                         "failed to leave realm: %s",
234                         ads_errstr(status));
235                 return status;
236         }
237
238         return ADS_SUCCESS;
239 }
240
241 /****************************************************************
242 ****************************************************************/
243
244 static ADS_STATUS libnet_join_find_machine_acct(TALLOC_CTX *mem_ctx,
245                                                 struct libnet_JoinCtx *r)
246 {
247         ADS_STATUS status;
248         LDAPMessage *res = NULL;
249         char *dn = NULL;
250
251         if (!r->in.machine_name) {
252                 return ADS_ERROR(LDAP_NO_MEMORY);
253         }
254
255         status = ads_find_machine_acct(r->in.ads,
256                                        &res,
257                                        r->in.machine_name);
258         if (!ADS_ERR_OK(status)) {
259                 return status;
260         }
261
262         if (ads_count_replies(r->in.ads, res) != 1) {
263                 status = ADS_ERROR_LDAP(LDAP_NO_MEMORY);
264                 goto done;
265         }
266
267         dn = ads_get_dn(r->in.ads, res);
268         if (!dn) {
269                 status = ADS_ERROR_LDAP(LDAP_NO_MEMORY);
270                 goto done;
271         }
272
273         r->out.dn = talloc_strdup(mem_ctx, dn);
274         if (!r->out.dn) {
275                 status = ADS_ERROR_LDAP(LDAP_NO_MEMORY);
276                 goto done;
277         }
278
279  done:
280         ads_msgfree(r->in.ads, res);
281         ads_memfree(r->in.ads, dn);
282
283         return status;
284 }
285
286 /****************************************************************
287 ****************************************************************/
288
289 static ADS_STATUS libnet_join_set_machine_spn(TALLOC_CTX *mem_ctx,
290                                               struct libnet_JoinCtx *r)
291 {
292         ADS_STATUS status;
293         ADS_MODLIST mods;
294         fstring my_fqdn;
295         const char *spn_array[3] = {NULL, NULL, NULL};
296         char *spn = NULL;
297
298         if (!r->in.ads) {
299                 status = libnet_join_connect_ads(mem_ctx, r);
300                 if (!ADS_ERR_OK(status)) {
301                         return status;
302                 }
303         }
304
305         status = libnet_join_find_machine_acct(mem_ctx, r);
306         if (!ADS_ERR_OK(status)) {
307                 return status;
308         }
309
310         spn = talloc_asprintf(mem_ctx, "HOST/%s", r->in.machine_name);
311         if (!spn) {
312                 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
313         }
314         strupper_m(spn);
315         spn_array[0] = spn;
316
317         if (name_to_fqdn(my_fqdn, r->in.machine_name) &&
318             !strequal(my_fqdn, r->in.machine_name)) {
319
320                 strlower_m(my_fqdn);
321                 spn = talloc_asprintf(mem_ctx, "HOST/%s", my_fqdn);
322                 if (!spn) {
323                         return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
324                 }
325                 spn_array[1] = spn;
326         }
327
328         mods = ads_init_mods(mem_ctx);
329         if (!mods) {
330                 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
331         }
332
333         status = ads_mod_str(mem_ctx, &mods, "dNSHostName", my_fqdn);
334         if (!ADS_ERR_OK(status)) {
335                 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
336         }
337
338         status = ads_mod_strlist(mem_ctx, &mods, "servicePrincipalName",
339                                  spn_array);
340         if (!ADS_ERR_OK(status)) {
341                 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
342         }
343
344         return ads_gen_mod(r->in.ads, r->out.dn, mods);
345 }
346
347 /****************************************************************
348 ****************************************************************/
349
350 static ADS_STATUS libnet_join_set_machine_upn(TALLOC_CTX *mem_ctx,
351                                               struct libnet_JoinCtx *r)
352 {
353         ADS_STATUS status;
354         ADS_MODLIST mods;
355
356         if (!r->in.create_upn) {
357                 return ADS_SUCCESS;
358         }
359
360         if (!r->in.ads) {
361                 status = libnet_join_connect_ads(mem_ctx, r);
362                 if (!ADS_ERR_OK(status)) {
363                         return status;
364                 }
365         }
366
367         status = libnet_join_find_machine_acct(mem_ctx, r);
368         if (!ADS_ERR_OK(status)) {
369                 return status;
370         }
371
372         if (!r->in.upn) {
373                 r->in.upn = talloc_asprintf(mem_ctx,
374                                             "host/%s@%s",
375                                             r->in.machine_name,
376                                             r->out.dns_domain_name);
377                 if (!r->in.upn) {
378                         return ADS_ERROR(LDAP_NO_MEMORY);
379                 }
380         }
381
382         mods = ads_init_mods(mem_ctx);
383         if (!mods) {
384                 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
385         }
386
387         status = ads_mod_str(mem_ctx, &mods, "userPrincipalName", r->in.upn);
388         if (!ADS_ERR_OK(status)) {
389                 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
390         }
391
392         return ads_gen_mod(r->in.ads, r->out.dn, mods);
393 }
394
395
396 /****************************************************************
397 ****************************************************************/
398
399 static ADS_STATUS libnet_join_set_os_attributes(TALLOC_CTX *mem_ctx,
400                                                 struct libnet_JoinCtx *r)
401 {
402         ADS_STATUS status;
403         ADS_MODLIST mods;
404         char *os_sp = NULL;
405
406         if (!r->in.os_name || !r->in.os_version ) {
407                 return ADS_SUCCESS;
408         }
409
410         if (!r->in.ads) {
411                 status = libnet_join_connect_ads(mem_ctx, r);
412                 if (!ADS_ERR_OK(status)) {
413                         return status;
414                 }
415         }
416
417         status = libnet_join_find_machine_acct(mem_ctx, r);
418         if (!ADS_ERR_OK(status)) {
419                 return status;
420         }
421
422         mods = ads_init_mods(mem_ctx);
423         if (!mods) {
424                 return ADS_ERROR(LDAP_NO_MEMORY);
425         }
426
427         os_sp = talloc_asprintf(mem_ctx, "Samba %s", SAMBA_VERSION_STRING);
428         if (!os_sp) {
429                 return ADS_ERROR(LDAP_NO_MEMORY);
430         }
431
432         status = ads_mod_str(mem_ctx, &mods, "operatingSystem",
433                              r->in.os_name);
434         if (!ADS_ERR_OK(status)) {
435                 return status;
436         }
437
438         status = ads_mod_str(mem_ctx, &mods, "operatingSystemVersion",
439                              r->in.os_version);
440         if (!ADS_ERR_OK(status)) {
441                 return status;
442         }
443
444         status = ads_mod_str(mem_ctx, &mods, "operatingSystemServicePack",
445                              os_sp);
446         if (!ADS_ERR_OK(status)) {
447                 return status;
448         }
449
450         return ads_gen_mod(r->in.ads, r->out.dn, mods);
451 }
452
453 /****************************************************************
454 ****************************************************************/
455
456 static bool libnet_join_create_keytab(TALLOC_CTX *mem_ctx,
457                                       struct libnet_JoinCtx *r)
458 {
459         if (!lp_use_kerberos_keytab()) {
460                 return true;
461         }
462
463         if (!ads_keytab_create_default(r->in.ads)) {
464                 return false;
465         }
466
467         return true;
468 }
469
470 /****************************************************************
471 ****************************************************************/
472
473 static bool libnet_join_derive_salting_principal(TALLOC_CTX *mem_ctx,
474                                                  struct libnet_JoinCtx *r)
475 {
476         uint32_t domain_func;
477         ADS_STATUS status;
478         const char *salt = NULL;
479         char *std_salt = NULL;
480
481         status = ads_domain_func_level(r->in.ads, &domain_func);
482         if (!ADS_ERR_OK(status)) {
483                 libnet_join_set_error_string(mem_ctx, r,
484                         "failed to determine domain functional level: %s",
485                         ads_errstr(status));
486                 return false;
487         }
488
489         std_salt = kerberos_standard_des_salt();
490         if (!std_salt) {
491                 libnet_join_set_error_string(mem_ctx, r,
492                         "failed to obtain standard DES salt");
493                 return false;
494         }
495
496         salt = talloc_strdup(mem_ctx, std_salt);
497         if (!salt) {
498                 return false;
499         }
500
501         SAFE_FREE(std_salt);
502
503         if (domain_func == DS_DOMAIN_FUNCTION_2000) {
504                 char *upn;
505
506                 upn = ads_get_upn(r->in.ads, mem_ctx,
507                                   r->in.machine_name);
508                 if (upn) {
509                         salt = talloc_strdup(mem_ctx, upn);
510                         if (!salt) {
511                                 return false;
512                         }
513                 }
514         }
515
516         return kerberos_secrets_store_des_salt(salt);
517 }
518
519 /****************************************************************
520 ****************************************************************/
521
522 static ADS_STATUS libnet_join_post_processing_ads(TALLOC_CTX *mem_ctx,
523                                                   struct libnet_JoinCtx *r)
524 {
525         ADS_STATUS status;
526
527         status = libnet_join_set_machine_spn(mem_ctx, r);
528         if (!ADS_ERR_OK(status)) {
529                 libnet_join_set_error_string(mem_ctx, r,
530                         "failed to set machine spn: %s",
531                         ads_errstr(status));
532                 return status;
533         }
534
535         status = libnet_join_set_os_attributes(mem_ctx, r);
536         if (!ADS_ERR_OK(status)) {
537                 libnet_join_set_error_string(mem_ctx, r,
538                         "failed to set machine os attributes: %s",
539                         ads_errstr(status));
540                 return status;
541         }
542
543         status = libnet_join_set_machine_upn(mem_ctx, r);
544         if (!ADS_ERR_OK(status)) {
545                 libnet_join_set_error_string(mem_ctx, r,
546                         "failed to set machine upn: %s",
547                         ads_errstr(status));
548                 return status;
549         }
550
551         if (!libnet_join_derive_salting_principal(mem_ctx, r)) {
552                 return ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL);
553         }
554
555         if (!libnet_join_create_keytab(mem_ctx, r)) {
556                 libnet_join_set_error_string(mem_ctx, r,
557                         "failed to create kerberos keytab");
558                 return ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL);
559         }
560
561         return ADS_SUCCESS;
562 }
563 #endif /* WITH_ADS */
564
565 /****************************************************************
566 ****************************************************************/
567
568 static bool libnet_join_joindomain_store_secrets(TALLOC_CTX *mem_ctx,
569                                                  struct libnet_JoinCtx *r)
570 {
571         if (!secrets_store_domain_sid(r->out.netbios_domain_name,
572                                       r->out.domain_sid))
573         {
574                 return false;
575         }
576
577         if (!secrets_store_machine_password(r->in.machine_password,
578                                             r->out.netbios_domain_name,
579                                             SEC_CHAN_WKSTA))
580         {
581                 return false;
582         }
583
584         return true;
585 }
586
587 /****************************************************************
588 ****************************************************************/
589
590 static NTSTATUS libnet_join_joindomain_rpc(TALLOC_CTX *mem_ctx,
591                                            struct libnet_JoinCtx *r)
592 {
593         struct cli_state *cli = NULL;
594         struct rpc_pipe_client *pipe_hnd = NULL;
595         POLICY_HND sam_pol, domain_pol, user_pol, lsa_pol;
596         NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
597         char *acct_name;
598         const char *const_acct_name;
599         struct lsa_String lsa_acct_name;
600         uint32 user_rid;
601         uint32 num_rids, *name_types, *user_rids;
602         uint32 flags = 0x3e8;
603         uint32 acb_info = ACB_WSTRUST;
604         uint32 fields_present;
605         uchar pwbuf[532];
606         SAM_USERINFO_CTR ctr;
607         SAM_USER_INFO_25 p25;
608         const int infolevel = 25;
609         struct MD5Context md5ctx;
610         uchar md5buffer[16];
611         DATA_BLOB digested_session_key;
612         uchar md4_trust_password[16];
613
614         if (!r->in.machine_password) {
615                 r->in.machine_password = talloc_strdup(mem_ctx, generate_random_str(DEFAULT_TRUST_ACCOUNT_PASSWORD_LENGTH));
616                 NT_STATUS_HAVE_NO_MEMORY(r->in.machine_password);
617         }
618
619         status = cli_full_connection(&cli, NULL,
620                                      r->in.dc_name,
621                                      NULL, 0,
622                                      "IPC$", "IPC",
623                                      r->in.admin_account,
624                                      NULL,
625                                      r->in.admin_password,
626                                      0,
627                                      Undefined, NULL);
628
629         if (!NT_STATUS_IS_OK(status)) {
630                 goto done;
631         }
632
633         pipe_hnd = cli_rpc_pipe_open_noauth(cli, PI_LSARPC, &status);
634         if (!pipe_hnd) {
635                 goto done;
636         }
637
638         status = rpccli_lsa_open_policy(pipe_hnd, mem_ctx, True,
639                                         SEC_RIGHTS_MAXIMUM_ALLOWED, &lsa_pol);
640         if (!NT_STATUS_IS_OK(status)) {
641                 goto done;
642         }
643
644         status = rpccli_lsa_query_info_policy2(pipe_hnd, mem_ctx, &lsa_pol,
645                                                12,
646                                                &r->out.netbios_domain_name,
647                                                &r->out.dns_domain_name,
648                                                NULL,
649                                                NULL,
650                                                &r->out.domain_sid);
651
652         if (NT_STATUS_IS_OK(status)) {
653                 r->out.domain_is_ad = true;
654         }
655
656         if (!NT_STATUS_IS_OK(status)) {
657                 status = rpccli_lsa_query_info_policy(pipe_hnd, mem_ctx, &lsa_pol,
658                                                       5,
659                                                       &r->out.netbios_domain_name,
660                                                       &r->out.domain_sid);
661                 if (!NT_STATUS_IS_OK(status)) {
662                         goto done;
663                 }
664         }
665
666         rpccli_lsa_Close(pipe_hnd, mem_ctx, &lsa_pol);
667         cli_rpc_pipe_close(pipe_hnd);
668
669         pipe_hnd = cli_rpc_pipe_open_noauth(cli, PI_SAMR, &status);
670         if (!pipe_hnd) {
671                 goto done;
672         }
673
674         status = rpccli_samr_Connect2(pipe_hnd, mem_ctx,
675                                       pipe_hnd->cli->desthost,
676                                       SEC_RIGHTS_MAXIMUM_ALLOWED,
677                                       &sam_pol);
678         if (!NT_STATUS_IS_OK(status)) {
679                 goto done;
680         }
681
682         status = rpccli_samr_OpenDomain(pipe_hnd, mem_ctx,
683                                         &sam_pol,
684                                         SEC_RIGHTS_MAXIMUM_ALLOWED,
685                                         r->out.domain_sid,
686                                         &domain_pol);
687         if (!NT_STATUS_IS_OK(status)) {
688                 goto done;
689         }
690
691         acct_name = talloc_asprintf(mem_ctx, "%s$", r->in.machine_name);
692         strlower_m(acct_name);
693         const_acct_name = acct_name;
694
695         init_lsa_String(&lsa_acct_name, acct_name);
696
697         if (r->in.join_flags & WKSSVC_JOIN_FLAGS_ACCOUNT_CREATE) {
698                 uint32_t acct_flags =
699                         SEC_GENERIC_READ | SEC_GENERIC_WRITE | SEC_GENERIC_EXECUTE |
700                         SEC_STD_WRITE_DAC | SEC_STD_DELETE |
701                         SAMR_USER_ACCESS_SET_PASSWORD |
702                         SAMR_USER_ACCESS_GET_ATTRIBUTES |
703                         SAMR_USER_ACCESS_SET_ATTRIBUTES;
704                 uint32_t access_granted = 0;
705
706                 status = rpccli_samr_CreateUser2(pipe_hnd, mem_ctx,
707                                                  &domain_pol,
708                                                  &lsa_acct_name,
709                                                  ACB_WSTRUST,
710                                                  acct_flags,
711                                                  &user_pol,
712                                                  &access_granted,
713                                                  &user_rid);
714                 if (NT_STATUS_EQUAL(status, NT_STATUS_USER_EXISTS)) {
715                         if (!(r->in.join_flags &
716                               WKSSVC_JOIN_FLAGS_DOMAIN_JOIN_IF_JOINED)) {
717                                 goto done;
718                         }
719                 }
720
721                 if (NT_STATUS_IS_OK(status)) {
722                         rpccli_samr_Close(pipe_hnd, mem_ctx, &user_pol);
723                 }
724         }
725
726         status = rpccli_samr_lookup_names(pipe_hnd, mem_ctx,
727                                           &domain_pol, flags, 1,
728                                           &const_acct_name,
729                                           &num_rids, &user_rids, &name_types);
730         if (!NT_STATUS_IS_OK(status)) {
731                 goto done;
732         }
733
734         if (name_types[0] != SID_NAME_USER) {
735                 status = NT_STATUS_INVALID_WORKSTATION;
736                 goto done;
737         }
738
739         user_rid = user_rids[0];
740
741         status = rpccli_samr_OpenUser(pipe_hnd, mem_ctx,
742                                       &domain_pol,
743                                       SEC_RIGHTS_MAXIMUM_ALLOWED,
744                                       user_rid,
745                                       &user_pol);
746         if (!NT_STATUS_IS_OK(status)) {
747                 goto done;
748         }
749
750         E_md4hash(r->in.machine_password, md4_trust_password);
751         encode_pw_buffer(pwbuf, r->in.machine_password, STR_UNICODE);
752
753         generate_random_buffer((uint8*)md5buffer, sizeof(md5buffer));
754         digested_session_key = data_blob_talloc(mem_ctx, 0, 16);
755
756         MD5Init(&md5ctx);
757         MD5Update(&md5ctx, md5buffer, sizeof(md5buffer));
758         MD5Update(&md5ctx, cli->user_session_key.data,
759                   cli->user_session_key.length);
760         MD5Final(digested_session_key.data, &md5ctx);
761
762         SamOEMhashBlob(pwbuf, sizeof(pwbuf), &digested_session_key);
763         memcpy(&pwbuf[516], md5buffer, sizeof(md5buffer));
764
765         acb_info |= ACB_PWNOEXP;
766         if (r->out.domain_is_ad) {
767 #if !defined(ENCTYPE_ARCFOUR_HMAC)
768                 acb_info |= ACB_USE_DES_KEY_ONLY;
769 #endif
770                 ;;
771         }
772
773         ZERO_STRUCT(ctr);
774         ZERO_STRUCT(p25);
775
776         fields_present = ACCT_NT_PWD_SET | ACCT_LM_PWD_SET | ACCT_FLAGS;
777         init_sam_user_info25P(&p25, fields_present, acb_info, (char *)pwbuf);
778
779         ctr.switch_value = infolevel;
780         ctr.info.id25    = &p25;
781
782         status = rpccli_samr_set_userinfo2(pipe_hnd, mem_ctx, &user_pol,
783                                            infolevel, &cli->user_session_key,
784                                            &ctr);
785         if (!NT_STATUS_IS_OK(status)) {
786                 goto done;
787         }
788
789         rpccli_samr_Close(pipe_hnd, mem_ctx, &user_pol);
790         cli_rpc_pipe_close(pipe_hnd);
791
792         status = NT_STATUS_OK;
793  done:
794         if (cli) {
795                 cli_shutdown(cli);
796         }
797
798         return status;
799 }
800
801 /****************************************************************
802 ****************************************************************/
803
804 static bool libnet_join_unjoindomain_remove_secrets(TALLOC_CTX *mem_ctx,
805                                                     struct libnet_UnjoinCtx *r)
806 {
807         if (!secrets_delete_machine_password_ex(lp_workgroup())) {
808                 return false;
809         }
810
811         if (!secrets_delete_domain_sid(lp_workgroup())) {
812                 return false;
813         }
814
815         return true;
816 }
817
818 /****************************************************************
819 ****************************************************************/
820
821 static NTSTATUS libnet_join_unjoindomain_rpc(TALLOC_CTX *mem_ctx,
822                                              struct libnet_UnjoinCtx *r)
823 {
824         struct cli_state *cli = NULL;
825         struct rpc_pipe_client *pipe_hnd = NULL;
826         POLICY_HND sam_pol, domain_pol, user_pol;
827         NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
828         char *acct_name;
829         uint32 flags = 0x3e8;
830         const char *const_acct_name;
831         uint32 user_rid;
832         uint32 num_rids, *name_types, *user_rids;
833         SAM_USERINFO_CTR ctr, *qctr = NULL;
834         SAM_USER_INFO_16 p16;
835
836         status = cli_full_connection(&cli, NULL,
837                                      r->in.dc_name,
838                                      NULL, 0,
839                                      "IPC$", "IPC",
840                                      r->in.admin_account,
841                                      NULL,
842                                      r->in.admin_password,
843                                      0, Undefined, NULL);
844
845         if (!NT_STATUS_IS_OK(status)) {
846                 goto done;
847         }
848
849         pipe_hnd = cli_rpc_pipe_open_noauth(cli, PI_SAMR, &status);
850         if (!pipe_hnd) {
851                 goto done;
852         }
853
854         status = rpccli_samr_Connect2(pipe_hnd, mem_ctx,
855                                       pipe_hnd->cli->desthost,
856                                       SEC_RIGHTS_MAXIMUM_ALLOWED,
857                                       &sam_pol);
858         if (!NT_STATUS_IS_OK(status)) {
859                 goto done;
860         }
861
862         status = rpccli_samr_OpenDomain(pipe_hnd, mem_ctx,
863                                         &sam_pol,
864                                         SEC_RIGHTS_MAXIMUM_ALLOWED,
865                                         r->in.domain_sid,
866                                         &domain_pol);
867         if (!NT_STATUS_IS_OK(status)) {
868                 goto done;
869         }
870
871         acct_name = talloc_asprintf(mem_ctx, "%s$", r->in.machine_name);
872         strlower_m(acct_name);
873         const_acct_name = acct_name;
874
875         status = rpccli_samr_lookup_names(pipe_hnd, mem_ctx,
876                                           &domain_pol, flags, 1,
877                                           &const_acct_name,
878                                           &num_rids, &user_rids, &name_types);
879         if (!NT_STATUS_IS_OK(status)) {
880                 goto done;
881         }
882
883         if (name_types[0] != SID_NAME_USER) {
884                 status = NT_STATUS_INVALID_WORKSTATION;
885                 goto done;
886         }
887
888         user_rid = user_rids[0];
889
890         status = rpccli_samr_OpenUser(pipe_hnd, mem_ctx,
891                                       &domain_pol,
892                                       SEC_RIGHTS_MAXIMUM_ALLOWED,
893                                       user_rid,
894                                       &user_pol);
895         if (!NT_STATUS_IS_OK(status)) {
896                 goto done;
897         }
898
899         status = rpccli_samr_query_userinfo(pipe_hnd, mem_ctx,
900                                             &user_pol, 16, &qctr);
901         if (!NT_STATUS_IS_OK(status)) {
902                 rpccli_samr_Close(pipe_hnd, mem_ctx, &user_pol);
903                 goto done;
904         }
905
906         ZERO_STRUCT(ctr);
907         ctr.switch_value = 16;
908         ctr.info.id16 = &p16;
909
910         p16.acb_info = qctr->info.id16->acb_info | ACB_DISABLED;
911
912         status = rpccli_samr_set_userinfo2(pipe_hnd, mem_ctx, &user_pol, 16,
913                                            &cli->user_session_key, &ctr);
914
915         rpccli_samr_Close(pipe_hnd, mem_ctx, &user_pol);
916
917 done:
918         if (pipe_hnd) {
919                 rpccli_samr_Close(pipe_hnd, mem_ctx, &domain_pol);
920                 rpccli_samr_Close(pipe_hnd, mem_ctx, &sam_pol);
921                 cli_rpc_pipe_close(pipe_hnd);
922         }
923
924         if (cli) {
925                 cli_shutdown(cli);
926         }
927
928         return status;
929 }
930
931 /****************************************************************
932 ****************************************************************/
933
934 static WERROR do_join_modify_vals_config(struct libnet_JoinCtx *r)
935 {
936         WERROR werr;
937         struct libnet_conf_ctx *ctx;
938
939         werr = libnet_conf_open(r, &ctx);
940         if (!W_ERROR_IS_OK(werr)) {
941                 goto done;
942         }
943
944         if (!(r->in.join_flags & WKSSVC_JOIN_FLAGS_JOIN_TYPE)) {
945
946                 werr = libnet_conf_set_global_parameter(ctx, "security", "user");
947                 if (!W_ERROR_IS_OK(werr)) {
948                         goto done;
949                 }
950
951                 werr = libnet_conf_set_global_parameter(ctx, "workgroup",
952                                                         r->in.domain_name);
953                 goto done;
954         }
955
956         werr = libnet_conf_set_global_parameter(ctx, "security", "domain");
957         if (!W_ERROR_IS_OK(werr)) {
958                 goto done;
959         }
960
961         werr = libnet_conf_set_global_parameter(ctx, "workgroup",
962                                                 r->out.netbios_domain_name);
963         if (!W_ERROR_IS_OK(werr)) {
964                 goto done;
965         }
966
967         if (r->out.domain_is_ad) {
968                 werr = libnet_conf_set_global_parameter(ctx, "security", "ads");
969                 if (!W_ERROR_IS_OK(werr)) {
970                         goto done;
971                 }
972
973                 werr = libnet_conf_set_global_parameter(ctx, "realm",
974                                                         r->out.dns_domain_name);
975         }
976
977 done:
978         libnet_conf_close(ctx);
979         return werr;
980 }
981
982 /****************************************************************
983 ****************************************************************/
984
985 static WERROR do_unjoin_modify_vals_config(struct libnet_UnjoinCtx *r)
986 {
987         WERROR werr = WERR_OK;
988         struct libnet_conf_ctx *ctx;
989
990         werr = libnet_conf_open(r, &ctx);
991         if (!W_ERROR_IS_OK(werr)) {
992                 goto done;
993         }
994
995         if (r->in.unjoin_flags & WKSSVC_JOIN_FLAGS_JOIN_TYPE) {
996
997                 werr = libnet_conf_set_global_parameter(ctx, "security", "user");
998                 if (!W_ERROR_IS_OK(werr)) {
999                         goto done;
1000                 }
1001         }
1002
1003         libnet_conf_delete_global_parameter(ctx, "realm");
1004
1005 done:
1006         libnet_conf_close(ctx);
1007         return werr;
1008 }
1009
1010 /****************************************************************
1011 ****************************************************************/
1012
1013 static WERROR do_JoinConfig(struct libnet_JoinCtx *r)
1014 {
1015         WERROR werr;
1016
1017         if (!W_ERROR_IS_OK(r->out.result)) {
1018                 return r->out.result;
1019         }
1020
1021         if (!r->in.modify_config) {
1022                 return WERR_OK;
1023         }
1024
1025         werr = do_join_modify_vals_config(r);
1026         if (!W_ERROR_IS_OK(werr)) {
1027                 return werr;
1028         }
1029
1030         r->out.modified_config = true;
1031         r->out.result = werr;
1032
1033         return werr;
1034 }
1035
1036 /****************************************************************
1037 ****************************************************************/
1038
1039 static WERROR do_UnjoinConfig(struct libnet_UnjoinCtx *r)
1040 {
1041         WERROR werr;
1042
1043         if (!W_ERROR_IS_OK(r->out.result)) {
1044                 return r->out.result;
1045         }
1046
1047         if (!r->in.modify_config) {
1048                 return WERR_OK;
1049         }
1050
1051         werr = do_unjoin_modify_vals_config(r);
1052         if (!W_ERROR_IS_OK(werr)) {
1053                 return werr;
1054         }
1055
1056         r->out.modified_config = true;
1057         r->out.result = werr;
1058
1059         return werr;
1060 }
1061
1062 /****************************************************************
1063 ****************************************************************/
1064
1065 static WERROR libnet_join_pre_processing(TALLOC_CTX *mem_ctx,
1066                                          struct libnet_JoinCtx *r)
1067 {
1068
1069         if (!r->in.domain_name) {
1070                 return WERR_INVALID_PARAM;
1071         }
1072
1073         if (r->in.modify_config && !lp_config_backend_is_registry()) {
1074                 return WERR_NOT_SUPPORTED;
1075         }
1076
1077         if (IS_DC) {
1078                 return WERR_SETUP_DOMAIN_CONTROLLER;
1079         }
1080
1081         if (!secrets_init()) {
1082                 libnet_join_set_error_string(mem_ctx, r,
1083                         "Unable to open secrets database");
1084                 return WERR_CAN_NOT_COMPLETE;
1085         }
1086
1087         return WERR_OK;
1088 }
1089
1090 /****************************************************************
1091 ****************************************************************/
1092
1093 static WERROR libnet_join_post_processing(TALLOC_CTX *mem_ctx,
1094                                           struct libnet_JoinCtx *r)
1095 {
1096         WERROR werr;
1097
1098         if (!W_ERROR_IS_OK(r->out.result)) {
1099                 return r->out.result;
1100         }
1101
1102         werr = do_JoinConfig(r);
1103         if (!W_ERROR_IS_OK(werr)) {
1104                 return werr;
1105         }
1106
1107         if (r->in.join_flags & WKSSVC_JOIN_FLAGS_JOIN_TYPE) {
1108                 saf_store(r->in.domain_name, r->in.dc_name);
1109         }
1110
1111         return WERR_OK;
1112 }
1113
1114 /****************************************************************
1115 ****************************************************************/
1116
1117 static int libnet_destroy_JoinCtx(struct libnet_JoinCtx *r)
1118 {
1119         if (r->in.ads) {
1120                 ads_destroy(&r->in.ads);
1121         }
1122
1123         return 0;
1124 }
1125
1126 /****************************************************************
1127 ****************************************************************/
1128
1129 static int libnet_destroy_UnjoinCtx(struct libnet_UnjoinCtx *r)
1130 {
1131         if (r->in.ads) {
1132                 ads_destroy(&r->in.ads);
1133         }
1134
1135         return 0;
1136 }
1137
1138 /****************************************************************
1139 ****************************************************************/
1140
1141 WERROR libnet_init_JoinCtx(TALLOC_CTX *mem_ctx,
1142                            struct libnet_JoinCtx **r)
1143 {
1144         struct libnet_JoinCtx *ctx;
1145
1146         ctx = talloc_zero(mem_ctx, struct libnet_JoinCtx);
1147         if (!ctx) {
1148                 return WERR_NOMEM;
1149         }
1150
1151         talloc_set_destructor(ctx, libnet_destroy_JoinCtx);
1152
1153         ctx->in.machine_name = talloc_strdup(mem_ctx, global_myname());
1154         W_ERROR_HAVE_NO_MEMORY(ctx->in.machine_name);
1155
1156         *r = ctx;
1157
1158         return WERR_OK;
1159 }
1160
1161 /****************************************************************
1162 ****************************************************************/
1163
1164 WERROR libnet_init_UnjoinCtx(TALLOC_CTX *mem_ctx,
1165                              struct libnet_UnjoinCtx **r)
1166 {
1167         struct libnet_UnjoinCtx *ctx;
1168
1169         ctx = talloc_zero(mem_ctx, struct libnet_UnjoinCtx);
1170         if (!ctx) {
1171                 return WERR_NOMEM;
1172         }
1173
1174         talloc_set_destructor(ctx, libnet_destroy_UnjoinCtx);
1175
1176         ctx->in.machine_name = talloc_strdup(mem_ctx, global_myname());
1177         W_ERROR_HAVE_NO_MEMORY(ctx->in.machine_name);
1178
1179         *r = ctx;
1180
1181         return WERR_OK;
1182 }
1183
1184 /****************************************************************
1185 ****************************************************************/
1186
1187 static WERROR libnet_DomainJoin(TALLOC_CTX *mem_ctx,
1188                                 struct libnet_JoinCtx *r)
1189 {
1190         NTSTATUS status;
1191 #ifdef WITH_ADS
1192         ADS_STATUS ads_status;
1193 #endif /* WITH_ADS */
1194
1195         if (!r->in.dc_name) {
1196                 struct DS_DOMAIN_CONTROLLER_INFO *info;
1197                 status = dsgetdcname(mem_ctx,
1198                                      r->in.domain_name,
1199                                      NULL,
1200                                      NULL,
1201                                      DS_DIRECTORY_SERVICE_REQUIRED |
1202                                      DS_WRITABLE_REQUIRED |
1203                                      DS_RETURN_DNS_NAME,
1204                                      &info);
1205                 if (!NT_STATUS_IS_OK(status)) {
1206                         libnet_join_set_error_string(mem_ctx, r,
1207                                 "failed to find DC for domain %s",
1208                                 r->in.domain_name,
1209                                 get_friendly_nt_error_msg(status));
1210                         return WERR_DOMAIN_CONTROLLER_NOT_FOUND;
1211                 }
1212
1213                 r->in.dc_name = talloc_strdup(mem_ctx,
1214                                               info->domain_controller_name);
1215                 W_ERROR_HAVE_NO_MEMORY(r->in.dc_name);
1216         }
1217
1218 #ifdef WITH_ADS
1219         if (r->in.account_ou) {
1220
1221                 ads_status = libnet_join_connect_ads(mem_ctx, r);
1222                 if (!ADS_ERR_OK(ads_status)) {
1223                         return WERR_DEFAULT_JOIN_REQUIRED;
1224                 }
1225
1226                 ads_status = libnet_join_precreate_machine_acct(mem_ctx, r);
1227                 if (!ADS_ERR_OK(ads_status)) {
1228                         libnet_join_set_error_string(mem_ctx, r,
1229                                 "failed to precreate account in ou %s: %s",
1230                                 r->in.account_ou,
1231                                 ads_errstr(ads_status));
1232                         return WERR_DEFAULT_JOIN_REQUIRED;
1233                 }
1234
1235                 r->in.join_flags &= ~WKSSVC_JOIN_FLAGS_ACCOUNT_CREATE;
1236         }
1237 #endif /* WITH_ADS */
1238
1239         status = libnet_join_joindomain_rpc(mem_ctx, r);
1240         if (!NT_STATUS_IS_OK(status)) {
1241                 libnet_join_set_error_string(mem_ctx, r,
1242                         "failed to join domain over rpc: %s",
1243                         get_friendly_nt_error_msg(status));
1244                 if (NT_STATUS_EQUAL(status, NT_STATUS_USER_EXISTS)) {
1245                         return WERR_SETUP_ALREADY_JOINED;
1246                 }
1247                 return ntstatus_to_werror(status);
1248         }
1249
1250         if (!libnet_join_joindomain_store_secrets(mem_ctx, r)) {
1251                 return WERR_SETUP_NOT_JOINED;
1252         }
1253
1254 #ifdef WITH_ADS
1255         if (r->out.domain_is_ad) {
1256                 ads_status  = libnet_join_post_processing_ads(mem_ctx, r);
1257                 if (!ADS_ERR_OK(ads_status)) {
1258                         return WERR_GENERAL_FAILURE;
1259                 }
1260         }
1261 #endif /* WITH_ADS */
1262
1263         return WERR_OK;
1264 }
1265
1266 /****************************************************************
1267 ****************************************************************/
1268
1269 WERROR libnet_Join(TALLOC_CTX *mem_ctx,
1270                    struct libnet_JoinCtx *r)
1271 {
1272         WERROR werr;
1273
1274         if (r->in.debug) {
1275                 LIBNET_JOIN_IN_DUMP_CTX(mem_ctx, r);
1276         }
1277
1278         werr = libnet_join_pre_processing(mem_ctx, r);
1279         if (!W_ERROR_IS_OK(werr)) {
1280                 goto done;
1281         }
1282
1283         if (r->in.join_flags & WKSSVC_JOIN_FLAGS_JOIN_TYPE) {
1284                 werr = libnet_DomainJoin(mem_ctx, r);
1285                 if (!W_ERROR_IS_OK(werr)) {
1286                         goto done;
1287                 }
1288         }
1289
1290         werr = libnet_join_post_processing(mem_ctx, r);
1291         if (!W_ERROR_IS_OK(werr)) {
1292                 goto done;
1293         }
1294  done:
1295         r->out.result = werr;
1296
1297         if (r->in.debug) {
1298                 LIBNET_JOIN_OUT_DUMP_CTX(mem_ctx, r);
1299         }
1300         return werr;
1301 }
1302
1303 /****************************************************************
1304 ****************************************************************/
1305
1306 static WERROR libnet_DomainUnjoin(TALLOC_CTX *mem_ctx,
1307                                   struct libnet_UnjoinCtx *r)
1308 {
1309         NTSTATUS status;
1310
1311         if (!r->in.domain_sid) {
1312                 struct dom_sid sid;
1313                 if (!secrets_fetch_domain_sid(lp_workgroup(), &sid)) {
1314                         libnet_unjoin_set_error_string(mem_ctx, r,
1315                                 "Unable to fetch domain sid: are we joined?");
1316                         return WERR_SETUP_NOT_JOINED;
1317                 }
1318                 r->in.domain_sid = sid_dup_talloc(mem_ctx, &sid);
1319                 W_ERROR_HAVE_NO_MEMORY(r->in.domain_sid);
1320         }
1321
1322         if (!r->in.dc_name) {
1323                 struct DS_DOMAIN_CONTROLLER_INFO *info;
1324                 status = dsgetdcname(mem_ctx,
1325                                      r->in.domain_name,
1326                                      NULL,
1327                                      NULL,
1328                                      DS_DIRECTORY_SERVICE_REQUIRED |
1329                                      DS_WRITABLE_REQUIRED |
1330                                      DS_RETURN_DNS_NAME,
1331                                      &info);
1332                 if (!NT_STATUS_IS_OK(status)) {
1333                         libnet_unjoin_set_error_string(mem_ctx, r,
1334                                 "failed to find DC for domain %s",
1335                                 r->in.domain_name,
1336                                 get_friendly_nt_error_msg(status));
1337                         return WERR_DOMAIN_CONTROLLER_NOT_FOUND;
1338                 }
1339
1340                 r->in.dc_name = talloc_strdup(mem_ctx,
1341                                               info->domain_controller_name);
1342                 W_ERROR_HAVE_NO_MEMORY(r->in.dc_name);
1343         }
1344
1345         status = libnet_join_unjoindomain_rpc(mem_ctx, r);
1346         if (!NT_STATUS_IS_OK(status)) {
1347                 libnet_unjoin_set_error_string(mem_ctx, r,
1348                         "failed to disable machine account via rpc: %s",
1349                         get_friendly_nt_error_msg(status));
1350                 if (NT_STATUS_EQUAL(status, NT_STATUS_NO_SUCH_USER)) {
1351                         return WERR_SETUP_NOT_JOINED;
1352                 }
1353                 return ntstatus_to_werror(status);
1354         }
1355
1356 #ifdef WITH_ADS
1357         if (r->in.unjoin_flags & WKSSVC_JOIN_FLAGS_ACCOUNT_DELETE) {
1358                 ADS_STATUS ads_status;
1359                 libnet_unjoin_connect_ads(mem_ctx, r);
1360                 ads_status = libnet_unjoin_remove_machine_acct(mem_ctx, r);
1361                 if (!ADS_ERR_OK(ads_status)) {
1362                         libnet_unjoin_set_error_string(mem_ctx, r,
1363                                 "failed to remove machine account from AD: %s",
1364                                 ads_errstr(ads_status));
1365                 }
1366         }
1367 #endif /* WITH_ADS */
1368
1369         libnet_join_unjoindomain_remove_secrets(mem_ctx, r);
1370
1371         return WERR_OK;
1372 }
1373
1374 /****************************************************************
1375 ****************************************************************/
1376
1377 static WERROR libnet_unjoin_pre_processing(TALLOC_CTX *mem_ctx,
1378                                            struct libnet_UnjoinCtx *r)
1379 {
1380         if (r->in.modify_config && !lp_config_backend_is_registry()) {
1381                 return WERR_NOT_SUPPORTED;
1382         }
1383
1384         if (!secrets_init()) {
1385                 libnet_unjoin_set_error_string(mem_ctx, r,
1386                         "Unable to open secrets database");
1387                 return WERR_CAN_NOT_COMPLETE;
1388         }
1389
1390         return WERR_OK;
1391 }
1392
1393
1394 /****************************************************************
1395 ****************************************************************/
1396
1397 WERROR libnet_Unjoin(TALLOC_CTX *mem_ctx,
1398                      struct libnet_UnjoinCtx *r)
1399 {
1400         WERROR werr;
1401
1402         if (r->in.debug) {
1403                 LIBNET_UNJOIN_IN_DUMP_CTX(mem_ctx, r);
1404         }
1405
1406         werr = libnet_unjoin_pre_processing(mem_ctx, r);
1407         if (!W_ERROR_IS_OK(werr)) {
1408                 goto done;
1409         }
1410
1411         if (r->in.unjoin_flags & WKSSVC_JOIN_FLAGS_JOIN_TYPE) {
1412                 werr = libnet_DomainUnjoin(mem_ctx, r);
1413                 if (!W_ERROR_IS_OK(werr)) {
1414                         goto done;
1415                 }
1416         }
1417
1418         werr = do_UnjoinConfig(r);
1419         if (!W_ERROR_IS_OK(werr)) {
1420                 goto done;
1421         }
1422
1423  done:
1424         r->out.result = werr;
1425
1426         if (r->in.debug) {
1427                 LIBNET_UNJOIN_OUT_DUMP_CTX(mem_ctx, r);
1428         }
1429
1430         return werr;
1431 }