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