Remove all callers of rpccli_samr_setuserinfo2 and replace with
[kai/samba.git] / source / 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         struct lsa_String lsa_acct_name;
599         uint32 user_rid;
600         uint32 acb_info = ACB_WSTRUST;
601         uchar pwbuf[532];
602         struct MD5Context md5ctx;
603         uchar md5buffer[16];
604         DATA_BLOB digested_session_key;
605         uchar md4_trust_password[16];
606         union lsa_PolicyInformation *info = NULL;
607         struct samr_Ids user_rids;
608         struct samr_Ids name_types;
609         union samr_UserInfo user_info;
610
611         if (!r->in.machine_password) {
612                 r->in.machine_password = talloc_strdup(mem_ctx, generate_random_str(DEFAULT_TRUST_ACCOUNT_PASSWORD_LENGTH));
613                 NT_STATUS_HAVE_NO_MEMORY(r->in.machine_password);
614         }
615
616         status = cli_full_connection(&cli, NULL,
617                                      r->in.dc_name,
618                                      NULL, 0,
619                                      "IPC$", "IPC",
620                                      r->in.admin_account,
621                                      NULL,
622                                      r->in.admin_password,
623                                      0,
624                                      Undefined, NULL);
625
626         if (!NT_STATUS_IS_OK(status)) {
627                 goto done;
628         }
629
630         pipe_hnd = cli_rpc_pipe_open_noauth(cli, PI_LSARPC, &status);
631         if (!pipe_hnd) {
632                 goto done;
633         }
634
635         status = rpccli_lsa_open_policy(pipe_hnd, mem_ctx, True,
636                                         SEC_RIGHTS_MAXIMUM_ALLOWED, &lsa_pol);
637         if (!NT_STATUS_IS_OK(status)) {
638                 goto done;
639         }
640
641         status = rpccli_lsa_QueryInfoPolicy2(pipe_hnd, mem_ctx,
642                                              &lsa_pol,
643                                              LSA_POLICY_INFO_DNS,
644                                              &info);
645         if (NT_STATUS_IS_OK(status)) {
646                 r->out.domain_is_ad = true;
647                 r->out.netbios_domain_name = info->dns.name.string;
648                 r->out.dns_domain_name = info->dns.dns_domain.string;
649                 r->out.domain_sid = info->dns.sid;
650         }
651
652         if (!NT_STATUS_IS_OK(status)) {
653                 status = rpccli_lsa_QueryInfoPolicy(pipe_hnd, mem_ctx,
654                                                     &lsa_pol,
655                                                     LSA_POLICY_INFO_ACCOUNT_DOMAIN,
656                                                     &info);
657                 if (!NT_STATUS_IS_OK(status)) {
658                         goto done;
659                 }
660
661                 r->out.netbios_domain_name = info->account_domain.name.string;
662                 r->out.domain_sid = info->account_domain.sid;
663         }
664
665         rpccli_lsa_Close(pipe_hnd, mem_ctx, &lsa_pol);
666         cli_rpc_pipe_close(pipe_hnd);
667
668         pipe_hnd = cli_rpc_pipe_open_noauth(cli, PI_SAMR, &status);
669         if (!pipe_hnd) {
670                 goto done;
671         }
672
673         status = rpccli_samr_Connect2(pipe_hnd, mem_ctx,
674                                       pipe_hnd->cli->desthost,
675                                       SEC_RIGHTS_MAXIMUM_ALLOWED,
676                                       &sam_pol);
677         if (!NT_STATUS_IS_OK(status)) {
678                 goto done;
679         }
680
681         status = rpccli_samr_OpenDomain(pipe_hnd, mem_ctx,
682                                         &sam_pol,
683                                         SEC_RIGHTS_MAXIMUM_ALLOWED,
684                                         r->out.domain_sid,
685                                         &domain_pol);
686         if (!NT_STATUS_IS_OK(status)) {
687                 goto done;
688         }
689
690         acct_name = talloc_asprintf(mem_ctx, "%s$", r->in.machine_name);
691         strlower_m(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_LookupNames(pipe_hnd, mem_ctx,
725                                          &domain_pol,
726                                          1,
727                                          &lsa_acct_name,
728                                          &user_rids,
729                                          &name_types);
730         if (!NT_STATUS_IS_OK(status)) {
731                 goto done;
732         }
733
734         if (name_types.ids[0] != SID_NAME_USER) {
735                 status = NT_STATUS_INVALID_WORKSTATION;
736                 goto done;
737         }
738
739         user_rid = user_rids.ids[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(user_info.info25);
774
775         user_info.info25.info.fields_present = ACCT_NT_PWD_SET |
776                                                ACCT_LM_PWD_SET |
777                                                SAMR_FIELD_ACCT_FLAGS;
778         user_info.info25.info.acct_flags = acb_info;
779         memcpy(&user_info.info25.password.data, pwbuf, sizeof(pwbuf));
780
781         status = rpccli_samr_SetUserInfo(pipe_hnd, mem_ctx,
782                                          &user_pol,
783                                          25,
784                                          &user_info);
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 user_rid;
830         struct lsa_String lsa_acct_name;
831         struct samr_Ids user_rids;
832         struct samr_Ids name_types;
833         union samr_UserInfo *info = NULL;
834
835         status = cli_full_connection(&cli, NULL,
836                                      r->in.dc_name,
837                                      NULL, 0,
838                                      "IPC$", "IPC",
839                                      r->in.admin_account,
840                                      NULL,
841                                      r->in.admin_password,
842                                      0, Undefined, NULL);
843
844         if (!NT_STATUS_IS_OK(status)) {
845                 goto done;
846         }
847
848         pipe_hnd = cli_rpc_pipe_open_noauth(cli, PI_SAMR, &status);
849         if (!pipe_hnd) {
850                 goto done;
851         }
852
853         status = rpccli_samr_Connect2(pipe_hnd, mem_ctx,
854                                       pipe_hnd->cli->desthost,
855                                       SEC_RIGHTS_MAXIMUM_ALLOWED,
856                                       &sam_pol);
857         if (!NT_STATUS_IS_OK(status)) {
858                 goto done;
859         }
860
861         status = rpccli_samr_OpenDomain(pipe_hnd, mem_ctx,
862                                         &sam_pol,
863                                         SEC_RIGHTS_MAXIMUM_ALLOWED,
864                                         r->in.domain_sid,
865                                         &domain_pol);
866         if (!NT_STATUS_IS_OK(status)) {
867                 goto done;
868         }
869
870         acct_name = talloc_asprintf(mem_ctx, "%s$", r->in.machine_name);
871         strlower_m(acct_name);
872
873         init_lsa_String(&lsa_acct_name, acct_name);
874
875         status = rpccli_samr_LookupNames(pipe_hnd, mem_ctx,
876                                          &domain_pol,
877                                          1,
878                                          &lsa_acct_name,
879                                          &user_rids,
880                                          &name_types);
881
882         if (!NT_STATUS_IS_OK(status)) {
883                 goto done;
884         }
885
886         if (name_types.ids[0] != SID_NAME_USER) {
887                 status = NT_STATUS_INVALID_WORKSTATION;
888                 goto done;
889         }
890
891         user_rid = user_rids.ids[0];
892
893         status = rpccli_samr_OpenUser(pipe_hnd, mem_ctx,
894                                       &domain_pol,
895                                       SEC_RIGHTS_MAXIMUM_ALLOWED,
896                                       user_rid,
897                                       &user_pol);
898         if (!NT_STATUS_IS_OK(status)) {
899                 goto done;
900         }
901
902         status = rpccli_samr_QueryUserInfo(pipe_hnd, mem_ctx,
903                                            &user_pol,
904                                            16,
905                                            &info);
906         if (!NT_STATUS_IS_OK(status)) {
907                 rpccli_samr_Close(pipe_hnd, mem_ctx, &user_pol);
908                 goto done;
909         }
910
911         info->info16.acct_flags |= ACB_DISABLED;
912
913         status = rpccli_samr_SetUserInfo(pipe_hnd, mem_ctx,
914                                          &user_pol,
915                                          16,
916                                          info);
917
918         rpccli_samr_Close(pipe_hnd, mem_ctx, &user_pol);
919
920 done:
921         if (pipe_hnd) {
922                 rpccli_samr_Close(pipe_hnd, mem_ctx, &domain_pol);
923                 rpccli_samr_Close(pipe_hnd, mem_ctx, &sam_pol);
924                 cli_rpc_pipe_close(pipe_hnd);
925         }
926
927         if (cli) {
928                 cli_shutdown(cli);
929         }
930
931         return status;
932 }
933
934 /****************************************************************
935 ****************************************************************/
936
937 static WERROR do_join_modify_vals_config(struct libnet_JoinCtx *r)
938 {
939         WERROR werr;
940         struct libnet_conf_ctx *ctx;
941
942         werr = libnet_conf_open(r, &ctx);
943         if (!W_ERROR_IS_OK(werr)) {
944                 goto done;
945         }
946
947         if (!(r->in.join_flags & WKSSVC_JOIN_FLAGS_JOIN_TYPE)) {
948
949                 werr = libnet_conf_set_global_parameter(ctx, "security", "user");
950                 if (!W_ERROR_IS_OK(werr)) {
951                         goto done;
952                 }
953
954                 werr = libnet_conf_set_global_parameter(ctx, "workgroup",
955                                                         r->in.domain_name);
956                 goto done;
957         }
958
959         werr = libnet_conf_set_global_parameter(ctx, "security", "domain");
960         if (!W_ERROR_IS_OK(werr)) {
961                 goto done;
962         }
963
964         werr = libnet_conf_set_global_parameter(ctx, "workgroup",
965                                                 r->out.netbios_domain_name);
966         if (!W_ERROR_IS_OK(werr)) {
967                 goto done;
968         }
969
970         if (r->out.domain_is_ad) {
971                 werr = libnet_conf_set_global_parameter(ctx, "security", "ads");
972                 if (!W_ERROR_IS_OK(werr)) {
973                         goto done;
974                 }
975
976                 werr = libnet_conf_set_global_parameter(ctx, "realm",
977                                                         r->out.dns_domain_name);
978         }
979
980 done:
981         libnet_conf_close(ctx);
982         return werr;
983 }
984
985 /****************************************************************
986 ****************************************************************/
987
988 static WERROR do_unjoin_modify_vals_config(struct libnet_UnjoinCtx *r)
989 {
990         WERROR werr = WERR_OK;
991         struct libnet_conf_ctx *ctx;
992
993         werr = libnet_conf_open(r, &ctx);
994         if (!W_ERROR_IS_OK(werr)) {
995                 goto done;
996         }
997
998         if (r->in.unjoin_flags & WKSSVC_JOIN_FLAGS_JOIN_TYPE) {
999
1000                 werr = libnet_conf_set_global_parameter(ctx, "security", "user");
1001                 if (!W_ERROR_IS_OK(werr)) {
1002                         goto done;
1003                 }
1004         }
1005
1006         libnet_conf_delete_global_parameter(ctx, "realm");
1007
1008 done:
1009         libnet_conf_close(ctx);
1010         return werr;
1011 }
1012
1013 /****************************************************************
1014 ****************************************************************/
1015
1016 static WERROR do_JoinConfig(struct libnet_JoinCtx *r)
1017 {
1018         WERROR werr;
1019
1020         if (!W_ERROR_IS_OK(r->out.result)) {
1021                 return r->out.result;
1022         }
1023
1024         if (!r->in.modify_config) {
1025                 return WERR_OK;
1026         }
1027
1028         werr = do_join_modify_vals_config(r);
1029         if (!W_ERROR_IS_OK(werr)) {
1030                 return werr;
1031         }
1032
1033         r->out.modified_config = true;
1034         r->out.result = werr;
1035
1036         return werr;
1037 }
1038
1039 /****************************************************************
1040 ****************************************************************/
1041
1042 static WERROR do_UnjoinConfig(struct libnet_UnjoinCtx *r)
1043 {
1044         WERROR werr;
1045
1046         if (!W_ERROR_IS_OK(r->out.result)) {
1047                 return r->out.result;
1048         }
1049
1050         if (!r->in.modify_config) {
1051                 return WERR_OK;
1052         }
1053
1054         werr = do_unjoin_modify_vals_config(r);
1055         if (!W_ERROR_IS_OK(werr)) {
1056                 return werr;
1057         }
1058
1059         r->out.modified_config = true;
1060         r->out.result = werr;
1061
1062         return werr;
1063 }
1064
1065 /****************************************************************
1066 ****************************************************************/
1067
1068 static WERROR libnet_join_pre_processing(TALLOC_CTX *mem_ctx,
1069                                          struct libnet_JoinCtx *r)
1070 {
1071
1072         if (!r->in.domain_name) {
1073                 return WERR_INVALID_PARAM;
1074         }
1075
1076         if (r->in.modify_config && !lp_config_backend_is_registry()) {
1077                 return WERR_NOT_SUPPORTED;
1078         }
1079
1080         if (IS_DC) {
1081                 return WERR_SETUP_DOMAIN_CONTROLLER;
1082         }
1083
1084         if (!secrets_init()) {
1085                 libnet_join_set_error_string(mem_ctx, r,
1086                         "Unable to open secrets database");
1087                 return WERR_CAN_NOT_COMPLETE;
1088         }
1089
1090         return WERR_OK;
1091 }
1092
1093 /****************************************************************
1094 ****************************************************************/
1095
1096 static WERROR libnet_join_post_processing(TALLOC_CTX *mem_ctx,
1097                                           struct libnet_JoinCtx *r)
1098 {
1099         WERROR werr;
1100
1101         if (!W_ERROR_IS_OK(r->out.result)) {
1102                 return r->out.result;
1103         }
1104
1105         werr = do_JoinConfig(r);
1106         if (!W_ERROR_IS_OK(werr)) {
1107                 return werr;
1108         }
1109
1110         if (r->in.join_flags & WKSSVC_JOIN_FLAGS_JOIN_TYPE) {
1111                 saf_store(r->in.domain_name, r->in.dc_name);
1112         }
1113
1114         return WERR_OK;
1115 }
1116
1117 /****************************************************************
1118 ****************************************************************/
1119
1120 static int libnet_destroy_JoinCtx(struct libnet_JoinCtx *r)
1121 {
1122         if (r->in.ads) {
1123                 ads_destroy(&r->in.ads);
1124         }
1125
1126         return 0;
1127 }
1128
1129 /****************************************************************
1130 ****************************************************************/
1131
1132 static int libnet_destroy_UnjoinCtx(struct libnet_UnjoinCtx *r)
1133 {
1134         if (r->in.ads) {
1135                 ads_destroy(&r->in.ads);
1136         }
1137
1138         return 0;
1139 }
1140
1141 /****************************************************************
1142 ****************************************************************/
1143
1144 WERROR libnet_init_JoinCtx(TALLOC_CTX *mem_ctx,
1145                            struct libnet_JoinCtx **r)
1146 {
1147         struct libnet_JoinCtx *ctx;
1148
1149         ctx = talloc_zero(mem_ctx, struct libnet_JoinCtx);
1150         if (!ctx) {
1151                 return WERR_NOMEM;
1152         }
1153
1154         talloc_set_destructor(ctx, libnet_destroy_JoinCtx);
1155
1156         ctx->in.machine_name = talloc_strdup(mem_ctx, global_myname());
1157         W_ERROR_HAVE_NO_MEMORY(ctx->in.machine_name);
1158
1159         *r = ctx;
1160
1161         return WERR_OK;
1162 }
1163
1164 /****************************************************************
1165 ****************************************************************/
1166
1167 WERROR libnet_init_UnjoinCtx(TALLOC_CTX *mem_ctx,
1168                              struct libnet_UnjoinCtx **r)
1169 {
1170         struct libnet_UnjoinCtx *ctx;
1171
1172         ctx = talloc_zero(mem_ctx, struct libnet_UnjoinCtx);
1173         if (!ctx) {
1174                 return WERR_NOMEM;
1175         }
1176
1177         talloc_set_destructor(ctx, libnet_destroy_UnjoinCtx);
1178
1179         ctx->in.machine_name = talloc_strdup(mem_ctx, global_myname());
1180         W_ERROR_HAVE_NO_MEMORY(ctx->in.machine_name);
1181
1182         *r = ctx;
1183
1184         return WERR_OK;
1185 }
1186
1187 /****************************************************************
1188 ****************************************************************/
1189
1190 static WERROR libnet_DomainJoin(TALLOC_CTX *mem_ctx,
1191                                 struct libnet_JoinCtx *r)
1192 {
1193         NTSTATUS status;
1194 #ifdef WITH_ADS
1195         ADS_STATUS ads_status;
1196 #endif /* WITH_ADS */
1197
1198         if (!r->in.dc_name) {
1199                 struct DS_DOMAIN_CONTROLLER_INFO *info;
1200                 status = dsgetdcname(mem_ctx,
1201                                      r->in.domain_name,
1202                                      NULL,
1203                                      NULL,
1204                                      DS_DIRECTORY_SERVICE_REQUIRED |
1205                                      DS_WRITABLE_REQUIRED |
1206                                      DS_RETURN_DNS_NAME,
1207                                      &info);
1208                 if (!NT_STATUS_IS_OK(status)) {
1209                         libnet_join_set_error_string(mem_ctx, r,
1210                                 "failed to find DC for domain %s",
1211                                 r->in.domain_name,
1212                                 get_friendly_nt_error_msg(status));
1213                         return WERR_DOMAIN_CONTROLLER_NOT_FOUND;
1214                 }
1215
1216                 r->in.dc_name = talloc_strdup(mem_ctx,
1217                                               info->domain_controller_name);
1218                 W_ERROR_HAVE_NO_MEMORY(r->in.dc_name);
1219         }
1220
1221 #ifdef WITH_ADS
1222         if (r->in.account_ou) {
1223
1224                 ads_status = libnet_join_connect_ads(mem_ctx, r);
1225                 if (!ADS_ERR_OK(ads_status)) {
1226                         return WERR_DEFAULT_JOIN_REQUIRED;
1227                 }
1228
1229                 ads_status = libnet_join_precreate_machine_acct(mem_ctx, r);
1230                 if (!ADS_ERR_OK(ads_status)) {
1231                         libnet_join_set_error_string(mem_ctx, r,
1232                                 "failed to precreate account in ou %s: %s",
1233                                 r->in.account_ou,
1234                                 ads_errstr(ads_status));
1235                         return WERR_DEFAULT_JOIN_REQUIRED;
1236                 }
1237
1238                 r->in.join_flags &= ~WKSSVC_JOIN_FLAGS_ACCOUNT_CREATE;
1239         }
1240 #endif /* WITH_ADS */
1241
1242         status = libnet_join_joindomain_rpc(mem_ctx, r);
1243         if (!NT_STATUS_IS_OK(status)) {
1244                 libnet_join_set_error_string(mem_ctx, r,
1245                         "failed to join domain over rpc: %s",
1246                         get_friendly_nt_error_msg(status));
1247                 if (NT_STATUS_EQUAL(status, NT_STATUS_USER_EXISTS)) {
1248                         return WERR_SETUP_ALREADY_JOINED;
1249                 }
1250                 return ntstatus_to_werror(status);
1251         }
1252
1253         if (!libnet_join_joindomain_store_secrets(mem_ctx, r)) {
1254                 return WERR_SETUP_NOT_JOINED;
1255         }
1256
1257 #ifdef WITH_ADS
1258         if (r->out.domain_is_ad) {
1259                 ads_status  = libnet_join_post_processing_ads(mem_ctx, r);
1260                 if (!ADS_ERR_OK(ads_status)) {
1261                         return WERR_GENERAL_FAILURE;
1262                 }
1263         }
1264 #endif /* WITH_ADS */
1265
1266         return WERR_OK;
1267 }
1268
1269 /****************************************************************
1270 ****************************************************************/
1271
1272 WERROR libnet_Join(TALLOC_CTX *mem_ctx,
1273                    struct libnet_JoinCtx *r)
1274 {
1275         WERROR werr;
1276
1277         if (r->in.debug) {
1278                 LIBNET_JOIN_IN_DUMP_CTX(mem_ctx, r);
1279         }
1280
1281         werr = libnet_join_pre_processing(mem_ctx, r);
1282         if (!W_ERROR_IS_OK(werr)) {
1283                 goto done;
1284         }
1285
1286         if (r->in.join_flags & WKSSVC_JOIN_FLAGS_JOIN_TYPE) {
1287                 werr = libnet_DomainJoin(mem_ctx, r);
1288                 if (!W_ERROR_IS_OK(werr)) {
1289                         goto done;
1290                 }
1291         }
1292
1293         werr = libnet_join_post_processing(mem_ctx, r);
1294         if (!W_ERROR_IS_OK(werr)) {
1295                 goto done;
1296         }
1297  done:
1298         r->out.result = werr;
1299
1300         if (r->in.debug) {
1301                 LIBNET_JOIN_OUT_DUMP_CTX(mem_ctx, r);
1302         }
1303         return werr;
1304 }
1305
1306 /****************************************************************
1307 ****************************************************************/
1308
1309 static WERROR libnet_DomainUnjoin(TALLOC_CTX *mem_ctx,
1310                                   struct libnet_UnjoinCtx *r)
1311 {
1312         NTSTATUS status;
1313
1314         if (!r->in.domain_sid) {
1315                 struct dom_sid sid;
1316                 if (!secrets_fetch_domain_sid(lp_workgroup(), &sid)) {
1317                         libnet_unjoin_set_error_string(mem_ctx, r,
1318                                 "Unable to fetch domain sid: are we joined?");
1319                         return WERR_SETUP_NOT_JOINED;
1320                 }
1321                 r->in.domain_sid = sid_dup_talloc(mem_ctx, &sid);
1322                 W_ERROR_HAVE_NO_MEMORY(r->in.domain_sid);
1323         }
1324
1325         if (!r->in.dc_name) {
1326                 struct DS_DOMAIN_CONTROLLER_INFO *info;
1327                 status = dsgetdcname(mem_ctx,
1328                                      r->in.domain_name,
1329                                      NULL,
1330                                      NULL,
1331                                      DS_DIRECTORY_SERVICE_REQUIRED |
1332                                      DS_WRITABLE_REQUIRED |
1333                                      DS_RETURN_DNS_NAME,
1334                                      &info);
1335                 if (!NT_STATUS_IS_OK(status)) {
1336                         libnet_unjoin_set_error_string(mem_ctx, r,
1337                                 "failed to find DC for domain %s",
1338                                 r->in.domain_name,
1339                                 get_friendly_nt_error_msg(status));
1340                         return WERR_DOMAIN_CONTROLLER_NOT_FOUND;
1341                 }
1342
1343                 r->in.dc_name = talloc_strdup(mem_ctx,
1344                                               info->domain_controller_name);
1345                 W_ERROR_HAVE_NO_MEMORY(r->in.dc_name);
1346         }
1347
1348         status = libnet_join_unjoindomain_rpc(mem_ctx, r);
1349         if (!NT_STATUS_IS_OK(status)) {
1350                 libnet_unjoin_set_error_string(mem_ctx, r,
1351                         "failed to disable machine account via rpc: %s",
1352                         get_friendly_nt_error_msg(status));
1353                 if (NT_STATUS_EQUAL(status, NT_STATUS_NO_SUCH_USER)) {
1354                         return WERR_SETUP_NOT_JOINED;
1355                 }
1356                 return ntstatus_to_werror(status);
1357         }
1358
1359 #ifdef WITH_ADS
1360         if (r->in.unjoin_flags & WKSSVC_JOIN_FLAGS_ACCOUNT_DELETE) {
1361                 ADS_STATUS ads_status;
1362                 libnet_unjoin_connect_ads(mem_ctx, r);
1363                 ads_status = libnet_unjoin_remove_machine_acct(mem_ctx, r);
1364                 if (!ADS_ERR_OK(ads_status)) {
1365                         libnet_unjoin_set_error_string(mem_ctx, r,
1366                                 "failed to remove machine account from AD: %s",
1367                                 ads_errstr(ads_status));
1368                 }
1369         }
1370 #endif /* WITH_ADS */
1371
1372         libnet_join_unjoindomain_remove_secrets(mem_ctx, r);
1373
1374         return WERR_OK;
1375 }
1376
1377 /****************************************************************
1378 ****************************************************************/
1379
1380 static WERROR libnet_unjoin_pre_processing(TALLOC_CTX *mem_ctx,
1381                                            struct libnet_UnjoinCtx *r)
1382 {
1383         if (r->in.modify_config && !lp_config_backend_is_registry()) {
1384                 return WERR_NOT_SUPPORTED;
1385         }
1386
1387         if (!secrets_init()) {
1388                 libnet_unjoin_set_error_string(mem_ctx, r,
1389                         "Unable to open secrets database");
1390                 return WERR_CAN_NOT_COMPLETE;
1391         }
1392
1393         return WERR_OK;
1394 }
1395
1396
1397 /****************************************************************
1398 ****************************************************************/
1399
1400 WERROR libnet_Unjoin(TALLOC_CTX *mem_ctx,
1401                      struct libnet_UnjoinCtx *r)
1402 {
1403         WERROR werr;
1404
1405         if (r->in.debug) {
1406                 LIBNET_UNJOIN_IN_DUMP_CTX(mem_ctx, r);
1407         }
1408
1409         werr = libnet_unjoin_pre_processing(mem_ctx, r);
1410         if (!W_ERROR_IS_OK(werr)) {
1411                 goto done;
1412         }
1413
1414         if (r->in.unjoin_flags & WKSSVC_JOIN_FLAGS_JOIN_TYPE) {
1415                 werr = libnet_DomainUnjoin(mem_ctx, r);
1416                 if (!W_ERROR_IS_OK(werr)) {
1417                         goto done;
1418                 }
1419         }
1420
1421         werr = do_UnjoinConfig(r);
1422         if (!W_ERROR_IS_OK(werr)) {
1423                 goto done;
1424         }
1425
1426  done:
1427         r->out.result = werr;
1428
1429         if (r->in.debug) {
1430                 LIBNET_UNJOIN_OUT_DUMP_CTX(mem_ctx, r);
1431         }
1432
1433         return werr;
1434 }