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