s3:libnet:libnet_join: always try to create machineaccount via LDAP first.
[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 "ads.h"
23 #include "librpc/gen_ndr/ndr_libnet_join.h"
24 #include "libnet/libnet_join.h"
25 #include "libcli/auth/libcli_auth.h"
26 #include "../librpc/gen_ndr/ndr_samr_c.h"
27 #include "rpc_client/init_samr.h"
28 #include "../librpc/gen_ndr/ndr_lsa_c.h"
29 #include "rpc_client/cli_lsarpc.h"
30 #include "../librpc/gen_ndr/ndr_netlogon.h"
31 #include "rpc_client/cli_netlogon.h"
32 #include "lib/smbconf/smbconf.h"
33 #include "lib/smbconf/smbconf_reg.h"
34 #include "../libds/common/flags.h"
35 #include "secrets.h"
36 #include "rpc_client/init_lsa.h"
37 #include "rpc_client/cli_pipe.h"
38 #include "../libcli/security/security.h"
39 #include "passdb.h"
40 #include "libsmb/libsmb.h"
41 #include "../libcli/smb/smbXcli_base.h"
42 #include "lib/param/loadparm.h"
43 #include "libcli/auth/netlogon_creds_cli.h"
44 #include "auth/credentials/credentials.h"
45 #include "krb5_env.h"
46
47 /****************************************************************
48 ****************************************************************/
49
50 #define LIBNET_JOIN_DUMP_CTX(ctx, r, f) \
51         do { \
52                 char *str = NULL; \
53                 str = NDR_PRINT_FUNCTION_STRING(ctx, libnet_JoinCtx, f, r); \
54                 DEBUG(1,("libnet_Join:\n%s", str)); \
55                 TALLOC_FREE(str); \
56         } while (0)
57
58 #define LIBNET_JOIN_IN_DUMP_CTX(ctx, r) \
59         LIBNET_JOIN_DUMP_CTX(ctx, r, NDR_IN | NDR_SET_VALUES)
60 #define LIBNET_JOIN_OUT_DUMP_CTX(ctx, r) \
61         LIBNET_JOIN_DUMP_CTX(ctx, r, NDR_OUT)
62
63 #define LIBNET_UNJOIN_DUMP_CTX(ctx, r, f) \
64         do { \
65                 char *str = NULL; \
66                 str = NDR_PRINT_FUNCTION_STRING(ctx, libnet_UnjoinCtx, f, r); \
67                 DEBUG(1,("libnet_Unjoin:\n%s", str)); \
68                 TALLOC_FREE(str); \
69         } while (0)
70
71 #define LIBNET_UNJOIN_IN_DUMP_CTX(ctx, r) \
72         LIBNET_UNJOIN_DUMP_CTX(ctx, r, NDR_IN | NDR_SET_VALUES)
73 #define LIBNET_UNJOIN_OUT_DUMP_CTX(ctx, r) \
74         LIBNET_UNJOIN_DUMP_CTX(ctx, r, NDR_OUT)
75
76 /****************************************************************
77 ****************************************************************/
78
79 static void libnet_join_set_error_string(TALLOC_CTX *mem_ctx,
80                                          struct libnet_JoinCtx *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 /****************************************************************
95 ****************************************************************/
96
97 static void libnet_unjoin_set_error_string(TALLOC_CTX *mem_ctx,
98                                            struct libnet_UnjoinCtx *r,
99                                            const char *format, ...)
100 {
101         va_list args;
102
103         if (r->out.error_string) {
104                 return;
105         }
106
107         va_start(args, format);
108         r->out.error_string = talloc_vasprintf(mem_ctx, format, args);
109         va_end(args);
110 }
111
112 #ifdef HAVE_ADS
113
114 /****************************************************************
115 ****************************************************************/
116
117 static ADS_STATUS libnet_connect_ads(const char *dns_domain_name,
118                                      const char *netbios_domain_name,
119                                      const char *dc_name,
120                                      const char *user_name,
121                                      const char *password,
122                                      const char *ccname,
123                                      ADS_STRUCT **ads)
124 {
125         ADS_STATUS status;
126         ADS_STRUCT *my_ads = NULL;
127         char *cp;
128
129         my_ads = ads_init(dns_domain_name,
130                           netbios_domain_name,
131                           dc_name);
132         if (!my_ads) {
133                 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
134         }
135
136         if (user_name) {
137                 SAFE_FREE(my_ads->auth.user_name);
138                 my_ads->auth.user_name = SMB_STRDUP(user_name);
139                 if ((cp = strchr_m(my_ads->auth.user_name, '@'))!=0) {
140                         *cp++ = '\0';
141                         SAFE_FREE(my_ads->auth.realm);
142                         my_ads->auth.realm = smb_xstrdup(cp);
143                         if (!strupper_m(my_ads->auth.realm)) {
144                                 ads_destroy(&my_ads);
145                                 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
146                         }
147                 }
148         }
149
150         if (password) {
151                 SAFE_FREE(my_ads->auth.password);
152                 my_ads->auth.password = SMB_STRDUP(password);
153         }
154
155         if (ccname != NULL) {
156                 SAFE_FREE(my_ads->auth.ccache_name);
157                 my_ads->auth.ccache_name = SMB_STRDUP(ccname);
158                 setenv(KRB5_ENV_CCNAME, my_ads->auth.ccache_name, 1);
159         }
160
161         status = ads_connect_user_creds(my_ads);
162         if (!ADS_ERR_OK(status)) {
163                 ads_destroy(&my_ads);
164                 return status;
165         }
166
167         *ads = my_ads;
168         return ADS_SUCCESS;
169 }
170
171 /****************************************************************
172 ****************************************************************/
173
174 static ADS_STATUS libnet_join_connect_ads(TALLOC_CTX *mem_ctx,
175                                           struct libnet_JoinCtx *r,
176                                           bool use_machine_creds)
177 {
178         ADS_STATUS status;
179         const char *username;
180         const char *password;
181         const char *ccname = NULL;
182
183         if (use_machine_creds) {
184                 if (r->in.machine_name == NULL ||
185                     r->in.machine_password == NULL) {
186                         return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
187                 }
188                 username = talloc_strdup(mem_ctx, r->in.machine_name);
189                 if (username == NULL) {
190                         return ADS_ERROR(LDAP_NO_MEMORY);
191                 }
192                 if (username[strlen(username)] != '$') {
193                         username = talloc_asprintf(username, "%s$", username);
194                         if (username == NULL) {
195                                 return ADS_ERROR(LDAP_NO_MEMORY);
196                         }
197                 }
198                 password = r->in.machine_password;
199                 ccname = "MEMORY:libnet_join_machine_creds";
200         } else {
201                 username = r->in.admin_account;
202                 password = r->in.admin_password;
203
204                 /*
205                  * when r->in.use_kerberos is set to allow "net ads join -k" we
206                  * may not override the provided credential cache - gd
207                  */
208
209                 if (!r->in.use_kerberos) {
210                         ccname = "MEMORY:libnet_join_user_creds";
211                 }
212         }
213
214         status = libnet_connect_ads(r->out.dns_domain_name,
215                                     r->out.netbios_domain_name,
216                                     r->in.dc_name,
217                                     username,
218                                     password,
219                                     ccname,
220                                     &r->in.ads);
221         if (!ADS_ERR_OK(status)) {
222                 libnet_join_set_error_string(mem_ctx, r,
223                         "failed to connect to AD: %s",
224                         ads_errstr(status));
225                 return status;
226         }
227
228         if (!r->out.netbios_domain_name) {
229                 r->out.netbios_domain_name = talloc_strdup(mem_ctx,
230                                                            r->in.ads->server.workgroup);
231                 ADS_ERROR_HAVE_NO_MEMORY(r->out.netbios_domain_name);
232         }
233
234         if (!r->out.dns_domain_name) {
235                 r->out.dns_domain_name = talloc_strdup(mem_ctx,
236                                                        r->in.ads->config.realm);
237                 ADS_ERROR_HAVE_NO_MEMORY(r->out.dns_domain_name);
238         }
239
240         r->out.domain_is_ad = true;
241
242         return ADS_SUCCESS;
243 }
244
245 /****************************************************************
246 ****************************************************************/
247
248 static ADS_STATUS libnet_join_connect_ads_user(TALLOC_CTX *mem_ctx,
249                                                struct libnet_JoinCtx *r)
250 {
251         return libnet_join_connect_ads(mem_ctx, r, false);
252 }
253
254 /****************************************************************
255 ****************************************************************/
256 #if 0
257 static ADS_STATUS libnet_join_connect_ads_machine(TALLOC_CTX *mem_ctx,
258                                                   struct libnet_JoinCtx *r)
259 {
260         return libnet_join_connect_ads(mem_ctx, r, true);
261 }
262 #endif
263 /****************************************************************
264 ****************************************************************/
265
266 static ADS_STATUS libnet_unjoin_connect_ads(TALLOC_CTX *mem_ctx,
267                                             struct libnet_UnjoinCtx *r)
268 {
269         ADS_STATUS status;
270
271         status = libnet_connect_ads(r->in.domain_name,
272                                     r->in.domain_name,
273                                     r->in.dc_name,
274                                     r->in.admin_account,
275                                     r->in.admin_password,
276                                     NULL,
277                                     &r->in.ads);
278         if (!ADS_ERR_OK(status)) {
279                 libnet_unjoin_set_error_string(mem_ctx, r,
280                         "failed to connect to AD: %s",
281                         ads_errstr(status));
282         }
283
284         return status;
285 }
286
287 /****************************************************************
288  join a domain using ADS (LDAP mods)
289 ****************************************************************/
290
291 static ADS_STATUS libnet_join_precreate_machine_acct(TALLOC_CTX *mem_ctx,
292                                                      struct libnet_JoinCtx *r)
293 {
294         ADS_STATUS status;
295         LDAPMessage *res = NULL;
296         const char *attrs[] = { "dn", NULL };
297         bool moved = false;
298
299         status = ads_check_ou_dn(mem_ctx, r->in.ads, &r->in.account_ou);
300         if (!ADS_ERR_OK(status)) {
301                 return status;
302         }
303
304         status = ads_search_dn(r->in.ads, &res, r->in.account_ou, attrs);
305         if (!ADS_ERR_OK(status)) {
306                 return status;
307         }
308
309         if (ads_count_replies(r->in.ads, res) != 1) {
310                 ads_msgfree(r->in.ads, res);
311                 return ADS_ERROR_LDAP(LDAP_NO_SUCH_OBJECT);
312         }
313
314         ads_msgfree(r->in.ads, res);
315
316         /* Attempt to create the machine account and bail if this fails.
317            Assume that the admin wants exactly what they requested */
318
319         status = ads_create_machine_acct(r->in.ads,
320                                          r->in.machine_name,
321                                          r->in.account_ou);
322
323         if (ADS_ERR_OK(status)) {
324                 DEBUG(1,("machine account creation created\n"));
325                 return status;
326         } else  if ((status.error_type == ENUM_ADS_ERROR_LDAP) &&
327                     (status.err.rc == LDAP_ALREADY_EXISTS)) {
328                 status = ADS_SUCCESS;
329         }
330
331         if (!ADS_ERR_OK(status)) {
332                 DEBUG(1,("machine account creation failed\n"));
333                 return status;
334         }
335
336         status = ads_move_machine_acct(r->in.ads,
337                                        r->in.machine_name,
338                                        r->in.account_ou,
339                                        &moved);
340         if (!ADS_ERR_OK(status)) {
341                 DEBUG(1,("failure to locate/move pre-existing "
342                         "machine account\n"));
343                 return status;
344         }
345
346         DEBUG(1,("The machine account %s the specified OU.\n",
347                 moved ? "was moved into" : "already exists in"));
348
349         return status;
350 }
351
352 /****************************************************************
353 ****************************************************************/
354
355 static ADS_STATUS libnet_unjoin_remove_machine_acct(TALLOC_CTX *mem_ctx,
356                                                     struct libnet_UnjoinCtx *r)
357 {
358         ADS_STATUS status;
359
360         if (!r->in.ads) {
361                 status = libnet_unjoin_connect_ads(mem_ctx, r);
362                 if (!ADS_ERR_OK(status)) {
363                         libnet_unjoin_set_error_string(mem_ctx, r,
364                                 "failed to connect to AD: %s",
365                                 ads_errstr(status));
366                         return status;
367                 }
368         }
369
370         status = ads_leave_realm(r->in.ads, r->in.machine_name);
371         if (!ADS_ERR_OK(status)) {
372                 libnet_unjoin_set_error_string(mem_ctx, r,
373                         "failed to leave realm: %s",
374                         ads_errstr(status));
375                 return status;
376         }
377
378         return ADS_SUCCESS;
379 }
380
381 /****************************************************************
382 ****************************************************************/
383
384 static ADS_STATUS libnet_join_find_machine_acct(TALLOC_CTX *mem_ctx,
385                                                 struct libnet_JoinCtx *r)
386 {
387         ADS_STATUS status;
388         LDAPMessage *res = NULL;
389         char *dn = NULL;
390
391         if (!r->in.machine_name) {
392                 return ADS_ERROR(LDAP_NO_MEMORY);
393         }
394
395         status = ads_find_machine_acct(r->in.ads,
396                                        &res,
397                                        r->in.machine_name);
398         if (!ADS_ERR_OK(status)) {
399                 return status;
400         }
401
402         if (ads_count_replies(r->in.ads, res) != 1) {
403                 status = ADS_ERROR_LDAP(LDAP_NO_MEMORY);
404                 goto done;
405         }
406
407         dn = ads_get_dn(r->in.ads, mem_ctx, res);
408         if (!dn) {
409                 status = ADS_ERROR_LDAP(LDAP_NO_MEMORY);
410                 goto done;
411         }
412
413         r->out.dn = talloc_strdup(mem_ctx, dn);
414         if (!r->out.dn) {
415                 status = ADS_ERROR_LDAP(LDAP_NO_MEMORY);
416                 goto done;
417         }
418
419  done:
420         ads_msgfree(r->in.ads, res);
421         TALLOC_FREE(dn);
422
423         return status;
424 }
425
426 static ADS_STATUS libnet_join_get_machine_spns(TALLOC_CTX *mem_ctx,
427                                                struct libnet_JoinCtx *r,
428                                                char ***spn_array,
429                                                size_t *num_spns)
430 {
431         ADS_STATUS status;
432
433         if (r->in.machine_name == NULL) {
434                 return ADS_ERROR_SYSTEM(EINVAL);
435         }
436
437         status = ads_get_service_principal_names(mem_ctx,
438                                                  r->in.ads,
439                                                  r->in.machine_name,
440                                                  spn_array,
441                                                  num_spns);
442
443         return status;
444 }
445
446 /****************************************************************
447  Set a machines dNSHostName and servicePrincipalName attributes
448 ****************************************************************/
449
450 static ADS_STATUS libnet_join_set_machine_spn(TALLOC_CTX *mem_ctx,
451                                               struct libnet_JoinCtx *r)
452 {
453         ADS_STATUS status;
454         ADS_MODLIST mods;
455         fstring my_fqdn;
456         const char **spn_array = NULL;
457         size_t num_spns = 0;
458         char *spn = NULL;
459         bool ok;
460
461         /* Find our DN */
462
463         status = libnet_join_find_machine_acct(mem_ctx, r);
464         if (!ADS_ERR_OK(status)) {
465                 return status;
466         }
467
468         status = libnet_join_get_machine_spns(mem_ctx,
469                                               r,
470                                               discard_const_p(char **, &spn_array),
471                                               &num_spns);
472         if (!ADS_ERR_OK(status)) {
473                 DEBUG(5, ("Retrieving the servicePrincipalNames failed.\n"));
474         }
475
476         /* Windows only creates HOST/shortname & HOST/fqdn. */
477
478         spn = talloc_asprintf(mem_ctx, "HOST/%s", r->in.machine_name);
479         if (!spn) {
480                 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
481         }
482         if (!strupper_m(spn)) {
483                 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
484         }
485
486         ok = ads_element_in_array(spn_array, num_spns, spn);
487         if (!ok) {
488                 ok = add_string_to_array(spn_array, spn,
489                                          &spn_array, &num_spns);
490                 if (!ok) {
491                         return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
492                 }
493         }
494
495         if (!name_to_fqdn(my_fqdn, r->in.machine_name)
496             || (strchr(my_fqdn, '.') == NULL)) {
497                 fstr_sprintf(my_fqdn, "%s.%s", r->in.machine_name,
498                              r->out.dns_domain_name);
499         }
500
501         if (!strlower_m(my_fqdn)) {
502                 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
503         }
504
505         if (!strequal(my_fqdn, r->in.machine_name)) {
506                 spn = talloc_asprintf(mem_ctx, "HOST/%s", my_fqdn);
507                 if (!spn) {
508                         return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
509                 }
510
511                 ok = ads_element_in_array(spn_array, num_spns, spn);
512                 if (!ok) {
513                         ok = add_string_to_array(spn_array, spn,
514                                                  &spn_array, &num_spns);
515                         if (!ok) {
516                                 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
517                         }
518                 }
519         }
520
521         /* make sure to NULL terminate the array */
522         spn_array = talloc_realloc(mem_ctx, spn_array, const char *, num_spns + 1);
523         if (spn_array == NULL) {
524                 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
525         }
526         spn_array[num_spns] = NULL;
527
528         mods = ads_init_mods(mem_ctx);
529         if (!mods) {
530                 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
531         }
532
533         /* fields of primary importance */
534
535         status = ads_mod_str(mem_ctx, &mods, "dNSHostName", my_fqdn);
536         if (!ADS_ERR_OK(status)) {
537                 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
538         }
539
540         status = ads_mod_strlist(mem_ctx, &mods, "servicePrincipalName",
541                                  spn_array);
542         if (!ADS_ERR_OK(status)) {
543                 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
544         }
545
546         return ads_gen_mod(r->in.ads, r->out.dn, mods);
547 }
548
549 /****************************************************************
550 ****************************************************************/
551
552 static ADS_STATUS libnet_join_set_machine_upn(TALLOC_CTX *mem_ctx,
553                                               struct libnet_JoinCtx *r)
554 {
555         ADS_STATUS status;
556         ADS_MODLIST mods;
557
558         if (!r->in.create_upn) {
559                 return ADS_SUCCESS;
560         }
561
562         /* Find our DN */
563
564         status = libnet_join_find_machine_acct(mem_ctx, r);
565         if (!ADS_ERR_OK(status)) {
566                 return status;
567         }
568
569         if (!r->in.upn) {
570                 const char *realm = r->out.dns_domain_name;
571
572                 /* in case we are about to generate a keytab during the join
573                  * make sure the default upn we create is usable with kinit -k.
574                  * gd */
575
576                 if (USE_KERBEROS_KEYTAB) {
577                         realm = talloc_strdup_upper(mem_ctx,
578                                                     r->out.dns_domain_name);
579                 }
580
581                 if (!realm) {
582                         return ADS_ERROR(LDAP_NO_MEMORY);
583                 }
584
585                 r->in.upn = talloc_asprintf(mem_ctx,
586                                             "host/%s@%s",
587                                             r->in.machine_name,
588                                             realm);
589                 if (!r->in.upn) {
590                         return ADS_ERROR(LDAP_NO_MEMORY);
591                 }
592         }
593
594         /* now do the mods */
595
596         mods = ads_init_mods(mem_ctx);
597         if (!mods) {
598                 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
599         }
600
601         /* fields of primary importance */
602
603         status = ads_mod_str(mem_ctx, &mods, "userPrincipalName", r->in.upn);
604         if (!ADS_ERR_OK(status)) {
605                 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
606         }
607
608         return ads_gen_mod(r->in.ads, r->out.dn, mods);
609 }
610
611
612 /****************************************************************
613 ****************************************************************/
614
615 static ADS_STATUS libnet_join_set_os_attributes(TALLOC_CTX *mem_ctx,
616                                                 struct libnet_JoinCtx *r)
617 {
618         ADS_STATUS status;
619         ADS_MODLIST mods;
620         char *os_sp = NULL;
621
622         if (!r->in.os_name || !r->in.os_version ) {
623                 return ADS_SUCCESS;
624         }
625
626         /* Find our DN */
627
628         status = libnet_join_find_machine_acct(mem_ctx, r);
629         if (!ADS_ERR_OK(status)) {
630                 return status;
631         }
632
633         /* now do the mods */
634
635         mods = ads_init_mods(mem_ctx);
636         if (!mods) {
637                 return ADS_ERROR(LDAP_NO_MEMORY);
638         }
639
640         if (r->in.os_servicepack) {
641                 /*
642                  * if blank string then leave os_sp equal to NULL to force
643                  * attribute delete (LDAP_MOD_DELETE)
644                  */
645                 if (!strequal(r->in.os_servicepack,"")) {
646                         os_sp = talloc_strdup(mem_ctx, r->in.os_servicepack);
647                 }
648         } else {
649                 os_sp = talloc_asprintf(mem_ctx, "Samba %s",
650                                         samba_version_string());
651         }
652         if (!os_sp && !strequal(r->in.os_servicepack,"")) {
653                 return ADS_ERROR(LDAP_NO_MEMORY);
654         }
655
656         /* fields of primary importance */
657
658         status = ads_mod_str(mem_ctx, &mods, "operatingSystem",
659                              r->in.os_name);
660         if (!ADS_ERR_OK(status)) {
661                 return status;
662         }
663
664         status = ads_mod_str(mem_ctx, &mods, "operatingSystemVersion",
665                              r->in.os_version);
666         if (!ADS_ERR_OK(status)) {
667                 return status;
668         }
669
670         status = ads_mod_str(mem_ctx, &mods, "operatingSystemServicePack",
671                              os_sp);
672         if (!ADS_ERR_OK(status)) {
673                 return status;
674         }
675
676         return ads_gen_mod(r->in.ads, r->out.dn, mods);
677 }
678
679 /****************************************************************
680 ****************************************************************/
681 #if 0
682 static ADS_STATUS libnet_join_set_etypes(TALLOC_CTX *mem_ctx,
683                                          struct libnet_JoinCtx *r)
684 {
685         ADS_STATUS status;
686         ADS_MODLIST mods;
687         uint32_t etype_list = ENC_CRC32 | ENC_RSA_MD5 | ENC_RC4_HMAC_MD5;
688         const char *etype_list_str;
689
690 #ifdef HAVE_ENCTYPE_AES128_CTS_HMAC_SHA1_96
691         etype_list |= ENC_HMAC_SHA1_96_AES128;
692 #endif
693 #ifdef HAVE_ENCTYPE_AES256_CTS_HMAC_SHA1_96
694         etype_list |= ENC_HMAC_SHA1_96_AES256;
695 #endif
696
697         etype_list_str = talloc_asprintf(mem_ctx, "%d", etype_list);
698         if (!etype_list_str) {
699                 return ADS_ERROR(LDAP_NO_MEMORY);
700         }
701
702         /* Find our DN */
703
704         status = libnet_join_find_machine_acct(mem_ctx, r);
705         if (!ADS_ERR_OK(status)) {
706                 return status;
707         }
708
709         /* now do the mods */
710
711         mods = ads_init_mods(mem_ctx);
712         if (!mods) {
713                 return ADS_ERROR(LDAP_NO_MEMORY);
714         }
715
716         status = ads_mod_str(mem_ctx, &mods, "msDS-SupportedEncryptionTypes",
717                              etype_list_str);
718         if (!ADS_ERR_OK(status)) {
719                 return status;
720         }
721
722         return ads_gen_mod(r->in.ads, r->out.dn, mods);
723 }
724 #endif
725 /****************************************************************
726 ****************************************************************/
727
728 static bool libnet_join_create_keytab(TALLOC_CTX *mem_ctx,
729                                       struct libnet_JoinCtx *r)
730 {
731         if (!USE_SYSTEM_KEYTAB) {
732                 return true;
733         }
734
735         if (ads_keytab_create_default(r->in.ads) != 0) {
736                 return false;
737         }
738
739         return true;
740 }
741
742 /****************************************************************
743 ****************************************************************/
744
745 static bool libnet_join_derive_salting_principal(TALLOC_CTX *mem_ctx,
746                                                  struct libnet_JoinCtx *r)
747 {
748         uint32_t domain_func;
749         ADS_STATUS status;
750         const char *salt = NULL;
751         char *std_salt = NULL;
752
753         status = ads_domain_func_level(r->in.ads, &domain_func);
754         if (!ADS_ERR_OK(status)) {
755                 libnet_join_set_error_string(mem_ctx, r,
756                         "failed to determine domain functional level: %s",
757                         ads_errstr(status));
758                 return false;
759         }
760
761         /* go ahead and setup the default salt */
762
763         std_salt = kerberos_standard_des_salt();
764         if (!std_salt) {
765                 libnet_join_set_error_string(mem_ctx, r,
766                         "failed to obtain standard DES salt");
767                 return false;
768         }
769
770         salt = talloc_strdup(mem_ctx, std_salt);
771         if (!salt) {
772                 return false;
773         }
774
775         SAFE_FREE(std_salt);
776
777         /* if it's a Windows functional domain, we have to look for the UPN */
778
779         if (domain_func == DS_DOMAIN_FUNCTION_2000) {
780                 char *upn;
781
782                 upn = ads_get_upn(r->in.ads, mem_ctx,
783                                   r->in.machine_name);
784                 if (upn) {
785                         salt = talloc_strdup(mem_ctx, upn);
786                         if (!salt) {
787                                 return false;
788                         }
789                 }
790         }
791
792         return kerberos_secrets_store_des_salt(salt);
793 }
794
795 /****************************************************************
796 ****************************************************************/
797
798 static ADS_STATUS libnet_join_post_processing_ads(TALLOC_CTX *mem_ctx,
799                                                   struct libnet_JoinCtx *r)
800 {
801         ADS_STATUS status;
802
803         if (!r->in.ads) {
804                 status = libnet_join_connect_ads_user(mem_ctx, r);
805                 if (!ADS_ERR_OK(status)) {
806                         return status;
807                 }
808         }
809
810         status = libnet_join_set_machine_spn(mem_ctx, r);
811         if (!ADS_ERR_OK(status)) {
812                 libnet_join_set_error_string(mem_ctx, r,
813                         "Failed to set machine spn: %s\n"
814                         "Do you have sufficient permissions to create machine "
815                         "accounts?",
816                         ads_errstr(status));
817                 return status;
818         }
819
820         status = libnet_join_set_os_attributes(mem_ctx, r);
821         if (!ADS_ERR_OK(status)) {
822                 libnet_join_set_error_string(mem_ctx, r,
823                         "failed to set machine os attributes: %s",
824                         ads_errstr(status));
825                 return status;
826         }
827
828         status = libnet_join_set_machine_upn(mem_ctx, r);
829         if (!ADS_ERR_OK(status)) {
830                 libnet_join_set_error_string(mem_ctx, r,
831                         "failed to set machine upn: %s",
832                         ads_errstr(status));
833                 return status;
834         }
835
836         if (!libnet_join_derive_salting_principal(mem_ctx, r)) {
837                 return ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL);
838         }
839
840         if (!libnet_join_create_keytab(mem_ctx, r)) {
841                 libnet_join_set_error_string(mem_ctx, r,
842                         "failed to create kerberos keytab");
843                 return ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL);
844         }
845
846         return ADS_SUCCESS;
847 }
848 #endif /* HAVE_ADS */
849
850 /****************************************************************
851  Store the machine password and domain SID
852 ****************************************************************/
853
854 static bool libnet_join_joindomain_store_secrets(TALLOC_CTX *mem_ctx,
855                                                  struct libnet_JoinCtx *r)
856 {
857         if (!secrets_store_domain_sid(r->out.netbios_domain_name,
858                                       r->out.domain_sid))
859         {
860                 DEBUG(1,("Failed to save domain sid\n"));
861                 return false;
862         }
863
864         if (!secrets_store_machine_password(r->in.machine_password,
865                                             r->out.netbios_domain_name,
866                                             r->in.secure_channel_type))
867         {
868                 DEBUG(1,("Failed to save machine password\n"));
869                 return false;
870         }
871
872         return true;
873 }
874
875 /****************************************************************
876  Connect dc's IPC$ share
877 ****************************************************************/
878
879 static NTSTATUS libnet_join_connect_dc_ipc(const char *dc,
880                                            const char *user,
881                                            const char *domain,
882                                            const char *pass,
883                                            bool use_kerberos,
884                                            struct cli_state **cli)
885 {
886         int flags = 0;
887
888         if (use_kerberos) {
889                 flags |= CLI_FULL_CONNECTION_USE_KERBEROS;
890         }
891
892         if (use_kerberos && pass) {
893                 flags |= CLI_FULL_CONNECTION_FALLBACK_AFTER_KERBEROS;
894         }
895
896         return cli_full_connection(cli, NULL,
897                                    dc,
898                                    NULL, 0,
899                                    "IPC$", "IPC",
900                                    user,
901                                    domain,
902                                    pass,
903                                    flags,
904                                    SMB_SIGNING_DEFAULT);
905 }
906
907 /****************************************************************
908  Lookup domain dc's info
909 ****************************************************************/
910
911 static NTSTATUS libnet_join_lookup_dc_rpc(TALLOC_CTX *mem_ctx,
912                                           struct libnet_JoinCtx *r,
913                                           struct cli_state **cli)
914 {
915         struct rpc_pipe_client *pipe_hnd = NULL;
916         struct policy_handle lsa_pol;
917         NTSTATUS status, result;
918         union lsa_PolicyInformation *info = NULL;
919         struct dcerpc_binding_handle *b;
920
921         status = libnet_join_connect_dc_ipc(r->in.dc_name,
922                                             r->in.admin_account,
923                                             r->in.admin_domain,
924                                             r->in.admin_password,
925                                             r->in.use_kerberos,
926                                             cli);
927         if (!NT_STATUS_IS_OK(status)) {
928                 goto done;
929         }
930
931         status = cli_rpc_pipe_open_noauth(*cli, &ndr_table_lsarpc,
932                                           &pipe_hnd);
933         if (!NT_STATUS_IS_OK(status)) {
934                 DEBUG(0,("Error connecting to LSA pipe. Error was %s\n",
935                         nt_errstr(status)));
936                 goto done;
937         }
938
939         b = pipe_hnd->binding_handle;
940
941         status = rpccli_lsa_open_policy(pipe_hnd, mem_ctx, true,
942                                         SEC_FLAG_MAXIMUM_ALLOWED, &lsa_pol);
943         if (!NT_STATUS_IS_OK(status)) {
944                 goto done;
945         }
946
947         status = dcerpc_lsa_QueryInfoPolicy2(b, mem_ctx,
948                                              &lsa_pol,
949                                              LSA_POLICY_INFO_DNS,
950                                              &info,
951                                              &result);
952         if (NT_STATUS_IS_OK(status) && NT_STATUS_IS_OK(result)) {
953                 r->out.domain_is_ad = true;
954                 r->out.netbios_domain_name = info->dns.name.string;
955                 r->out.dns_domain_name = info->dns.dns_domain.string;
956                 r->out.forest_name = info->dns.dns_forest.string;
957                 r->out.domain_sid = dom_sid_dup(mem_ctx, info->dns.sid);
958                 NT_STATUS_HAVE_NO_MEMORY(r->out.domain_sid);
959         }
960
961         if (!NT_STATUS_IS_OK(status)) {
962                 status = dcerpc_lsa_QueryInfoPolicy(b, mem_ctx,
963                                                     &lsa_pol,
964                                                     LSA_POLICY_INFO_ACCOUNT_DOMAIN,
965                                                     &info,
966                                                     &result);
967                 if (!NT_STATUS_IS_OK(status)) {
968                         goto done;
969                 }
970                 if (!NT_STATUS_IS_OK(result)) {
971                         status = result;
972                         goto done;
973                 }
974
975                 r->out.netbios_domain_name = info->account_domain.name.string;
976                 r->out.domain_sid = dom_sid_dup(mem_ctx, info->account_domain.sid);
977                 NT_STATUS_HAVE_NO_MEMORY(r->out.domain_sid);
978         }
979
980         dcerpc_lsa_Close(b, mem_ctx, &lsa_pol, &result);
981         TALLOC_FREE(pipe_hnd);
982
983  done:
984         return status;
985 }
986
987 /****************************************************************
988  Do the domain join unsecure
989 ****************************************************************/
990
991 static NTSTATUS libnet_join_joindomain_rpc_unsecure(TALLOC_CTX *mem_ctx,
992                                                     struct libnet_JoinCtx *r,
993                                                     struct cli_state *cli)
994 {
995         TALLOC_CTX *frame = talloc_stackframe();
996         struct rpc_pipe_client *netlogon_pipe = NULL;
997         struct netlogon_creds_cli_context *netlogon_creds = NULL;
998         struct samr_Password current_nt_hash;
999         const char *account_name = NULL;
1000         NTSTATUS status;
1001
1002         status = cli_rpc_pipe_open_noauth(cli, &ndr_table_netlogon,
1003                                           &netlogon_pipe);
1004         if (!NT_STATUS_IS_OK(status)) {
1005                 TALLOC_FREE(frame);
1006                 return status;
1007         }
1008
1009         if (!r->in.machine_password) {
1010                 r->in.machine_password = generate_random_password(mem_ctx,
1011                                 DEFAULT_TRUST_ACCOUNT_PASSWORD_LENGTH,
1012                                 DEFAULT_TRUST_ACCOUNT_PASSWORD_LENGTH);
1013                 if (r->in.machine_password == NULL) {
1014                         TALLOC_FREE(frame);
1015                         return NT_STATUS_NO_MEMORY;
1016                 }
1017         }
1018
1019         /* according to WKSSVC_JOIN_FLAGS_MACHINE_PWD_PASSED */
1020         E_md4hash(r->in.admin_password, current_nt_hash.hash);
1021
1022         account_name = talloc_asprintf(frame, "%s$",
1023                                        r->in.machine_name);
1024         if (account_name == NULL) {
1025                 TALLOC_FREE(frame);
1026                 return NT_STATUS_NO_MEMORY;
1027         }
1028
1029         status = rpccli_create_netlogon_creds(netlogon_pipe->desthost,
1030                                               r->in.domain_name,
1031                                               account_name,
1032                                               r->in.secure_channel_type,
1033                                               r->in.msg_ctx,
1034                                               frame,
1035                                               &netlogon_creds);
1036         if (!NT_STATUS_IS_OK(status)) {
1037                 TALLOC_FREE(frame);
1038                 return status;
1039         }
1040
1041         status = rpccli_setup_netlogon_creds(cli, NCACN_NP,
1042                                              netlogon_creds,
1043                                              true, /* force_reauth */
1044                                              current_nt_hash,
1045                                              NULL); /* previous_nt_hash */
1046         if (!NT_STATUS_IS_OK(status)) {
1047                 TALLOC_FREE(frame);
1048                 return status;
1049         }
1050
1051         status = netlogon_creds_cli_ServerPasswordSet(netlogon_creds,
1052                                                       netlogon_pipe->binding_handle,
1053                                                       r->in.machine_password,
1054                                                       NULL); /* new_version */
1055         if (!NT_STATUS_IS_OK(status)) {
1056                 TALLOC_FREE(frame);
1057                 return status;
1058         }
1059
1060         TALLOC_FREE(frame);
1061         return NT_STATUS_OK;
1062 }
1063
1064 /****************************************************************
1065  Do the domain join
1066 ****************************************************************/
1067
1068 static NTSTATUS libnet_join_joindomain_rpc(TALLOC_CTX *mem_ctx,
1069                                            struct libnet_JoinCtx *r,
1070                                            struct cli_state *cli)
1071 {
1072         struct rpc_pipe_client *pipe_hnd = NULL;
1073         struct policy_handle sam_pol, domain_pol, user_pol;
1074         NTSTATUS status = NT_STATUS_UNSUCCESSFUL, result;
1075         char *acct_name;
1076         struct lsa_String lsa_acct_name;
1077         uint32_t user_rid;
1078         uint32_t acct_flags = ACB_WSTRUST;
1079         struct samr_Ids user_rids;
1080         struct samr_Ids name_types;
1081         union samr_UserInfo user_info;
1082         struct dcerpc_binding_handle *b = NULL;
1083         unsigned int old_timeout = 0;
1084
1085         DATA_BLOB session_key = data_blob_null;
1086         struct samr_CryptPassword crypt_pwd;
1087         struct samr_CryptPasswordEx crypt_pwd_ex;
1088
1089         ZERO_STRUCT(sam_pol);
1090         ZERO_STRUCT(domain_pol);
1091         ZERO_STRUCT(user_pol);
1092
1093         switch (r->in.secure_channel_type) {
1094         case SEC_CHAN_WKSTA:
1095                 acct_flags = ACB_WSTRUST;
1096                 break;
1097         case SEC_CHAN_BDC:
1098                 acct_flags = ACB_SVRTRUST;
1099                 break;
1100         default:
1101                 return NT_STATUS_INVALID_PARAMETER;
1102         }
1103
1104         if (!r->in.machine_password) {
1105                 r->in.machine_password = generate_random_password(mem_ctx,
1106                                 DEFAULT_TRUST_ACCOUNT_PASSWORD_LENGTH,
1107                                 DEFAULT_TRUST_ACCOUNT_PASSWORD_LENGTH);
1108                 NT_STATUS_HAVE_NO_MEMORY(r->in.machine_password);
1109         }
1110
1111         /* Open the domain */
1112
1113         status = cli_rpc_pipe_open_noauth(cli, &ndr_table_samr,
1114                                           &pipe_hnd);
1115         if (!NT_STATUS_IS_OK(status)) {
1116                 DEBUG(0,("Error connecting to SAM pipe. Error was %s\n",
1117                         nt_errstr(status)));
1118                 goto done;
1119         }
1120
1121         b = pipe_hnd->binding_handle;
1122
1123         status = cli_get_session_key(mem_ctx, pipe_hnd, &session_key);
1124         if (!NT_STATUS_IS_OK(status)) {
1125                 DEBUG(0,("Error getting session_key of SAM pipe. Error was %s\n",
1126                         nt_errstr(status)));
1127                 goto done;
1128         }
1129
1130         status = dcerpc_samr_Connect2(b, mem_ctx,
1131                                       pipe_hnd->desthost,
1132                                       SAMR_ACCESS_ENUM_DOMAINS
1133                                       | SAMR_ACCESS_LOOKUP_DOMAIN,
1134                                       &sam_pol,
1135                                       &result);
1136         if (!NT_STATUS_IS_OK(status)) {
1137                 goto done;
1138         }
1139         if (!NT_STATUS_IS_OK(result)) {
1140                 status = result;
1141                 goto done;
1142         }
1143
1144         status = dcerpc_samr_OpenDomain(b, mem_ctx,
1145                                         &sam_pol,
1146                                         SAMR_DOMAIN_ACCESS_LOOKUP_INFO_1
1147                                         | SAMR_DOMAIN_ACCESS_CREATE_USER
1148                                         | SAMR_DOMAIN_ACCESS_OPEN_ACCOUNT,
1149                                         r->out.domain_sid,
1150                                         &domain_pol,
1151                                         &result);
1152         if (!NT_STATUS_IS_OK(status)) {
1153                 goto done;
1154         }
1155         if (!NT_STATUS_IS_OK(result)) {
1156                 status = result;
1157                 goto done;
1158         }
1159
1160         /* Create domain user */
1161
1162         acct_name = talloc_asprintf(mem_ctx, "%s$", r->in.machine_name);
1163         if (!strlower_m(acct_name)) {
1164                 status = NT_STATUS_INVALID_PARAMETER;
1165                 goto done;
1166         }
1167
1168         init_lsa_String(&lsa_acct_name, acct_name);
1169
1170         if (r->in.join_flags & WKSSVC_JOIN_FLAGS_ACCOUNT_CREATE) {
1171                 uint32_t access_desired =
1172                         SEC_GENERIC_READ | SEC_GENERIC_WRITE | SEC_GENERIC_EXECUTE |
1173                         SEC_STD_WRITE_DAC | SEC_STD_DELETE |
1174                         SAMR_USER_ACCESS_SET_PASSWORD |
1175                         SAMR_USER_ACCESS_GET_ATTRIBUTES |
1176                         SAMR_USER_ACCESS_SET_ATTRIBUTES;
1177                 uint32_t access_granted = 0;
1178
1179                 DEBUG(10,("Creating account with desired access mask: %d\n",
1180                         access_desired));
1181
1182                 status = dcerpc_samr_CreateUser2(b, mem_ctx,
1183                                                  &domain_pol,
1184                                                  &lsa_acct_name,
1185                                                  acct_flags,
1186                                                  access_desired,
1187                                                  &user_pol,
1188                                                  &access_granted,
1189                                                  &user_rid,
1190                                                  &result);
1191                 if (!NT_STATUS_IS_OK(status)) {
1192                         goto done;
1193                 }
1194
1195                 status = result;
1196                 if (!NT_STATUS_IS_OK(status) &&
1197                     !NT_STATUS_EQUAL(status, NT_STATUS_USER_EXISTS)) {
1198
1199                         DEBUG(10,("Creation of workstation account failed: %s\n",
1200                                 nt_errstr(status)));
1201
1202                         /* If NT_STATUS_ACCESS_DENIED then we have a valid
1203                            username/password combo but the user does not have
1204                            administrator access. */
1205
1206                         if (NT_STATUS_EQUAL(status, NT_STATUS_ACCESS_DENIED)) {
1207                                 libnet_join_set_error_string(mem_ctx, r,
1208                                         "User specified does not have "
1209                                         "administrator privileges");
1210                         }
1211
1212                         goto done;
1213                 }
1214
1215                 if (NT_STATUS_EQUAL(status, NT_STATUS_USER_EXISTS)) {
1216                         if (!(r->in.join_flags &
1217                               WKSSVC_JOIN_FLAGS_DOMAIN_JOIN_IF_JOINED)) {
1218                                 goto done;
1219                         }
1220                 }
1221
1222                 /* We *must* do this.... don't ask... */
1223
1224                 if (NT_STATUS_IS_OK(status)) {
1225                         dcerpc_samr_Close(b, mem_ctx, &user_pol, &result);
1226                 }
1227         }
1228
1229         status = dcerpc_samr_LookupNames(b, mem_ctx,
1230                                          &domain_pol,
1231                                          1,
1232                                          &lsa_acct_name,
1233                                          &user_rids,
1234                                          &name_types,
1235                                          &result);
1236         if (!NT_STATUS_IS_OK(status)) {
1237                 goto done;
1238         }
1239         if (!NT_STATUS_IS_OK(result)) {
1240                 status = result;
1241                 goto done;
1242         }
1243         if (user_rids.count != 1) {
1244                 status = NT_STATUS_INVALID_NETWORK_RESPONSE;
1245                 goto done;
1246         }
1247         if (name_types.count != 1) {
1248                 status = NT_STATUS_INVALID_NETWORK_RESPONSE;
1249                 goto done;
1250         }
1251
1252         if (name_types.ids[0] != SID_NAME_USER) {
1253                 DEBUG(0,("%s is not a user account (type=%d)\n",
1254                         acct_name, name_types.ids[0]));
1255                 status = NT_STATUS_INVALID_WORKSTATION;
1256                 goto done;
1257         }
1258
1259         user_rid = user_rids.ids[0];
1260
1261         /* Open handle on user */
1262
1263         status = dcerpc_samr_OpenUser(b, mem_ctx,
1264                                       &domain_pol,
1265                                       SEC_FLAG_MAXIMUM_ALLOWED,
1266                                       user_rid,
1267                                       &user_pol,
1268                                       &result);
1269         if (!NT_STATUS_IS_OK(status)) {
1270                 goto done;
1271         }
1272         if (!NT_STATUS_IS_OK(result)) {
1273                 status = result;
1274                 goto done;
1275         }
1276
1277         /* Fill in the additional account flags now */
1278
1279         acct_flags |= ACB_PWNOEXP;
1280
1281         /* Set account flags on machine account */
1282         ZERO_STRUCT(user_info.info16);
1283         user_info.info16.acct_flags = acct_flags;
1284
1285         status = dcerpc_samr_SetUserInfo(b, mem_ctx,
1286                                          &user_pol,
1287                                          16,
1288                                          &user_info,
1289                                          &result);
1290         if (!NT_STATUS_IS_OK(status)) {
1291                 dcerpc_samr_DeleteUser(b, mem_ctx,
1292                                        &user_pol,
1293                                        &result);
1294
1295                 libnet_join_set_error_string(mem_ctx, r,
1296                         "Failed to set account flags for machine account (%s)\n",
1297                         nt_errstr(status));
1298                 goto done;
1299         }
1300
1301         if (!NT_STATUS_IS_OK(result)) {
1302                 status = result;
1303
1304                 dcerpc_samr_DeleteUser(b, mem_ctx,
1305                                        &user_pol,
1306                                        &result);
1307
1308                 libnet_join_set_error_string(mem_ctx, r,
1309                         "Failed to set account flags for machine account (%s)\n",
1310                         nt_errstr(status));
1311                 goto done;
1312         }
1313
1314         /* Set password on machine account - first try level 26 */
1315
1316         /*
1317          * increase the timeout as password filter modules on the DC
1318          * might delay the operation for a significant amount of time
1319          */
1320         old_timeout = rpccli_set_timeout(pipe_hnd, 600000);
1321
1322         init_samr_CryptPasswordEx(r->in.machine_password,
1323                                   &session_key,
1324                                   &crypt_pwd_ex);
1325
1326         user_info.info26.password = crypt_pwd_ex;
1327         user_info.info26.password_expired = PASS_DONT_CHANGE_AT_NEXT_LOGON;
1328
1329         status = dcerpc_samr_SetUserInfo2(b, mem_ctx,
1330                                           &user_pol,
1331                                           26,
1332                                           &user_info,
1333                                           &result);
1334
1335         if (NT_STATUS_EQUAL(status, NT_STATUS_RPC_ENUM_VALUE_OUT_OF_RANGE)) {
1336
1337                 /* retry with level 24 */
1338
1339                 init_samr_CryptPassword(r->in.machine_password,
1340                                         &session_key,
1341                                         &crypt_pwd);
1342
1343                 user_info.info24.password = crypt_pwd;
1344                 user_info.info24.password_expired = PASS_DONT_CHANGE_AT_NEXT_LOGON;
1345
1346                 status = dcerpc_samr_SetUserInfo2(b, mem_ctx,
1347                                                   &user_pol,
1348                                                   24,
1349                                                   &user_info,
1350                                                   &result);
1351         }
1352
1353         old_timeout = rpccli_set_timeout(pipe_hnd, old_timeout);
1354
1355         if (!NT_STATUS_IS_OK(status)) {
1356
1357                 dcerpc_samr_DeleteUser(b, mem_ctx,
1358                                        &user_pol,
1359                                        &result);
1360
1361                 libnet_join_set_error_string(mem_ctx, r,
1362                         "Failed to set password for machine account (%s)\n",
1363                         nt_errstr(status));
1364                 goto done;
1365         }
1366         if (!NT_STATUS_IS_OK(result)) {
1367                 status = result;
1368
1369                 dcerpc_samr_DeleteUser(b, mem_ctx,
1370                                        &user_pol,
1371                                        &result);
1372
1373                 libnet_join_set_error_string(mem_ctx, r,
1374                         "Failed to set password for machine account (%s)\n",
1375                         nt_errstr(status));
1376                 goto done;
1377         }
1378
1379         status = NT_STATUS_OK;
1380
1381  done:
1382         if (!pipe_hnd) {
1383                 return status;
1384         }
1385
1386         data_blob_clear_free(&session_key);
1387
1388         if (is_valid_policy_hnd(&sam_pol)) {
1389                 dcerpc_samr_Close(b, mem_ctx, &sam_pol, &result);
1390         }
1391         if (is_valid_policy_hnd(&domain_pol)) {
1392                 dcerpc_samr_Close(b, mem_ctx, &domain_pol, &result);
1393         }
1394         if (is_valid_policy_hnd(&user_pol)) {
1395                 dcerpc_samr_Close(b, mem_ctx, &user_pol, &result);
1396         }
1397         TALLOC_FREE(pipe_hnd);
1398
1399         return status;
1400 }
1401
1402 /****************************************************************
1403 ****************************************************************/
1404
1405 NTSTATUS libnet_join_ok(struct messaging_context *msg_ctx,
1406                         const char *netbios_domain_name,
1407                         const char *dc_name,
1408                         const bool use_kerberos)
1409 {
1410         TALLOC_CTX *frame = talloc_stackframe();
1411         struct cli_state *cli = NULL;
1412         struct rpc_pipe_client *netlogon_pipe = NULL;
1413         struct cli_credentials *cli_creds = NULL;
1414         struct netlogon_creds_cli_context *netlogon_creds = NULL;
1415         struct netlogon_creds_CredentialState *creds = NULL;
1416         uint32_t netlogon_flags = 0;
1417         NTSTATUS status;
1418         const char *machine_account = NULL;
1419         const char *machine_domain = NULL;
1420         const char *machine_password = NULL;
1421         int flags = 0;
1422
1423         if (!dc_name) {
1424                 TALLOC_FREE(frame);
1425                 return NT_STATUS_INVALID_PARAMETER;
1426         }
1427
1428         if (!secrets_init()) {
1429                 TALLOC_FREE(frame);
1430                 return NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
1431         }
1432
1433         status = pdb_get_trust_credentials(netbios_domain_name, NULL,
1434                                            frame, &cli_creds);
1435         if (!NT_STATUS_IS_OK(status)) {
1436                 TALLOC_FREE(frame);
1437                 return status;
1438         }
1439
1440         /* we don't want any old password */
1441         cli_credentials_set_old_password(cli_creds, NULL, CRED_SPECIFIED);
1442
1443         if (use_kerberos) {
1444                 flags |= CLI_FULL_CONNECTION_USE_KERBEROS;
1445         }
1446
1447         machine_account = cli_credentials_get_username(cli_creds);
1448         machine_domain = cli_credentials_get_domain(cli_creds);
1449         machine_password = cli_credentials_get_password(cli_creds);
1450
1451         status = cli_full_connection(&cli, NULL,
1452                                      dc_name,
1453                                      NULL, 0,
1454                                      "IPC$", "IPC",
1455                                      machine_account,
1456                                      machine_domain,
1457                                      machine_password,
1458                                      flags,
1459                                      SMB_SIGNING_DEFAULT);
1460
1461         if (!NT_STATUS_IS_OK(status)) {
1462                 status = cli_full_connection(&cli, NULL,
1463                                              dc_name,
1464                                              NULL, 0,
1465                                              "IPC$", "IPC",
1466                                              "",
1467                                              NULL,
1468                                              "",
1469                                              0,
1470                                              SMB_SIGNING_DEFAULT);
1471         }
1472
1473         if (!NT_STATUS_IS_OK(status)) {
1474                 TALLOC_FREE(frame);
1475                 return status;
1476         }
1477
1478         status = rpccli_create_netlogon_creds_with_creds(cli_creds,
1479                                                          dc_name,
1480                                                          msg_ctx,
1481                                                          frame,
1482                                                          &netlogon_creds);
1483         if (!NT_STATUS_IS_OK(status)) {
1484                 cli_shutdown(cli);
1485                 TALLOC_FREE(frame);
1486                 return status;
1487         }
1488
1489         status = rpccli_setup_netlogon_creds_with_creds(cli, NCACN_NP,
1490                                                         netlogon_creds,
1491                                                         true, /* force_reauth */
1492                                                         cli_creds);
1493         if (!NT_STATUS_IS_OK(status)) {
1494                 DEBUG(0,("connect_to_domain_password_server: "
1495                          "unable to open the domain client session to "
1496                          "machine %s. Flags[0x%08X] Error was : %s.\n",
1497                          dc_name, (unsigned)netlogon_flags,
1498                          nt_errstr(status)));
1499                 cli_shutdown(cli);
1500                 TALLOC_FREE(frame);
1501                 return status;
1502         }
1503
1504         status = netlogon_creds_cli_get(netlogon_creds,
1505                                         talloc_tos(),
1506                                         &creds);
1507         if (!NT_STATUS_IS_OK(status)) {
1508                 cli_shutdown(cli);
1509                 TALLOC_FREE(frame);
1510                 return status;
1511         }
1512         netlogon_flags = creds->negotiate_flags;
1513         TALLOC_FREE(creds);
1514
1515         if (!(netlogon_flags & NETLOGON_NEG_AUTHENTICATED_RPC)) {
1516                 cli_shutdown(cli);
1517                 TALLOC_FREE(frame);
1518                 return NT_STATUS_OK;
1519         }
1520
1521         status = cli_rpc_pipe_open_schannel_with_creds(
1522                 cli, &ndr_table_netlogon, NCACN_NP,
1523                 cli_creds,
1524                 netlogon_creds, &netlogon_pipe);
1525
1526         TALLOC_FREE(netlogon_pipe);
1527
1528         if (!NT_STATUS_IS_OK(status)) {
1529                 DEBUG(0,("libnet_join_ok: failed to open schannel session "
1530                         "on netlogon pipe to server %s for domain %s. "
1531                         "Error was %s\n",
1532                         smbXcli_conn_remote_name(cli->conn),
1533                         netbios_domain_name, nt_errstr(status)));
1534                 cli_shutdown(cli);
1535                 TALLOC_FREE(frame);
1536                 return status;
1537         }
1538
1539         cli_shutdown(cli);
1540         TALLOC_FREE(frame);
1541         return NT_STATUS_OK;
1542 }
1543
1544 /****************************************************************
1545 ****************************************************************/
1546
1547 static WERROR libnet_join_post_verify(TALLOC_CTX *mem_ctx,
1548                                       struct libnet_JoinCtx *r)
1549 {
1550         NTSTATUS status;
1551
1552         status = libnet_join_ok(r->in.msg_ctx,
1553                                 r->out.netbios_domain_name,
1554                                 r->in.dc_name,
1555                                 r->in.use_kerberos);
1556         if (!NT_STATUS_IS_OK(status)) {
1557                 libnet_join_set_error_string(mem_ctx, r,
1558                         "failed to verify domain membership after joining: %s",
1559                         get_friendly_nt_error_msg(status));
1560                 return WERR_SETUP_NOT_JOINED;
1561         }
1562
1563         return WERR_OK;
1564 }
1565
1566 /****************************************************************
1567 ****************************************************************/
1568
1569 static bool libnet_join_unjoindomain_remove_secrets(TALLOC_CTX *mem_ctx,
1570                                                     struct libnet_UnjoinCtx *r)
1571 {
1572         if (!secrets_delete_machine_password_ex(lp_workgroup())) {
1573                 return false;
1574         }
1575
1576         if (!secrets_delete_domain_sid(lp_workgroup())) {
1577                 return false;
1578         }
1579
1580         return true;
1581 }
1582
1583 /****************************************************************
1584 ****************************************************************/
1585
1586 static NTSTATUS libnet_join_unjoindomain_rpc(TALLOC_CTX *mem_ctx,
1587                                              struct libnet_UnjoinCtx *r)
1588 {
1589         struct cli_state *cli = NULL;
1590         struct rpc_pipe_client *pipe_hnd = NULL;
1591         struct policy_handle sam_pol, domain_pol, user_pol;
1592         NTSTATUS status = NT_STATUS_UNSUCCESSFUL, result;
1593         char *acct_name;
1594         uint32_t user_rid;
1595         struct lsa_String lsa_acct_name;
1596         struct samr_Ids user_rids;
1597         struct samr_Ids name_types;
1598         union samr_UserInfo *info = NULL;
1599         struct dcerpc_binding_handle *b = NULL;
1600
1601         ZERO_STRUCT(sam_pol);
1602         ZERO_STRUCT(domain_pol);
1603         ZERO_STRUCT(user_pol);
1604
1605         status = libnet_join_connect_dc_ipc(r->in.dc_name,
1606                                             r->in.admin_account,
1607                                             r->in.admin_domain,
1608                                             r->in.admin_password,
1609                                             r->in.use_kerberos,
1610                                             &cli);
1611         if (!NT_STATUS_IS_OK(status)) {
1612                 goto done;
1613         }
1614
1615         /* Open the domain */
1616
1617         status = cli_rpc_pipe_open_noauth(cli, &ndr_table_samr,
1618                                           &pipe_hnd);
1619         if (!NT_STATUS_IS_OK(status)) {
1620                 DEBUG(0,("Error connecting to SAM pipe. Error was %s\n",
1621                         nt_errstr(status)));
1622                 goto done;
1623         }
1624
1625         b = pipe_hnd->binding_handle;
1626
1627         status = dcerpc_samr_Connect2(b, mem_ctx,
1628                                       pipe_hnd->desthost,
1629                                       SEC_FLAG_MAXIMUM_ALLOWED,
1630                                       &sam_pol,
1631                                       &result);
1632         if (!NT_STATUS_IS_OK(status)) {
1633                 goto done;
1634         }
1635         if (!NT_STATUS_IS_OK(result)) {
1636                 status = result;
1637                 goto done;
1638         }
1639
1640         status = dcerpc_samr_OpenDomain(b, mem_ctx,
1641                                         &sam_pol,
1642                                         SEC_FLAG_MAXIMUM_ALLOWED,
1643                                         r->in.domain_sid,
1644                                         &domain_pol,
1645                                         &result);
1646         if (!NT_STATUS_IS_OK(status)) {
1647                 goto done;
1648         }
1649         if (!NT_STATUS_IS_OK(result)) {
1650                 status = result;
1651                 goto done;
1652         }
1653
1654         /* Create domain user */
1655
1656         acct_name = talloc_asprintf(mem_ctx, "%s$", r->in.machine_name);
1657         if (!strlower_m(acct_name)) {
1658                 status = NT_STATUS_INVALID_PARAMETER;
1659                 goto done;
1660         }
1661
1662         init_lsa_String(&lsa_acct_name, acct_name);
1663
1664         status = dcerpc_samr_LookupNames(b, mem_ctx,
1665                                          &domain_pol,
1666                                          1,
1667                                          &lsa_acct_name,
1668                                          &user_rids,
1669                                          &name_types,
1670                                          &result);
1671
1672         if (!NT_STATUS_IS_OK(status)) {
1673                 goto done;
1674         }
1675         if (!NT_STATUS_IS_OK(result)) {
1676                 status = result;
1677                 goto done;
1678         }
1679         if (user_rids.count != 1) {
1680                 status = NT_STATUS_INVALID_NETWORK_RESPONSE;
1681                 goto done;
1682         }
1683         if (name_types.count != 1) {
1684                 status = NT_STATUS_INVALID_NETWORK_RESPONSE;
1685                 goto done;
1686         }
1687
1688         if (name_types.ids[0] != SID_NAME_USER) {
1689                 DEBUG(0, ("%s is not a user account (type=%d)\n", acct_name,
1690                         name_types.ids[0]));
1691                 status = NT_STATUS_INVALID_WORKSTATION;
1692                 goto done;
1693         }
1694
1695         user_rid = user_rids.ids[0];
1696
1697         /* Open handle on user */
1698
1699         status = dcerpc_samr_OpenUser(b, mem_ctx,
1700                                       &domain_pol,
1701                                       SEC_FLAG_MAXIMUM_ALLOWED,
1702                                       user_rid,
1703                                       &user_pol,
1704                                       &result);
1705         if (!NT_STATUS_IS_OK(status)) {
1706                 goto done;
1707         }
1708         if (!NT_STATUS_IS_OK(result)) {
1709                 status = result;
1710                 goto done;
1711         }
1712
1713         /* Get user info */
1714
1715         status = dcerpc_samr_QueryUserInfo(b, mem_ctx,
1716                                            &user_pol,
1717                                            16,
1718                                            &info,
1719                                            &result);
1720         if (!NT_STATUS_IS_OK(status)) {
1721                 dcerpc_samr_Close(b, mem_ctx, &user_pol, &result);
1722                 goto done;
1723         }
1724         if (!NT_STATUS_IS_OK(result)) {
1725                 status = result;
1726                 dcerpc_samr_Close(b, mem_ctx, &user_pol, &result);
1727                 goto done;
1728         }
1729
1730         /* now disable and setuser info */
1731
1732         info->info16.acct_flags |= ACB_DISABLED;
1733
1734         status = dcerpc_samr_SetUserInfo(b, mem_ctx,
1735                                          &user_pol,
1736                                          16,
1737                                          info,
1738                                          &result);
1739         if (!NT_STATUS_IS_OK(status)) {
1740                 dcerpc_samr_Close(b, mem_ctx, &user_pol, &result);
1741                 goto done;
1742         }
1743         if (!NT_STATUS_IS_OK(result)) {
1744                 status = result;
1745                 dcerpc_samr_Close(b, mem_ctx, &user_pol, &result);
1746                 goto done;
1747         }
1748         status = result;
1749         dcerpc_samr_Close(b, mem_ctx, &user_pol, &result);
1750
1751 done:
1752         if (pipe_hnd && b) {
1753                 if (is_valid_policy_hnd(&domain_pol)) {
1754                         dcerpc_samr_Close(b, mem_ctx, &domain_pol, &result);
1755                 }
1756                 if (is_valid_policy_hnd(&sam_pol)) {
1757                         dcerpc_samr_Close(b, mem_ctx, &sam_pol, &result);
1758                 }
1759                 TALLOC_FREE(pipe_hnd);
1760         }
1761
1762         if (cli) {
1763                 cli_shutdown(cli);
1764         }
1765
1766         return status;
1767 }
1768
1769 /****************************************************************
1770 ****************************************************************/
1771
1772 static WERROR do_join_modify_vals_config(struct libnet_JoinCtx *r)
1773 {
1774         WERROR werr = WERR_OK;
1775         sbcErr err;
1776         struct smbconf_ctx *ctx;
1777
1778         err = smbconf_init_reg(r, &ctx, NULL);
1779         if (!SBC_ERROR_IS_OK(err)) {
1780                 werr = WERR_NO_SUCH_SERVICE;
1781                 goto done;
1782         }
1783
1784         if (!(r->in.join_flags & WKSSVC_JOIN_FLAGS_JOIN_TYPE)) {
1785
1786                 err = smbconf_set_global_parameter(ctx, "security", "user");
1787                 if (!SBC_ERROR_IS_OK(err)) {
1788                         werr = WERR_NO_SUCH_SERVICE;
1789                         goto done;
1790                 }
1791
1792                 err = smbconf_set_global_parameter(ctx, "workgroup",
1793                                                    r->in.domain_name);
1794                 if (!SBC_ERROR_IS_OK(err)) {
1795                         werr = WERR_NO_SUCH_SERVICE;
1796                         goto done;
1797                 }
1798
1799                 smbconf_delete_global_parameter(ctx, "realm");
1800                 goto done;
1801         }
1802
1803         err = smbconf_set_global_parameter(ctx, "security", "domain");
1804         if (!SBC_ERROR_IS_OK(err)) {
1805                 werr = WERR_NO_SUCH_SERVICE;
1806                 goto done;
1807         }
1808
1809         err = smbconf_set_global_parameter(ctx, "workgroup",
1810                                            r->out.netbios_domain_name);
1811         if (!SBC_ERROR_IS_OK(err)) {
1812                 werr = WERR_NO_SUCH_SERVICE;
1813                 goto done;
1814         }
1815
1816         if (r->out.domain_is_ad) {
1817                 err = smbconf_set_global_parameter(ctx, "security", "ads");
1818                 if (!SBC_ERROR_IS_OK(err)) {
1819                         werr = WERR_NO_SUCH_SERVICE;
1820                         goto done;
1821                 }
1822
1823                 err = smbconf_set_global_parameter(ctx, "realm",
1824                                                    r->out.dns_domain_name);
1825                 if (!SBC_ERROR_IS_OK(err)) {
1826                         werr = WERR_NO_SUCH_SERVICE;
1827                         goto done;
1828                 }
1829         }
1830
1831  done:
1832         smbconf_shutdown(ctx);
1833         return werr;
1834 }
1835
1836 /****************************************************************
1837 ****************************************************************/
1838
1839 static WERROR do_unjoin_modify_vals_config(struct libnet_UnjoinCtx *r)
1840 {
1841         WERROR werr = WERR_OK;
1842         sbcErr err;
1843         struct smbconf_ctx *ctx;
1844
1845         err = smbconf_init_reg(r, &ctx, NULL);
1846         if (!SBC_ERROR_IS_OK(err)) {
1847                 werr = WERR_NO_SUCH_SERVICE;
1848                 goto done;
1849         }
1850
1851         if (r->in.unjoin_flags & WKSSVC_JOIN_FLAGS_JOIN_TYPE) {
1852
1853                 err = smbconf_set_global_parameter(ctx, "security", "user");
1854                 if (!SBC_ERROR_IS_OK(err)) {
1855                         werr = WERR_NO_SUCH_SERVICE;
1856                         goto done;
1857                 }
1858
1859                 err = smbconf_delete_global_parameter(ctx, "workgroup");
1860                 if (!SBC_ERROR_IS_OK(err)) {
1861                         werr = WERR_NO_SUCH_SERVICE;
1862                         goto done;
1863                 }
1864
1865                 smbconf_delete_global_parameter(ctx, "realm");
1866         }
1867
1868  done:
1869         smbconf_shutdown(ctx);
1870         return werr;
1871 }
1872
1873 /****************************************************************
1874 ****************************************************************/
1875
1876 static WERROR do_JoinConfig(struct libnet_JoinCtx *r)
1877 {
1878         WERROR werr;
1879
1880         if (!W_ERROR_IS_OK(r->out.result)) {
1881                 return r->out.result;
1882         }
1883
1884         if (!r->in.modify_config) {
1885                 return WERR_OK;
1886         }
1887
1888         werr = do_join_modify_vals_config(r);
1889         if (!W_ERROR_IS_OK(werr)) {
1890                 return werr;
1891         }
1892
1893         lp_load_global(get_dyn_CONFIGFILE());
1894
1895         r->out.modified_config = true;
1896         r->out.result = werr;
1897
1898         return werr;
1899 }
1900
1901 /****************************************************************
1902 ****************************************************************/
1903
1904 static WERROR libnet_unjoin_config(struct libnet_UnjoinCtx *r)
1905 {
1906         WERROR werr;
1907
1908         if (!W_ERROR_IS_OK(r->out.result)) {
1909                 return r->out.result;
1910         }
1911
1912         if (!r->in.modify_config) {
1913                 return WERR_OK;
1914         }
1915
1916         werr = do_unjoin_modify_vals_config(r);
1917         if (!W_ERROR_IS_OK(werr)) {
1918                 return werr;
1919         }
1920
1921         lp_load_global(get_dyn_CONFIGFILE());
1922
1923         r->out.modified_config = true;
1924         r->out.result = werr;
1925
1926         return werr;
1927 }
1928
1929 /****************************************************************
1930 ****************************************************************/
1931
1932 static bool libnet_parse_domain_dc(TALLOC_CTX *mem_ctx,
1933                                    const char *domain_str,
1934                                    const char **domain_p,
1935                                    const char **dc_p)
1936 {
1937         char *domain = NULL;
1938         char *dc = NULL;
1939         const char *p = NULL;
1940
1941         if (!domain_str || !domain_p || !dc_p) {
1942                 return false;
1943         }
1944
1945         p = strchr_m(domain_str, '\\');
1946
1947         if (p != NULL) {
1948                 domain = talloc_strndup(mem_ctx, domain_str,
1949                                          PTR_DIFF(p, domain_str));
1950                 dc = talloc_strdup(mem_ctx, p+1);
1951                 if (!dc) {
1952                         return false;
1953                 }
1954         } else {
1955                 domain = talloc_strdup(mem_ctx, domain_str);
1956                 dc = NULL;
1957         }
1958         if (!domain) {
1959                 return false;
1960         }
1961
1962         *domain_p = domain;
1963
1964         if (!*dc_p && dc) {
1965                 *dc_p = dc;
1966         }
1967
1968         return true;
1969 }
1970
1971 /****************************************************************
1972 ****************************************************************/
1973
1974 static WERROR libnet_join_pre_processing(TALLOC_CTX *mem_ctx,
1975                                          struct libnet_JoinCtx *r)
1976 {
1977         if (!r->in.domain_name) {
1978                 libnet_join_set_error_string(mem_ctx, r,
1979                         "No domain name defined");
1980                 return WERR_INVALID_PARAM;
1981         }
1982
1983         if (strlen(r->in.machine_name) > 15) {
1984                 libnet_join_set_error_string(mem_ctx, r,
1985                         "Our netbios name can be at most 15 chars long, "
1986                          "\"%s\" is %u chars long\n",
1987                          r->in.machine_name,
1988                          (unsigned int)strlen(r->in.machine_name));
1989                 return WERR_INVALID_PARAM;
1990         }
1991
1992         if (!libnet_parse_domain_dc(mem_ctx, r->in.domain_name,
1993                                     &r->in.domain_name,
1994                                     &r->in.dc_name)) {
1995                 libnet_join_set_error_string(mem_ctx, r,
1996                         "Failed to parse domain name");
1997                 return WERR_INVALID_PARAM;
1998         }
1999
2000         if (!r->in.admin_domain) {
2001                 char *admin_domain = NULL;
2002                 char *admin_account = NULL;
2003                 split_domain_user(mem_ctx,
2004                                   r->in.admin_account,
2005                                   &admin_domain,
2006                                   &admin_account);
2007                 r->in.admin_domain = admin_domain;
2008                 r->in.admin_account = admin_account;
2009         }
2010
2011         if (!secrets_init()) {
2012                 libnet_join_set_error_string(mem_ctx, r,
2013                         "Unable to open secrets database");
2014                 return WERR_CAN_NOT_COMPLETE;
2015         }
2016
2017         return WERR_OK;
2018 }
2019
2020 /****************************************************************
2021 ****************************************************************/
2022
2023 static void libnet_join_add_dom_rids_to_builtins(struct dom_sid *domain_sid)
2024 {
2025         NTSTATUS status;
2026
2027         /* Try adding dom admins to builtin\admins. Only log failures. */
2028         status = create_builtin_administrators(domain_sid);
2029         if (NT_STATUS_EQUAL(status, NT_STATUS_PROTOCOL_UNREACHABLE)) {
2030                 DEBUG(10,("Unable to auto-add domain administrators to "
2031                           "BUILTIN\\Administrators during join because "
2032                           "winbindd must be running.\n"));
2033         } else if (!NT_STATUS_IS_OK(status)) {
2034                 DEBUG(5, ("Failed to auto-add domain administrators to "
2035                           "BUILTIN\\Administrators during join: %s\n",
2036                           nt_errstr(status)));
2037         }
2038
2039         /* Try adding dom users to builtin\users. Only log failures. */
2040         status = create_builtin_users(domain_sid);
2041         if (NT_STATUS_EQUAL(status, NT_STATUS_PROTOCOL_UNREACHABLE)) {
2042                 DEBUG(10,("Unable to auto-add domain users to BUILTIN\\users "
2043                           "during join because winbindd must be running.\n"));
2044         } else if (!NT_STATUS_IS_OK(status)) {
2045                 DEBUG(5, ("Failed to auto-add domain administrators to "
2046                           "BUILTIN\\Administrators during join: %s\n",
2047                           nt_errstr(status)));
2048         }
2049 }
2050
2051 /****************************************************************
2052 ****************************************************************/
2053
2054 static WERROR libnet_join_post_processing(TALLOC_CTX *mem_ctx,
2055                                           struct libnet_JoinCtx *r)
2056 {
2057         WERROR werr;
2058
2059         if (!W_ERROR_IS_OK(r->out.result)) {
2060                 return r->out.result;
2061         }
2062
2063         werr = do_JoinConfig(r);
2064         if (!W_ERROR_IS_OK(werr)) {
2065                 return werr;
2066         }
2067
2068         if (!(r->in.join_flags & WKSSVC_JOIN_FLAGS_JOIN_TYPE)) {
2069                 return WERR_OK;
2070         }
2071
2072         saf_join_store(r->out.netbios_domain_name, r->in.dc_name);
2073         if (r->out.dns_domain_name) {
2074                 saf_join_store(r->out.dns_domain_name, r->in.dc_name);
2075         }
2076
2077 #ifdef HAVE_ADS
2078         if (r->out.domain_is_ad &&
2079             !(r->in.join_flags & WKSSVC_JOIN_FLAGS_JOIN_UNSECURE)) {
2080                 ADS_STATUS ads_status;
2081
2082                 ads_status  = libnet_join_post_processing_ads(mem_ctx, r);
2083                 if (!ADS_ERR_OK(ads_status)) {
2084                         return WERR_GENERAL_FAILURE;
2085                 }
2086         }
2087 #endif /* HAVE_ADS */
2088
2089         libnet_join_add_dom_rids_to_builtins(r->out.domain_sid);
2090
2091         return WERR_OK;
2092 }
2093
2094 /****************************************************************
2095 ****************************************************************/
2096
2097 static int libnet_destroy_JoinCtx(struct libnet_JoinCtx *r)
2098 {
2099         if (r->in.ads) {
2100                 ads_destroy(&r->in.ads);
2101         }
2102
2103         return 0;
2104 }
2105
2106 /****************************************************************
2107 ****************************************************************/
2108
2109 static int libnet_destroy_UnjoinCtx(struct libnet_UnjoinCtx *r)
2110 {
2111         if (r->in.ads) {
2112                 ads_destroy(&r->in.ads);
2113         }
2114
2115         return 0;
2116 }
2117
2118 /****************************************************************
2119 ****************************************************************/
2120
2121 WERROR libnet_init_JoinCtx(TALLOC_CTX *mem_ctx,
2122                            struct libnet_JoinCtx **r)
2123 {
2124         struct libnet_JoinCtx *ctx;
2125
2126         ctx = talloc_zero(mem_ctx, struct libnet_JoinCtx);
2127         if (!ctx) {
2128                 return WERR_NOMEM;
2129         }
2130
2131         talloc_set_destructor(ctx, libnet_destroy_JoinCtx);
2132
2133         ctx->in.machine_name = talloc_strdup(mem_ctx, lp_netbios_name());
2134         W_ERROR_HAVE_NO_MEMORY(ctx->in.machine_name);
2135
2136         ctx->in.secure_channel_type = SEC_CHAN_WKSTA;
2137
2138         *r = ctx;
2139
2140         return WERR_OK;
2141 }
2142
2143 /****************************************************************
2144 ****************************************************************/
2145
2146 WERROR libnet_init_UnjoinCtx(TALLOC_CTX *mem_ctx,
2147                              struct libnet_UnjoinCtx **r)
2148 {
2149         struct libnet_UnjoinCtx *ctx;
2150
2151         ctx = talloc_zero(mem_ctx, struct libnet_UnjoinCtx);
2152         if (!ctx) {
2153                 return WERR_NOMEM;
2154         }
2155
2156         talloc_set_destructor(ctx, libnet_destroy_UnjoinCtx);
2157
2158         ctx->in.machine_name = talloc_strdup(mem_ctx, lp_netbios_name());
2159         W_ERROR_HAVE_NO_MEMORY(ctx->in.machine_name);
2160
2161         *r = ctx;
2162
2163         return WERR_OK;
2164 }
2165
2166 /****************************************************************
2167 ****************************************************************/
2168
2169 static WERROR libnet_join_check_config(TALLOC_CTX *mem_ctx,
2170                                        struct libnet_JoinCtx *r)
2171 {
2172         bool valid_security = false;
2173         bool valid_workgroup = false;
2174         bool valid_realm = false;
2175
2176         /* check if configuration is already set correctly */
2177
2178         valid_workgroup = strequal(lp_workgroup(), r->out.netbios_domain_name);
2179
2180         switch (r->out.domain_is_ad) {
2181                 case false:
2182                         valid_security = (lp_security() == SEC_DOMAIN)
2183                                 || (lp_server_role() == ROLE_DOMAIN_PDC)
2184                                 || (lp_server_role() == ROLE_DOMAIN_BDC);
2185                         if (valid_workgroup && valid_security) {
2186                                 /* nothing to be done */
2187                                 return WERR_OK;
2188                         }
2189                         break;
2190                 case true:
2191                         valid_realm = strequal(lp_realm(), r->out.dns_domain_name);
2192                         switch (lp_security()) {
2193                         case SEC_DOMAIN:
2194                         case SEC_ADS:
2195                                 valid_security = true;
2196                         }
2197
2198                         if (valid_workgroup && valid_realm && valid_security) {
2199                                 /* nothing to be done */
2200                                 return WERR_OK;
2201                         }
2202                         break;
2203         }
2204
2205         /* check if we are supposed to manipulate configuration */
2206
2207         if (!r->in.modify_config) {
2208
2209                 char *wrong_conf = talloc_strdup(mem_ctx, "");
2210
2211                 if (!valid_workgroup) {
2212                         wrong_conf = talloc_asprintf_append(wrong_conf,
2213                                 "\"workgroup\" set to '%s', should be '%s'",
2214                                 lp_workgroup(), r->out.netbios_domain_name);
2215                         W_ERROR_HAVE_NO_MEMORY(wrong_conf);
2216                 }
2217
2218                 if (!valid_realm) {
2219                         wrong_conf = talloc_asprintf_append(wrong_conf,
2220                                 "\"realm\" set to '%s', should be '%s'",
2221                                 lp_realm(), r->out.dns_domain_name);
2222                         W_ERROR_HAVE_NO_MEMORY(wrong_conf);
2223                 }
2224
2225                 if (!valid_security) {
2226                         const char *sec = NULL;
2227                         switch (lp_security()) {
2228                         case SEC_USER:  sec = "user"; break;
2229                         case SEC_DOMAIN: sec = "domain"; break;
2230                         case SEC_ADS: sec = "ads"; break;
2231                         }
2232                         wrong_conf = talloc_asprintf_append(wrong_conf,
2233                                 "\"security\" set to '%s', should be %s",
2234                                 sec, r->out.domain_is_ad ?
2235                                 "either 'domain' or 'ads'" : "'domain'");
2236                         W_ERROR_HAVE_NO_MEMORY(wrong_conf);
2237                 }
2238
2239                 libnet_join_set_error_string(mem_ctx, r,
2240                         "Invalid configuration (%s) and configuration modification "
2241                         "was not requested", wrong_conf);
2242                 return WERR_CAN_NOT_COMPLETE;
2243         }
2244
2245         /* check if we are able to manipulate configuration */
2246
2247         if (!lp_config_backend_is_registry()) {
2248                 libnet_join_set_error_string(mem_ctx, r,
2249                         "Configuration manipulation requested but not "
2250                         "supported by backend");
2251                 return WERR_NOT_SUPPORTED;
2252         }
2253
2254         return WERR_OK;
2255 }
2256
2257 /****************************************************************
2258 ****************************************************************/
2259
2260 static WERROR libnet_DomainJoin(TALLOC_CTX *mem_ctx,
2261                                 struct libnet_JoinCtx *r)
2262 {
2263         NTSTATUS status;
2264         WERROR werr;
2265         struct cli_state *cli = NULL;
2266 #ifdef HAVE_ADS
2267         ADS_STATUS ads_status;
2268 #endif /* HAVE_ADS */
2269         const char *pre_connect_realm = NULL;
2270         const char *numeric_dcip = NULL;
2271         const char *sitename = NULL;
2272
2273         /* Before contacting a DC, we can securely know
2274          * the realm only if the user specifies it.
2275          */
2276         if (r->in.use_kerberos &&
2277             r->in.domain_name_type == JoinDomNameTypeDNS) {
2278                 pre_connect_realm = r->in.domain_name;
2279         }
2280
2281         if (!r->in.dc_name) {
2282                 struct netr_DsRGetDCNameInfo *info;
2283                 const char *dc;
2284                 uint32_t name_type_flags = 0;
2285                 if (r->in.domain_name_type == JoinDomNameTypeDNS) {
2286                         name_type_flags = DS_IS_DNS_NAME;
2287                 } else if (r->in.domain_name_type == JoinDomNameTypeNBT) {
2288                         name_type_flags = DS_IS_FLAT_NAME;
2289                 }
2290                 status = dsgetdcname(mem_ctx,
2291                                      r->in.msg_ctx,
2292                                      r->in.domain_name,
2293                                      NULL,
2294                                      NULL,
2295                                      DS_FORCE_REDISCOVERY |
2296                                      DS_DIRECTORY_SERVICE_REQUIRED |
2297                                      DS_WRITABLE_REQUIRED |
2298                                      DS_RETURN_DNS_NAME |
2299                                      name_type_flags,
2300                                      &info);
2301                 if (!NT_STATUS_IS_OK(status)) {
2302                         libnet_join_set_error_string(mem_ctx, r,
2303                                 "failed to find DC for domain %s",
2304                                 r->in.domain_name,
2305                                 get_friendly_nt_error_msg(status));
2306                         return WERR_DCNOTFOUND;
2307                 }
2308
2309                 dc = strip_hostname(info->dc_unc);
2310                 r->in.dc_name = talloc_strdup(mem_ctx, dc);
2311                 W_ERROR_HAVE_NO_MEMORY(r->in.dc_name);
2312
2313                 if (info->dc_address == NULL || info->dc_address[0] != '\\' ||
2314                     info->dc_address[1] != '\\') {
2315                         DBG_ERR("ill-formed DC address '%s'\n",
2316                                 info->dc_address);
2317                         return WERR_DCNOTFOUND;
2318                 }
2319
2320                 numeric_dcip = info->dc_address + 2;
2321                 sitename = info->dc_site_name;
2322                 /* info goes out of scope but the memory stays
2323                    allocated on the talloc context */
2324         }
2325
2326         if (pre_connect_realm != NULL) {
2327                 struct sockaddr_storage ss = {0};
2328
2329                 if (numeric_dcip != NULL) {
2330                         if (!interpret_string_addr(&ss, numeric_dcip,
2331                                                    AI_NUMERICHOST)) {
2332                                 DBG_ERR(
2333                                     "cannot parse IP address '%s' of DC '%s'\n",
2334                                     numeric_dcip, r->in.dc_name);
2335                                 return WERR_DCNOTFOUND;
2336                         }
2337                 } else {
2338                         if (!interpret_string_addr(&ss, r->in.dc_name, 0)) {
2339                                 DBG_WARNING(
2340                                     "cannot resolve IP address of DC '%s'\n",
2341                                     r->in.dc_name);
2342                                 return WERR_DCNOTFOUND;
2343                         }
2344                 }
2345
2346                 /* The domain parameter is only used as modifier
2347                  * to krb5.conf file name. .JOIN is is not a valid
2348                  * NetBIOS name so it cannot clash with another domain
2349                  * -- Uri.
2350                  */
2351                 create_local_private_krb5_conf_for_domain(
2352                     pre_connect_realm, ".JOIN", sitename, &ss);
2353         }
2354
2355         status = libnet_join_lookup_dc_rpc(mem_ctx, r, &cli);
2356         if (!NT_STATUS_IS_OK(status)) {
2357                 libnet_join_set_error_string(mem_ctx, r,
2358                         "failed to lookup DC info for domain '%s' over rpc: %s",
2359                         r->in.domain_name, get_friendly_nt_error_msg(status));
2360                 return ntstatus_to_werror(status);
2361         }
2362
2363         werr = libnet_join_check_config(mem_ctx, r);
2364         if (!W_ERROR_IS_OK(werr)) {
2365                 goto done;
2366         }
2367
2368 #ifdef HAVE_ADS
2369
2370         create_local_private_krb5_conf_for_domain(
2371                 r->out.dns_domain_name, r->out.netbios_domain_name,
2372                 NULL, smbXcli_conn_remote_sockaddr(cli->conn));
2373
2374         if (r->out.domain_is_ad &&
2375             !(r->in.join_flags & WKSSVC_JOIN_FLAGS_JOIN_UNSECURE)) {
2376
2377                 const char *initial_account_ou = r->in.account_ou;
2378
2379                 /*
2380                  * we want to create the msDS-SupportedEncryptionTypes attribute
2381                  * as early as possible so always try an LDAP create as the user
2382                  * first. We copy r->in.account_ou because it may be changed
2383                  * during the machine pre-creation.
2384                  */
2385
2386                 ads_status = libnet_join_connect_ads_user(mem_ctx, r);
2387                 if (!ADS_ERR_OK(ads_status)) {
2388                         return WERR_DEFAULT_JOIN_REQUIRED;
2389                 }
2390
2391                 ads_status = libnet_join_precreate_machine_acct(mem_ctx, r);
2392                 if (ADS_ERR_OK(ads_status)) {
2393
2394                         /*
2395                          * LDAP object create succeeded, now go to the rpc
2396                          * password set routines
2397                          */
2398
2399                         r->in.join_flags &= ~WKSSVC_JOIN_FLAGS_ACCOUNT_CREATE;
2400                         goto rpc_join;
2401                 }
2402
2403                 if (initial_account_ou != NULL) {
2404                         libnet_join_set_error_string(mem_ctx, r,
2405                                 "failed to precreate account in ou %s: %s",
2406                                 r->in.account_ou,
2407                                 ads_errstr(ads_status));
2408                         return WERR_DEFAULT_JOIN_REQUIRED;
2409                 }
2410
2411                 DEBUG(5, ("failed to precreate account in ou %s: %s",
2412                         r->in.account_ou, ads_errstr(ads_status)));
2413         }
2414 #endif /* HAVE_ADS */
2415
2416  rpc_join:
2417         if ((r->in.join_flags & WKSSVC_JOIN_FLAGS_JOIN_UNSECURE) &&
2418             (r->in.join_flags & WKSSVC_JOIN_FLAGS_MACHINE_PWD_PASSED)) {
2419                 status = libnet_join_joindomain_rpc_unsecure(mem_ctx, r, cli);
2420         } else {
2421                 status = libnet_join_joindomain_rpc(mem_ctx, r, cli);
2422         }
2423         if (!NT_STATUS_IS_OK(status)) {
2424                 libnet_join_set_error_string(mem_ctx, r,
2425                         "failed to join domain '%s' over rpc: %s",
2426                         r->in.domain_name, get_friendly_nt_error_msg(status));
2427                 if (NT_STATUS_EQUAL(status, NT_STATUS_USER_EXISTS)) {
2428                         return WERR_SETUP_ALREADY_JOINED;
2429                 }
2430                 werr = ntstatus_to_werror(status);
2431                 goto done;
2432         }
2433
2434         if (!libnet_join_joindomain_store_secrets(mem_ctx, r)) {
2435                 werr = WERR_SETUP_NOT_JOINED;
2436                 goto done;
2437         }
2438
2439         werr = WERR_OK;
2440
2441  done:
2442         if (cli) {
2443                 cli_shutdown(cli);
2444         }
2445
2446         return werr;
2447 }
2448
2449 /****************************************************************
2450 ****************************************************************/
2451
2452 static WERROR libnet_join_rollback(TALLOC_CTX *mem_ctx,
2453                                    struct libnet_JoinCtx *r)
2454 {
2455         WERROR werr;
2456         struct libnet_UnjoinCtx *u = NULL;
2457
2458         werr = libnet_init_UnjoinCtx(mem_ctx, &u);
2459         if (!W_ERROR_IS_OK(werr)) {
2460                 return werr;
2461         }
2462
2463         u->in.debug             = r->in.debug;
2464         u->in.dc_name           = r->in.dc_name;
2465         u->in.domain_name       = r->in.domain_name;
2466         u->in.admin_account     = r->in.admin_account;
2467         u->in.admin_password    = r->in.admin_password;
2468         u->in.modify_config     = r->in.modify_config;
2469         u->in.use_kerberos      = r->in.use_kerberos;
2470         u->in.unjoin_flags      = WKSSVC_JOIN_FLAGS_JOIN_TYPE |
2471                                   WKSSVC_JOIN_FLAGS_ACCOUNT_DELETE;
2472
2473         werr = libnet_Unjoin(mem_ctx, u);
2474         TALLOC_FREE(u);
2475
2476         return werr;
2477 }
2478
2479 /****************************************************************
2480 ****************************************************************/
2481
2482 WERROR libnet_Join(TALLOC_CTX *mem_ctx,
2483                    struct libnet_JoinCtx *r)
2484 {
2485         WERROR werr;
2486
2487         if (r->in.debug) {
2488                 LIBNET_JOIN_IN_DUMP_CTX(mem_ctx, r);
2489         }
2490
2491         ZERO_STRUCT(r->out);
2492
2493         werr = libnet_join_pre_processing(mem_ctx, r);
2494         if (!W_ERROR_IS_OK(werr)) {
2495                 goto done;
2496         }
2497
2498         if (r->in.join_flags & WKSSVC_JOIN_FLAGS_JOIN_TYPE) {
2499                 werr = libnet_DomainJoin(mem_ctx, r);
2500                 if (!W_ERROR_IS_OK(werr)) {
2501                         goto done;
2502                 }
2503         }
2504
2505         werr = libnet_join_post_processing(mem_ctx, r);
2506         if (!W_ERROR_IS_OK(werr)) {
2507                 goto done;
2508         }
2509
2510         if (r->in.join_flags & WKSSVC_JOIN_FLAGS_JOIN_TYPE) {
2511                 werr = libnet_join_post_verify(mem_ctx, r);
2512                 if (!W_ERROR_IS_OK(werr)) {
2513                         libnet_join_rollback(mem_ctx, r);
2514                 }
2515         }
2516
2517  done:
2518         r->out.result = werr;
2519
2520         if (r->in.debug) {
2521                 LIBNET_JOIN_OUT_DUMP_CTX(mem_ctx, r);
2522         }
2523         return werr;
2524 }
2525
2526 /****************************************************************
2527 ****************************************************************/
2528
2529 static WERROR libnet_DomainUnjoin(TALLOC_CTX *mem_ctx,
2530                                   struct libnet_UnjoinCtx *r)
2531 {
2532         NTSTATUS status;
2533
2534         if (!r->in.domain_sid) {
2535                 struct dom_sid sid;
2536                 if (!secrets_fetch_domain_sid(lp_workgroup(), &sid)) {
2537                         libnet_unjoin_set_error_string(mem_ctx, r,
2538                                 "Unable to fetch domain sid: are we joined?");
2539                         return WERR_SETUP_NOT_JOINED;
2540                 }
2541                 r->in.domain_sid = dom_sid_dup(mem_ctx, &sid);
2542                 W_ERROR_HAVE_NO_MEMORY(r->in.domain_sid);
2543         }
2544
2545         if (!(r->in.unjoin_flags & WKSSVC_JOIN_FLAGS_ACCOUNT_DELETE) && 
2546             !r->in.delete_machine_account) {
2547                 libnet_join_unjoindomain_remove_secrets(mem_ctx, r);
2548                 return WERR_OK;
2549         }
2550
2551         if (!r->in.dc_name) {
2552                 struct netr_DsRGetDCNameInfo *info;
2553                 const char *dc;
2554                 status = dsgetdcname(mem_ctx,
2555                                      r->in.msg_ctx,
2556                                      r->in.domain_name,
2557                                      NULL,
2558                                      NULL,
2559                                      DS_DIRECTORY_SERVICE_REQUIRED |
2560                                      DS_WRITABLE_REQUIRED |
2561                                      DS_RETURN_DNS_NAME,
2562                                      &info);
2563                 if (!NT_STATUS_IS_OK(status)) {
2564                         libnet_unjoin_set_error_string(mem_ctx, r,
2565                                 "failed to find DC for domain %s",
2566                                 r->in.domain_name,
2567                                 get_friendly_nt_error_msg(status));
2568                         return WERR_DCNOTFOUND;
2569                 }
2570
2571                 dc = strip_hostname(info->dc_unc);
2572                 r->in.dc_name = talloc_strdup(mem_ctx, dc);
2573                 W_ERROR_HAVE_NO_MEMORY(r->in.dc_name);
2574         }
2575
2576 #ifdef HAVE_ADS
2577         /* for net ads leave, try to delete the account.  If it works, 
2578            no sense in disabling.  If it fails, we can still try to 
2579            disable it. jmcd */
2580
2581         if (r->in.delete_machine_account) {
2582                 ADS_STATUS ads_status;
2583                 ads_status = libnet_unjoin_connect_ads(mem_ctx, r);
2584                 if (ADS_ERR_OK(ads_status)) {
2585                         /* dirty hack */
2586                         r->out.dns_domain_name = 
2587                                 talloc_strdup(mem_ctx,
2588                                               r->in.ads->server.realm);
2589                         ads_status = 
2590                                 libnet_unjoin_remove_machine_acct(mem_ctx, r);
2591                 }
2592                 if (!ADS_ERR_OK(ads_status)) {
2593                         libnet_unjoin_set_error_string(mem_ctx, r,
2594                                 "failed to remove machine account from AD: %s",
2595                                 ads_errstr(ads_status));
2596                 } else {
2597                         r->out.deleted_machine_account = true;
2598                         W_ERROR_HAVE_NO_MEMORY(r->out.dns_domain_name);
2599                         libnet_join_unjoindomain_remove_secrets(mem_ctx, r);
2600                         return WERR_OK;
2601                 }
2602         }
2603 #endif /* HAVE_ADS */
2604
2605         /* The WKSSVC_JOIN_FLAGS_ACCOUNT_DELETE flag really means 
2606            "disable".  */
2607         if (r->in.unjoin_flags & WKSSVC_JOIN_FLAGS_ACCOUNT_DELETE) {
2608                 status = libnet_join_unjoindomain_rpc(mem_ctx, r);
2609                 if (!NT_STATUS_IS_OK(status)) {
2610                         libnet_unjoin_set_error_string(mem_ctx, r,
2611                                 "failed to disable machine account via rpc: %s",
2612                                 get_friendly_nt_error_msg(status));
2613                         if (NT_STATUS_EQUAL(status, NT_STATUS_NO_SUCH_USER)) {
2614                                 return WERR_SETUP_NOT_JOINED;
2615                         }
2616                         return ntstatus_to_werror(status);
2617                 }
2618
2619                 r->out.disabled_machine_account = true;
2620         }
2621
2622         /* If disable succeeded or was not requested at all, we 
2623            should be getting rid of our end of things */
2624
2625         libnet_join_unjoindomain_remove_secrets(mem_ctx, r);
2626
2627         return WERR_OK;
2628 }
2629
2630 /****************************************************************
2631 ****************************************************************/
2632
2633 static WERROR libnet_unjoin_pre_processing(TALLOC_CTX *mem_ctx,
2634                                            struct libnet_UnjoinCtx *r)
2635 {
2636         if (!r->in.domain_name) {
2637                 libnet_unjoin_set_error_string(mem_ctx, r,
2638                         "No domain name defined");
2639                 return WERR_INVALID_PARAM;
2640         }
2641
2642         if (!libnet_parse_domain_dc(mem_ctx, r->in.domain_name,
2643                                     &r->in.domain_name,
2644                                     &r->in.dc_name)) {
2645                 libnet_unjoin_set_error_string(mem_ctx, r,
2646                         "Failed to parse domain name");
2647                 return WERR_INVALID_PARAM;
2648         }
2649
2650         if (IS_DC) {
2651                 return WERR_SETUP_DOMAIN_CONTROLLER;
2652         }
2653
2654         if (!r->in.admin_domain) {
2655                 char *admin_domain = NULL;
2656                 char *admin_account = NULL;
2657                 split_domain_user(mem_ctx,
2658                                   r->in.admin_account,
2659                                   &admin_domain,
2660                                   &admin_account);
2661                 r->in.admin_domain = admin_domain;
2662                 r->in.admin_account = admin_account;
2663         }
2664
2665         if (!secrets_init()) {
2666                 libnet_unjoin_set_error_string(mem_ctx, r,
2667                         "Unable to open secrets database");
2668                 return WERR_CAN_NOT_COMPLETE;
2669         }
2670
2671         return WERR_OK;
2672 }
2673
2674 /****************************************************************
2675 ****************************************************************/
2676
2677 static WERROR libnet_unjoin_post_processing(TALLOC_CTX *mem_ctx,
2678                                             struct libnet_UnjoinCtx *r)
2679 {
2680         saf_delete(r->out.netbios_domain_name);
2681         saf_delete(r->out.dns_domain_name);
2682
2683         return libnet_unjoin_config(r);
2684 }
2685
2686 /****************************************************************
2687 ****************************************************************/
2688
2689 WERROR libnet_Unjoin(TALLOC_CTX *mem_ctx,
2690                      struct libnet_UnjoinCtx *r)
2691 {
2692         WERROR werr;
2693
2694         if (r->in.debug) {
2695                 LIBNET_UNJOIN_IN_DUMP_CTX(mem_ctx, r);
2696         }
2697
2698         werr = libnet_unjoin_pre_processing(mem_ctx, r);
2699         if (!W_ERROR_IS_OK(werr)) {
2700                 goto done;
2701         }
2702
2703         if (r->in.unjoin_flags & WKSSVC_JOIN_FLAGS_JOIN_TYPE) {
2704                 werr = libnet_DomainUnjoin(mem_ctx, r);
2705                 if (!W_ERROR_IS_OK(werr)) {
2706                         libnet_unjoin_config(r);
2707                         goto done;
2708                 }
2709         }
2710
2711         werr = libnet_unjoin_post_processing(mem_ctx, r);
2712         if (!W_ERROR_IS_OK(werr)) {
2713                 goto done;
2714         }
2715
2716  done:
2717         r->out.result = werr;
2718
2719         if (r->in.debug) {
2720                 LIBNET_UNJOIN_OUT_DUMP_CTX(mem_ctx, r);
2721         }
2722
2723         return werr;
2724 }