Use rpccli_samr_CreateUser2() all over the place.
[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_connect(pipe_hnd, mem_ctx,
675                                      SEC_RIGHTS_MAXIMUM_ALLOWED, &sam_pol);
676         if (!NT_STATUS_IS_OK(status)) {
677                 goto done;
678         }
679
680         status = rpccli_samr_OpenDomain(pipe_hnd, mem_ctx,
681                                         &sam_pol,
682                                         SEC_RIGHTS_MAXIMUM_ALLOWED,
683                                         r->out.domain_sid,
684                                         &domain_pol);
685         if (!NT_STATUS_IS_OK(status)) {
686                 goto done;
687         }
688
689         acct_name = talloc_asprintf(mem_ctx, "%s$", r->in.machine_name);
690         strlower_m(acct_name);
691         const_acct_name = acct_name;
692
693         init_lsa_String(&lsa_acct_name, acct_name);
694
695         if (r->in.join_flags & WKSSVC_JOIN_FLAGS_ACCOUNT_CREATE) {
696                 uint32_t acct_flags =
697                         SEC_GENERIC_READ | SEC_GENERIC_WRITE | SEC_GENERIC_EXECUTE |
698                         SEC_STD_WRITE_DAC | SEC_STD_DELETE |
699                         SAMR_USER_ACCESS_SET_PASSWORD |
700                         SAMR_USER_ACCESS_GET_ATTRIBUTES |
701                         SAMR_USER_ACCESS_SET_ATTRIBUTES;
702                 uint32_t access_granted = 0;
703
704                 status = rpccli_samr_CreateUser2(pipe_hnd, mem_ctx,
705                                                  &domain_pol,
706                                                  &lsa_acct_name,
707                                                  ACB_WSTRUST,
708                                                  acct_flags,
709                                                  &user_pol,
710                                                  &access_granted,
711                                                  &user_rid);
712                 if (NT_STATUS_EQUAL(status, NT_STATUS_USER_EXISTS)) {
713                         if (!(r->in.join_flags &
714                               WKSSVC_JOIN_FLAGS_DOMAIN_JOIN_IF_JOINED)) {
715                                 goto done;
716                         }
717                 }
718
719                 if (NT_STATUS_IS_OK(status)) {
720                         rpccli_samr_Close(pipe_hnd, mem_ctx, &user_pol);
721                 }
722         }
723
724         status = rpccli_samr_lookup_names(pipe_hnd, mem_ctx,
725                                           &domain_pol, flags, 1,
726                                           &const_acct_name,
727                                           &num_rids, &user_rids, &name_types);
728         if (!NT_STATUS_IS_OK(status)) {
729                 goto done;
730         }
731
732         if (name_types[0] != SID_NAME_USER) {
733                 status = NT_STATUS_INVALID_WORKSTATION;
734                 goto done;
735         }
736
737         user_rid = user_rids[0];
738
739         status = rpccli_samr_OpenUser(pipe_hnd, mem_ctx,
740                                       &domain_pol,
741                                       SEC_RIGHTS_MAXIMUM_ALLOWED,
742                                       user_rid,
743                                       &user_pol);
744         if (!NT_STATUS_IS_OK(status)) {
745                 goto done;
746         }
747
748         E_md4hash(r->in.machine_password, md4_trust_password);
749         encode_pw_buffer(pwbuf, r->in.machine_password, STR_UNICODE);
750
751         generate_random_buffer((uint8*)md5buffer, sizeof(md5buffer));
752         digested_session_key = data_blob_talloc(mem_ctx, 0, 16);
753
754         MD5Init(&md5ctx);
755         MD5Update(&md5ctx, md5buffer, sizeof(md5buffer));
756         MD5Update(&md5ctx, cli->user_session_key.data,
757                   cli->user_session_key.length);
758         MD5Final(digested_session_key.data, &md5ctx);
759
760         SamOEMhashBlob(pwbuf, sizeof(pwbuf), &digested_session_key);
761         memcpy(&pwbuf[516], md5buffer, sizeof(md5buffer));
762
763         acb_info |= ACB_PWNOEXP;
764         if (r->out.domain_is_ad) {
765 #if !defined(ENCTYPE_ARCFOUR_HMAC)
766                 acb_info |= ACB_USE_DES_KEY_ONLY;
767 #endif
768                 ;;
769         }
770
771         ZERO_STRUCT(ctr);
772         ZERO_STRUCT(p25);
773
774         fields_present = ACCT_NT_PWD_SET | ACCT_LM_PWD_SET | ACCT_FLAGS;
775         init_sam_user_info25P(&p25, fields_present, acb_info, (char *)pwbuf);
776
777         ctr.switch_value = infolevel;
778         ctr.info.id25    = &p25;
779
780         status = rpccli_samr_set_userinfo2(pipe_hnd, mem_ctx, &user_pol,
781                                            infolevel, &cli->user_session_key,
782                                            &ctr);
783         if (!NT_STATUS_IS_OK(status)) {
784                 goto done;
785         }
786
787         rpccli_samr_Close(pipe_hnd, mem_ctx, &user_pol);
788         cli_rpc_pipe_close(pipe_hnd);
789
790         status = NT_STATUS_OK;
791  done:
792         if (cli) {
793                 cli_shutdown(cli);
794         }
795
796         return status;
797 }
798
799 /****************************************************************
800 ****************************************************************/
801
802 static bool libnet_join_unjoindomain_remove_secrets(TALLOC_CTX *mem_ctx,
803                                                     struct libnet_UnjoinCtx *r)
804 {
805         if (!secrets_delete_machine_password_ex(lp_workgroup())) {
806                 return false;
807         }
808
809         if (!secrets_delete_domain_sid(lp_workgroup())) {
810                 return false;
811         }
812
813         return true;
814 }
815
816 /****************************************************************
817 ****************************************************************/
818
819 static NTSTATUS libnet_join_unjoindomain_rpc(TALLOC_CTX *mem_ctx,
820                                              struct libnet_UnjoinCtx *r)
821 {
822         struct cli_state *cli = NULL;
823         struct rpc_pipe_client *pipe_hnd = NULL;
824         POLICY_HND sam_pol, domain_pol, user_pol;
825         NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
826         char *acct_name;
827         uint32 flags = 0x3e8;
828         const char *const_acct_name;
829         uint32 user_rid;
830         uint32 num_rids, *name_types, *user_rids;
831         SAM_USERINFO_CTR ctr, *qctr = NULL;
832         SAM_USER_INFO_16 p16;
833
834         status = cli_full_connection(&cli, NULL,
835                                      r->in.dc_name,
836                                      NULL, 0,
837                                      "IPC$", "IPC",
838                                      r->in.admin_account,
839                                      NULL,
840                                      r->in.admin_password,
841                                      0, Undefined, NULL);
842
843         if (!NT_STATUS_IS_OK(status)) {
844                 goto done;
845         }
846
847         pipe_hnd = cli_rpc_pipe_open_noauth(cli, PI_SAMR, &status);
848         if (!pipe_hnd) {
849                 goto done;
850         }
851
852         status = rpccli_samr_connect(pipe_hnd, mem_ctx,
853                                      SEC_RIGHTS_MAXIMUM_ALLOWED, &sam_pol);
854         if (!NT_STATUS_IS_OK(status)) {
855                 goto done;
856         }
857
858         status = rpccli_samr_OpenDomain(pipe_hnd, mem_ctx,
859                                         &sam_pol,
860                                         SEC_RIGHTS_MAXIMUM_ALLOWED,
861                                         r->in.domain_sid,
862                                         &domain_pol);
863         if (!NT_STATUS_IS_OK(status)) {
864                 goto done;
865         }
866
867         acct_name = talloc_asprintf(mem_ctx, "%s$", r->in.machine_name);
868         strlower_m(acct_name);
869         const_acct_name = acct_name;
870
871         status = rpccli_samr_lookup_names(pipe_hnd, mem_ctx,
872                                           &domain_pol, flags, 1,
873                                           &const_acct_name,
874                                           &num_rids, &user_rids, &name_types);
875         if (!NT_STATUS_IS_OK(status)) {
876                 goto done;
877         }
878
879         if (name_types[0] != SID_NAME_USER) {
880                 status = NT_STATUS_INVALID_WORKSTATION;
881                 goto done;
882         }
883
884         user_rid = user_rids[0];
885
886         status = rpccli_samr_OpenUser(pipe_hnd, mem_ctx,
887                                       &domain_pol,
888                                       SEC_RIGHTS_MAXIMUM_ALLOWED,
889                                       user_rid,
890                                       &user_pol);
891         if (!NT_STATUS_IS_OK(status)) {
892                 goto done;
893         }
894
895         status = rpccli_samr_query_userinfo(pipe_hnd, mem_ctx,
896                                             &user_pol, 16, &qctr);
897         if (!NT_STATUS_IS_OK(status)) {
898                 rpccli_samr_Close(pipe_hnd, mem_ctx, &user_pol);
899                 goto done;
900         }
901
902         ZERO_STRUCT(ctr);
903         ctr.switch_value = 16;
904         ctr.info.id16 = &p16;
905
906         p16.acb_info = qctr->info.id16->acb_info | ACB_DISABLED;
907
908         status = rpccli_samr_set_userinfo2(pipe_hnd, mem_ctx, &user_pol, 16,
909                                            &cli->user_session_key, &ctr);
910
911         rpccli_samr_Close(pipe_hnd, mem_ctx, &user_pol);
912
913 done:
914         if (pipe_hnd) {
915                 rpccli_samr_Close(pipe_hnd, mem_ctx, &domain_pol);
916                 rpccli_samr_Close(pipe_hnd, mem_ctx, &sam_pol);
917                 cli_rpc_pipe_close(pipe_hnd);
918         }
919
920         if (cli) {
921                 cli_shutdown(cli);
922         }
923
924         return status;
925 }
926
927 /****************************************************************
928 ****************************************************************/
929
930 static WERROR do_join_modify_vals_config(struct libnet_JoinCtx *r)
931 {
932         WERROR werr;
933         struct libnet_conf_ctx *ctx;
934
935         werr = libnet_conf_open(r, &ctx);
936         if (!W_ERROR_IS_OK(werr)) {
937                 goto done;
938         }
939
940         if (!(r->in.join_flags & WKSSVC_JOIN_FLAGS_JOIN_TYPE)) {
941
942                 werr = libnet_conf_set_global_parameter(ctx, "security", "user");
943                 if (!W_ERROR_IS_OK(werr)) {
944                         goto done;
945                 }
946
947                 werr = libnet_conf_set_global_parameter(ctx, "workgroup",
948                                                         r->in.domain_name);
949                 goto done;
950         }
951
952         werr = libnet_conf_set_global_parameter(ctx, "security", "domain");
953         if (!W_ERROR_IS_OK(werr)) {
954                 goto done;
955         }
956
957         werr = libnet_conf_set_global_parameter(ctx, "workgroup",
958                                                 r->out.netbios_domain_name);
959         if (!W_ERROR_IS_OK(werr)) {
960                 goto done;
961         }
962
963         if (r->out.domain_is_ad) {
964                 werr = libnet_conf_set_global_parameter(ctx, "security", "ads");
965                 if (!W_ERROR_IS_OK(werr)) {
966                         goto done;
967                 }
968
969                 werr = libnet_conf_set_global_parameter(ctx, "realm",
970                                                         r->out.dns_domain_name);
971         }
972
973 done:
974         libnet_conf_close(ctx);
975         return werr;
976 }
977
978 /****************************************************************
979 ****************************************************************/
980
981 static WERROR do_unjoin_modify_vals_config(struct libnet_UnjoinCtx *r)
982 {
983         WERROR werr = WERR_OK;
984         struct libnet_conf_ctx *ctx;
985
986         werr = libnet_conf_open(r, &ctx);
987         if (!W_ERROR_IS_OK(werr)) {
988                 goto done;
989         }
990
991         if (r->in.unjoin_flags & WKSSVC_JOIN_FLAGS_JOIN_TYPE) {
992
993                 werr = libnet_conf_set_global_parameter(ctx, "security", "user");
994                 if (!W_ERROR_IS_OK(werr)) {
995                         goto done;
996                 }
997         }
998
999         libnet_conf_delete_global_parameter(ctx, "realm");
1000
1001 done:
1002         libnet_conf_close(ctx);
1003         return werr;
1004 }
1005
1006 /****************************************************************
1007 ****************************************************************/
1008
1009 static WERROR do_JoinConfig(struct libnet_JoinCtx *r)
1010 {
1011         WERROR werr;
1012
1013         if (!W_ERROR_IS_OK(r->out.result)) {
1014                 return r->out.result;
1015         }
1016
1017         if (!r->in.modify_config) {
1018                 return WERR_OK;
1019         }
1020
1021         werr = do_join_modify_vals_config(r);
1022         if (!W_ERROR_IS_OK(werr)) {
1023                 return werr;
1024         }
1025
1026         r->out.modified_config = true;
1027         r->out.result = werr;
1028
1029         return werr;
1030 }
1031
1032 /****************************************************************
1033 ****************************************************************/
1034
1035 static WERROR do_UnjoinConfig(struct libnet_UnjoinCtx *r)
1036 {
1037         WERROR werr;
1038
1039         if (!W_ERROR_IS_OK(r->out.result)) {
1040                 return r->out.result;
1041         }
1042
1043         if (!r->in.modify_config) {
1044                 return WERR_OK;
1045         }
1046
1047         werr = do_unjoin_modify_vals_config(r);
1048         if (!W_ERROR_IS_OK(werr)) {
1049                 return werr;
1050         }
1051
1052         r->out.modified_config = true;
1053         r->out.result = werr;
1054
1055         return werr;
1056 }
1057
1058 /****************************************************************
1059 ****************************************************************/
1060
1061 static WERROR libnet_join_pre_processing(TALLOC_CTX *mem_ctx,
1062                                          struct libnet_JoinCtx *r)
1063 {
1064
1065         if (!r->in.domain_name) {
1066                 return WERR_INVALID_PARAM;
1067         }
1068
1069         if (r->in.modify_config && !lp_config_backend_is_registry()) {
1070                 return WERR_NOT_SUPPORTED;
1071         }
1072
1073         if (IS_DC) {
1074                 return WERR_SETUP_DOMAIN_CONTROLLER;
1075         }
1076
1077         if (!secrets_init()) {
1078                 libnet_join_set_error_string(mem_ctx, r,
1079                         "Unable to open secrets database");
1080                 return WERR_CAN_NOT_COMPLETE;
1081         }
1082
1083         return WERR_OK;
1084 }
1085
1086 /****************************************************************
1087 ****************************************************************/
1088
1089 static WERROR libnet_join_post_processing(TALLOC_CTX *mem_ctx,
1090                                           struct libnet_JoinCtx *r)
1091 {
1092         WERROR werr;
1093
1094         if (!W_ERROR_IS_OK(r->out.result)) {
1095                 return r->out.result;
1096         }
1097
1098         werr = do_JoinConfig(r);
1099         if (!W_ERROR_IS_OK(werr)) {
1100                 return werr;
1101         }
1102
1103         if (r->in.join_flags & WKSSVC_JOIN_FLAGS_JOIN_TYPE) {
1104                 saf_store(r->in.domain_name, r->in.dc_name);
1105         }
1106
1107         return WERR_OK;
1108 }
1109
1110 /****************************************************************
1111 ****************************************************************/
1112
1113 static int libnet_destroy_JoinCtx(struct libnet_JoinCtx *r)
1114 {
1115         if (r->in.ads) {
1116                 ads_destroy(&r->in.ads);
1117         }
1118
1119         return 0;
1120 }
1121
1122 /****************************************************************
1123 ****************************************************************/
1124
1125 static int libnet_destroy_UnjoinCtx(struct libnet_UnjoinCtx *r)
1126 {
1127         if (r->in.ads) {
1128                 ads_destroy(&r->in.ads);
1129         }
1130
1131         return 0;
1132 }
1133
1134 /****************************************************************
1135 ****************************************************************/
1136
1137 WERROR libnet_init_JoinCtx(TALLOC_CTX *mem_ctx,
1138                            struct libnet_JoinCtx **r)
1139 {
1140         struct libnet_JoinCtx *ctx;
1141
1142         ctx = talloc_zero(mem_ctx, struct libnet_JoinCtx);
1143         if (!ctx) {
1144                 return WERR_NOMEM;
1145         }
1146
1147         talloc_set_destructor(ctx, libnet_destroy_JoinCtx);
1148
1149         ctx->in.machine_name = talloc_strdup(mem_ctx, global_myname());
1150         W_ERROR_HAVE_NO_MEMORY(ctx->in.machine_name);
1151
1152         *r = ctx;
1153
1154         return WERR_OK;
1155 }
1156
1157 /****************************************************************
1158 ****************************************************************/
1159
1160 WERROR libnet_init_UnjoinCtx(TALLOC_CTX *mem_ctx,
1161                              struct libnet_UnjoinCtx **r)
1162 {
1163         struct libnet_UnjoinCtx *ctx;
1164
1165         ctx = talloc_zero(mem_ctx, struct libnet_UnjoinCtx);
1166         if (!ctx) {
1167                 return WERR_NOMEM;
1168         }
1169
1170         talloc_set_destructor(ctx, libnet_destroy_UnjoinCtx);
1171
1172         ctx->in.machine_name = talloc_strdup(mem_ctx, global_myname());
1173         W_ERROR_HAVE_NO_MEMORY(ctx->in.machine_name);
1174
1175         *r = ctx;
1176
1177         return WERR_OK;
1178 }
1179
1180 /****************************************************************
1181 ****************************************************************/
1182
1183 static WERROR libnet_DomainJoin(TALLOC_CTX *mem_ctx,
1184                                 struct libnet_JoinCtx *r)
1185 {
1186         NTSTATUS status;
1187 #ifdef WITH_ADS
1188         ADS_STATUS ads_status;
1189 #endif /* WITH_ADS */
1190
1191         if (!r->in.dc_name) {
1192                 struct DS_DOMAIN_CONTROLLER_INFO *info;
1193                 status = dsgetdcname(mem_ctx,
1194                                      r->in.domain_name,
1195                                      NULL,
1196                                      NULL,
1197                                      DS_DIRECTORY_SERVICE_REQUIRED |
1198                                      DS_WRITABLE_REQUIRED |
1199                                      DS_RETURN_DNS_NAME,
1200                                      &info);
1201                 if (!NT_STATUS_IS_OK(status)) {
1202                         libnet_join_set_error_string(mem_ctx, r,
1203                                 "failed to find DC for domain %s",
1204                                 r->in.domain_name,
1205                                 get_friendly_nt_error_msg(status));
1206                         return WERR_DOMAIN_CONTROLLER_NOT_FOUND;
1207                 }
1208
1209                 r->in.dc_name = talloc_strdup(mem_ctx,
1210                                               info->domain_controller_name);
1211                 W_ERROR_HAVE_NO_MEMORY(r->in.dc_name);
1212         }
1213
1214 #ifdef WITH_ADS
1215         if (r->in.account_ou) {
1216
1217                 ads_status = libnet_join_connect_ads(mem_ctx, r);
1218                 if (!ADS_ERR_OK(ads_status)) {
1219                         return WERR_DEFAULT_JOIN_REQUIRED;
1220                 }
1221
1222                 ads_status = libnet_join_precreate_machine_acct(mem_ctx, r);
1223                 if (!ADS_ERR_OK(ads_status)) {
1224                         libnet_join_set_error_string(mem_ctx, r,
1225                                 "failed to precreate account in ou %s: %s",
1226                                 r->in.account_ou,
1227                                 ads_errstr(ads_status));
1228                         return WERR_DEFAULT_JOIN_REQUIRED;
1229                 }
1230
1231                 r->in.join_flags &= ~WKSSVC_JOIN_FLAGS_ACCOUNT_CREATE;
1232         }
1233 #endif /* WITH_ADS */
1234
1235         status = libnet_join_joindomain_rpc(mem_ctx, r);
1236         if (!NT_STATUS_IS_OK(status)) {
1237                 libnet_join_set_error_string(mem_ctx, r,
1238                         "failed to join domain over rpc: %s",
1239                         get_friendly_nt_error_msg(status));
1240                 if (NT_STATUS_EQUAL(status, NT_STATUS_USER_EXISTS)) {
1241                         return WERR_SETUP_ALREADY_JOINED;
1242                 }
1243                 return ntstatus_to_werror(status);
1244         }
1245
1246         if (!libnet_join_joindomain_store_secrets(mem_ctx, r)) {
1247                 return WERR_SETUP_NOT_JOINED;
1248         }
1249
1250 #ifdef WITH_ADS
1251         if (r->out.domain_is_ad) {
1252                 ads_status  = libnet_join_post_processing_ads(mem_ctx, r);
1253                 if (!ADS_ERR_OK(ads_status)) {
1254                         return WERR_GENERAL_FAILURE;
1255                 }
1256         }
1257 #endif /* WITH_ADS */
1258
1259         return WERR_OK;
1260 }
1261
1262 /****************************************************************
1263 ****************************************************************/
1264
1265 WERROR libnet_Join(TALLOC_CTX *mem_ctx,
1266                    struct libnet_JoinCtx *r)
1267 {
1268         WERROR werr;
1269
1270         if (r->in.debug) {
1271                 LIBNET_JOIN_IN_DUMP_CTX(mem_ctx, r);
1272         }
1273
1274         werr = libnet_join_pre_processing(mem_ctx, r);
1275         if (!W_ERROR_IS_OK(werr)) {
1276                 goto done;
1277         }
1278
1279         if (r->in.join_flags & WKSSVC_JOIN_FLAGS_JOIN_TYPE) {
1280                 werr = libnet_DomainJoin(mem_ctx, r);
1281                 if (!W_ERROR_IS_OK(werr)) {
1282                         goto done;
1283                 }
1284         }
1285
1286         werr = libnet_join_post_processing(mem_ctx, r);
1287         if (!W_ERROR_IS_OK(werr)) {
1288                 goto done;
1289         }
1290  done:
1291         r->out.result = werr;
1292
1293         if (r->in.debug) {
1294                 LIBNET_JOIN_OUT_DUMP_CTX(mem_ctx, r);
1295         }
1296         return werr;
1297 }
1298
1299 /****************************************************************
1300 ****************************************************************/
1301
1302 static WERROR libnet_DomainUnjoin(TALLOC_CTX *mem_ctx,
1303                                   struct libnet_UnjoinCtx *r)
1304 {
1305         NTSTATUS status;
1306
1307         if (!r->in.domain_sid) {
1308                 struct dom_sid sid;
1309                 if (!secrets_fetch_domain_sid(lp_workgroup(), &sid)) {
1310                         libnet_unjoin_set_error_string(mem_ctx, r,
1311                                 "Unable to fetch domain sid: are we joined?");
1312                         return WERR_SETUP_NOT_JOINED;
1313                 }
1314                 r->in.domain_sid = sid_dup_talloc(mem_ctx, &sid);
1315                 W_ERROR_HAVE_NO_MEMORY(r->in.domain_sid);
1316         }
1317
1318         if (!r->in.dc_name) {
1319                 struct DS_DOMAIN_CONTROLLER_INFO *info;
1320                 status = dsgetdcname(mem_ctx,
1321                                      r->in.domain_name,
1322                                      NULL,
1323                                      NULL,
1324                                      DS_DIRECTORY_SERVICE_REQUIRED |
1325                                      DS_WRITABLE_REQUIRED |
1326                                      DS_RETURN_DNS_NAME,
1327                                      &info);
1328                 if (!NT_STATUS_IS_OK(status)) {
1329                         libnet_unjoin_set_error_string(mem_ctx, r,
1330                                 "failed to find DC for domain %s",
1331                                 r->in.domain_name,
1332                                 get_friendly_nt_error_msg(status));
1333                         return WERR_DOMAIN_CONTROLLER_NOT_FOUND;
1334                 }
1335
1336                 r->in.dc_name = talloc_strdup(mem_ctx,
1337                                               info->domain_controller_name);
1338                 W_ERROR_HAVE_NO_MEMORY(r->in.dc_name);
1339         }
1340
1341         status = libnet_join_unjoindomain_rpc(mem_ctx, r);
1342         if (!NT_STATUS_IS_OK(status)) {
1343                 libnet_unjoin_set_error_string(mem_ctx, r,
1344                         "failed to disable machine account via rpc: %s",
1345                         get_friendly_nt_error_msg(status));
1346                 if (NT_STATUS_EQUAL(status, NT_STATUS_NO_SUCH_USER)) {
1347                         return WERR_SETUP_NOT_JOINED;
1348                 }
1349                 return ntstatus_to_werror(status);
1350         }
1351
1352 #ifdef WITH_ADS
1353         if (r->in.unjoin_flags & WKSSVC_JOIN_FLAGS_ACCOUNT_DELETE) {
1354                 ADS_STATUS ads_status;
1355                 libnet_unjoin_connect_ads(mem_ctx, r);
1356                 ads_status = libnet_unjoin_remove_machine_acct(mem_ctx, r);
1357                 if (!ADS_ERR_OK(ads_status)) {
1358                         libnet_unjoin_set_error_string(mem_ctx, r,
1359                                 "failed to remove machine account from AD: %s",
1360                                 ads_errstr(ads_status));
1361                 }
1362         }
1363 #endif /* WITH_ADS */
1364
1365         libnet_join_unjoindomain_remove_secrets(mem_ctx, r);
1366
1367         return WERR_OK;
1368 }
1369
1370 /****************************************************************
1371 ****************************************************************/
1372
1373 static WERROR libnet_unjoin_pre_processing(TALLOC_CTX *mem_ctx,
1374                                            struct libnet_UnjoinCtx *r)
1375 {
1376         if (r->in.modify_config && !lp_config_backend_is_registry()) {
1377                 return WERR_NOT_SUPPORTED;
1378         }
1379
1380         if (!secrets_init()) {
1381                 libnet_unjoin_set_error_string(mem_ctx, r,
1382                         "Unable to open secrets database");
1383                 return WERR_CAN_NOT_COMPLETE;
1384         }
1385
1386         return WERR_OK;
1387 }
1388
1389
1390 /****************************************************************
1391 ****************************************************************/
1392
1393 WERROR libnet_Unjoin(TALLOC_CTX *mem_ctx,
1394                      struct libnet_UnjoinCtx *r)
1395 {
1396         WERROR werr;
1397
1398         if (r->in.debug) {
1399                 LIBNET_UNJOIN_IN_DUMP_CTX(mem_ctx, r);
1400         }
1401
1402         werr = libnet_unjoin_pre_processing(mem_ctx, r);
1403         if (!W_ERROR_IS_OK(werr)) {
1404                 goto done;
1405         }
1406
1407         if (r->in.unjoin_flags & WKSSVC_JOIN_FLAGS_JOIN_TYPE) {
1408                 werr = libnet_DomainUnjoin(mem_ctx, r);
1409                 if (!W_ERROR_IS_OK(werr)) {
1410                         goto done;
1411                 }
1412         }
1413
1414         werr = do_UnjoinConfig(r);
1415         if (!W_ERROR_IS_OK(werr)) {
1416                 goto done;
1417         }
1418
1419  done:
1420         r->out.result = werr;
1421
1422         if (r->in.debug) {
1423                 LIBNET_UNJOIN_OUT_DUMP_CTX(mem_ctx, r);
1424         }
1425
1426         return werr;
1427 }