Rearrange libnet join code and add support for account pre-creation in AD.
[sfrench/samba-autobuild/.git] / source / libnet / libnet_join.c
1 /*
2  *  Unix SMB/CIFS implementation.
3  *  libnet Join Support
4  *  Copyright (C) Gerald (Jerry) Carter 2006
5  *  Copyright (C) Guenther Deschner 2007-2008
6  *
7  *  This program is free software; you can redistribute it and/or modify
8  *  it under the terms of the GNU General Public License as published by
9  *  the Free Software Foundation; either version 3 of the License, or
10  *  (at your option) any later version.
11  *
12  *  This program is distributed in the hope that it will be useful,
13  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  *  GNU General Public License for more details.
16  *
17  *  You should have received a copy of the GNU General Public License
18  *  along with this program; if not, see <http://www.gnu.org/licenses/>.
19  */
20
21 #include "includes.h"
22 #include "libnet/libnet_join.h"
23 #include "libnet/libnet_proto.h"
24
25 /****************************************************************
26 ****************************************************************/
27
28 static void libnet_join_set_error_string(TALLOC_CTX *mem_ctx,
29                                          struct libnet_JoinCtx *r,
30                                          const char *format, ...)
31 {
32         va_list args;
33         char *tmp = NULL;
34
35         va_start(args, format);
36         tmp = talloc_vasprintf(mem_ctx, format, args);
37         va_end(args);
38
39         TALLOC_FREE(r->out.error_string);
40         r->out.error_string = tmp;
41 }
42
43 /****************************************************************
44 ****************************************************************/
45
46 static void libnet_unjoin_set_error_string(TALLOC_CTX *mem_ctx,
47                                            struct libnet_UnjoinCtx *r,
48                                            const char *format, ...)
49 {
50         va_list args;
51         char *tmp = NULL;
52
53         va_start(args, format);
54         tmp = talloc_vasprintf(mem_ctx, format, args);
55         va_end(args);
56
57         TALLOC_FREE(r->out.error_string);
58         r->out.error_string = tmp;
59 }
60
61 /****************************************************************
62 ****************************************************************/
63
64 static ADS_STATUS libnet_connect_ads(const char *dns_domain_name,
65                                      const char *netbios_domain_name,
66                                      const char *dc_name,
67                                      const char *user_name,
68                                      const char *password,
69                                      ADS_STRUCT **ads)
70 {
71         ADS_STATUS status;
72         ADS_STRUCT *my_ads = NULL;
73
74         my_ads = ads_init(dns_domain_name,
75                           netbios_domain_name,
76                           dc_name);
77         if (!my_ads) {
78                 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
79         }
80
81         if (user_name) {
82                 SAFE_FREE(my_ads->auth.user_name);
83                 my_ads->auth.user_name = SMB_STRDUP(user_name);
84         }
85
86         if (password) {
87                 SAFE_FREE(my_ads->auth.password);
88                 my_ads->auth.password = SMB_STRDUP(password);
89         }
90
91         status = ads_connect(my_ads);
92         if (!ADS_ERR_OK(status)) {
93                 ads_destroy(&my_ads);
94                 return status;
95         }
96
97         *ads = my_ads;
98         return ADS_SUCCESS;
99 }
100
101 /****************************************************************
102 ****************************************************************/
103
104 static ADS_STATUS libnet_join_connect_ads(TALLOC_CTX *mem_ctx,
105                                           struct libnet_JoinCtx *r)
106 {
107         ADS_STATUS status;
108
109         if (r->in.ads) {
110                 ads_destroy(&r->in.ads);
111         }
112
113         status = libnet_connect_ads(r->in.domain_name,
114                                     r->in.domain_name,
115                                     r->in.dc_name,
116                                     r->in.admin_account,
117                                     r->in.admin_password,
118                                     &r->in.ads);
119         if (!ADS_ERR_OK(status)) {
120                 libnet_join_set_error_string(mem_ctx, r,
121                         "failed to connect to AD: %s\n",
122                         ads_errstr(status));
123         }
124
125         return status;
126 }
127
128 /****************************************************************
129 ****************************************************************/
130
131 static ADS_STATUS libnet_unjoin_connect_ads(TALLOC_CTX *mem_ctx,
132                                             struct libnet_UnjoinCtx *r)
133 {
134         ADS_STATUS status;
135
136         if (r->in.ads) {
137                 ads_destroy(&r->in.ads);
138         }
139
140         status = libnet_connect_ads(r->in.domain_name,
141                                     r->in.domain_name,
142                                     r->in.dc_name,
143                                     r->in.admin_account,
144                                     r->in.admin_password,
145                                     &r->in.ads);
146         if (!ADS_ERR_OK(status)) {
147                 libnet_unjoin_set_error_string(mem_ctx, r,
148                         "failed to connect to AD: %s\n",
149                         ads_errstr(status));
150         }
151
152         return status;
153 }
154
155 /****************************************************************
156 ****************************************************************/
157
158 static ADS_STATUS libnet_join_precreate_machine_acct(TALLOC_CTX *mem_ctx,
159                                                      struct libnet_JoinCtx *r)
160 {
161         ADS_STATUS status;
162         LDAPMessage *res = NULL;
163         const char *attrs[] = { "dn", NULL };
164
165         status = ads_search_dn(r->in.ads, &res, r->in.account_ou, attrs);
166         if (!ADS_ERR_OK(status)) {
167                 return status;
168         }
169
170         if (ads_count_replies(r->in.ads, res) != 1) {
171                 ads_msgfree(r->in.ads, res);
172                 return ADS_ERROR_LDAP(LDAP_NO_SUCH_OBJECT);
173         }
174
175         status = ads_create_machine_acct(r->in.ads,
176                                          r->in.machine_name,
177                                          r->in.account_ou);
178         ads_msgfree(r->in.ads, res);
179
180         if ((status.error_type == ENUM_ADS_ERROR_LDAP) &&
181             (status.err.rc == LDAP_ALREADY_EXISTS)) {
182                 status = ADS_SUCCESS;
183         }
184
185         return status;
186 }
187
188 /****************************************************************
189 ****************************************************************/
190
191 static bool libnet_join_joindomain_store_secrets(TALLOC_CTX *mem_ctx,
192                                                  struct libnet_JoinCtx *r)
193 {
194         if (!secrets_store_domain_sid(r->out.netbios_domain_name,
195                                       r->out.domain_sid))
196         {
197                 return false;
198         }
199
200         if (!secrets_store_machine_password(r->in.machine_password,
201                                             r->out.netbios_domain_name,
202                                             SEC_CHAN_WKSTA))
203         {
204                 return false;
205         }
206
207         return true;
208 }
209
210 /****************************************************************
211 ****************************************************************/
212
213 static NTSTATUS libnet_join_joindomain_rpc(TALLOC_CTX *mem_ctx,
214                                            struct libnet_JoinCtx *r)
215 {
216         struct cli_state *cli = NULL;
217         struct rpc_pipe_client *pipe_hnd = NULL;
218         POLICY_HND sam_pol, domain_pol, user_pol, lsa_pol;
219         NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
220         char *acct_name;
221         const char *const_acct_name;
222         uint32 user_rid;
223         uint32 num_rids, *name_types, *user_rids;
224         uint32 flags = 0x3e8;
225         uint32 acb_info = ACB_WSTRUST;
226         uint32 fields_present;
227         uchar pwbuf[532];
228         SAM_USERINFO_CTR ctr;
229         SAM_USER_INFO_25 p25;
230         const int infolevel = 25;
231         struct MD5Context md5ctx;
232         uchar md5buffer[16];
233         DATA_BLOB digested_session_key;
234         uchar md4_trust_password[16];
235
236         if (!r->in.machine_password) {
237                 r->in.machine_password = talloc_strdup(mem_ctx, generate_random_str(DEFAULT_TRUST_ACCOUNT_PASSWORD_LENGTH));
238                 NT_STATUS_HAVE_NO_MEMORY(r->in.machine_password);
239         }
240
241         status = cli_full_connection(&cli, NULL,
242                                      r->in.dc_name,
243                                      NULL, 0,
244                                      "IPC$", "IPC",
245                                      r->in.admin_account,
246                                      NULL,
247                                      r->in.admin_password,
248                                      0,
249                                      Undefined, NULL);
250
251         if (!NT_STATUS_IS_OK(status)) {
252                 goto done;
253         }
254
255         pipe_hnd = cli_rpc_pipe_open_noauth(cli, PI_LSARPC, &status);
256         if (!pipe_hnd) {
257                 goto done;
258         }
259
260         status = rpccli_lsa_open_policy(pipe_hnd, mem_ctx, True,
261                                         SEC_RIGHTS_MAXIMUM_ALLOWED, &lsa_pol);
262         if (!NT_STATUS_IS_OK(status)) {
263                 goto done;
264         }
265
266         status = rpccli_lsa_query_info_policy2(pipe_hnd, mem_ctx, &lsa_pol,
267                                                12,
268                                                &r->out.netbios_domain_name,
269                                                &r->out.dns_domain_name,
270                                                NULL,
271                                                NULL,
272                                                &r->out.domain_sid);
273
274         if (!NT_STATUS_IS_OK(status)) {
275                 status = rpccli_lsa_query_info_policy(pipe_hnd, mem_ctx, &lsa_pol,
276                                                       5,
277                                                       &r->out.netbios_domain_name,
278                                                       &r->out.domain_sid);
279                 if (!NT_STATUS_IS_OK(status)) {
280                         goto done;
281                 }
282         }
283
284         rpccli_lsa_Close(pipe_hnd, mem_ctx, &lsa_pol);
285         cli_rpc_pipe_close(pipe_hnd);
286
287         pipe_hnd = cli_rpc_pipe_open_noauth(cli, PI_SAMR, &status);
288         if (!pipe_hnd) {
289                 goto done;
290         }
291
292         status = rpccli_samr_connect(pipe_hnd, mem_ctx,
293                                      SEC_RIGHTS_MAXIMUM_ALLOWED, &sam_pol);
294         if (!NT_STATUS_IS_OK(status)) {
295                 goto done;
296         }
297
298         status = rpccli_samr_open_domain(pipe_hnd, mem_ctx, &sam_pol,
299                                          SEC_RIGHTS_MAXIMUM_ALLOWED,
300                                          r->out.domain_sid,
301                                          &domain_pol);
302         if (!NT_STATUS_IS_OK(status)) {
303                 goto done;
304         }
305
306         acct_name = talloc_asprintf(mem_ctx, "%s$", global_myname());
307         strlower_m(acct_name);
308         const_acct_name = acct_name;
309
310         if (r->in.join_flags & WKSSVC_JOIN_FLAGS_ACCOUNT_CREATE) {
311                 status = rpccli_samr_create_dom_user(pipe_hnd, mem_ctx,
312                                                      &domain_pol,
313                                                      acct_name, ACB_WSTRUST,
314                                                      0xe005000b, &user_pol,
315                                                      &user_rid);
316                 if (NT_STATUS_EQUAL(status, NT_STATUS_USER_EXISTS)) {
317                         if (!(r->in.join_flags & WKSSVC_JOIN_FLAGS_DOMAIN_JOIN_IF_JOINED)) {
318                                 goto done;
319                         }
320                 }
321
322                 if (NT_STATUS_IS_OK(status)) {
323                         rpccli_samr_close(pipe_hnd, mem_ctx, &user_pol);
324                 }
325         }
326
327         status = rpccli_samr_lookup_names(pipe_hnd, mem_ctx,
328                                           &domain_pol, flags, 1,
329                                           &const_acct_name,
330                                           &num_rids, &user_rids, &name_types);
331         if (!NT_STATUS_IS_OK(status)) {
332                 goto done;
333         }
334
335         if (name_types[0] != SID_NAME_USER) {
336                 status = NT_STATUS_INVALID_WORKSTATION;
337                 goto done;
338         }
339
340         user_rid = user_rids[0];
341
342         status = rpccli_samr_open_user(pipe_hnd, mem_ctx, &domain_pol,
343                                        SEC_RIGHTS_MAXIMUM_ALLOWED, user_rid,
344                                        &user_pol);
345         if (!NT_STATUS_IS_OK(status)) {
346                 goto done;
347         }
348
349         E_md4hash(r->in.machine_password, md4_trust_password);
350         encode_pw_buffer(pwbuf, r->in.machine_password, STR_UNICODE);
351
352         generate_random_buffer((uint8*)md5buffer, sizeof(md5buffer));
353         digested_session_key = data_blob_talloc(mem_ctx, 0, 16);
354
355         MD5Init(&md5ctx);
356         MD5Update(&md5ctx, md5buffer, sizeof(md5buffer));
357         MD5Update(&md5ctx, cli->user_session_key.data,
358                   cli->user_session_key.length);
359         MD5Final(digested_session_key.data, &md5ctx);
360
361         SamOEMhashBlob(pwbuf, sizeof(pwbuf), &digested_session_key);
362         memcpy(&pwbuf[516], md5buffer, sizeof(md5buffer));
363
364         acb_info |= ACB_PWNOEXP;
365 #if 0
366         if ( dom_type == ND_TYPE_AD ) {
367 #if !defined(ENCTYPE_ARCFOUR_HMAC)
368                 acb_info |= ACB_USE_DES_KEY_ONLY;
369 #endif
370                 ;;
371         }
372 #endif
373         ZERO_STRUCT(ctr);
374         ZERO_STRUCT(p25);
375
376         fields_present = ACCT_NT_PWD_SET | ACCT_LM_PWD_SET | ACCT_FLAGS;
377         init_sam_user_info25P(&p25, fields_present, acb_info, (char *)pwbuf);
378
379         ctr.switch_value = infolevel;
380         ctr.info.id25    = &p25;
381
382         status = rpccli_samr_set_userinfo2(pipe_hnd, mem_ctx, &user_pol,
383                                            infolevel, &cli->user_session_key,
384                                            &ctr);
385         if (!NT_STATUS_IS_OK(status)) {
386                 goto done;
387         }
388
389         rpccli_samr_close(pipe_hnd, mem_ctx, &user_pol);
390         cli_rpc_pipe_close(pipe_hnd);
391
392         status = NT_STATUS_OK;
393  done:
394         if (cli) {
395                 cli_shutdown(cli);
396         }
397
398         return status;
399 }
400
401 /****************************************************************
402 ****************************************************************/
403
404 static bool libnet_join_unjoindomain_remove_secrets(TALLOC_CTX *mem_ctx,
405                                                     struct libnet_UnjoinCtx *r)
406 {
407         if (!secrets_delete_machine_password_ex(lp_workgroup())) {
408                 return false;
409         }
410
411         if (!secrets_delete_domain_sid(lp_workgroup())) {
412                 return false;
413         }
414
415         return true;
416 }
417
418 /****************************************************************
419 ****************************************************************/
420
421 static NTSTATUS libnet_join_unjoindomain_rpc(TALLOC_CTX *mem_ctx,
422                                              struct libnet_UnjoinCtx *r)
423 {
424         struct cli_state *cli = NULL;
425         struct rpc_pipe_client *pipe_hnd = NULL;
426         POLICY_HND sam_pol, domain_pol, user_pol;
427         NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
428         char *acct_name;
429         uint32 flags = 0x3e8;
430         const char *const_acct_name;
431         uint32 user_rid;
432         uint32 num_rids, *name_types, *user_rids;
433         SAM_USERINFO_CTR ctr, *qctr = NULL;
434         SAM_USER_INFO_16 p16;
435
436         status = cli_full_connection(&cli, NULL,
437                                      r->in.dc_name,
438                                      NULL, 0,
439                                      "IPC$", "IPC",
440                                      r->in.admin_account,
441                                      NULL,
442                                      r->in.admin_password,
443                                      0, Undefined, NULL);
444
445         if (!NT_STATUS_IS_OK(status)) {
446                 goto done;
447         }
448
449         pipe_hnd = cli_rpc_pipe_open_noauth(cli, PI_SAMR, &status);
450         if (!pipe_hnd) {
451                 goto done;
452         }
453
454         status = rpccli_samr_connect(pipe_hnd, mem_ctx,
455                                      SEC_RIGHTS_MAXIMUM_ALLOWED, &sam_pol);
456         if (!NT_STATUS_IS_OK(status)) {
457                 goto done;
458         }
459
460         status = rpccli_samr_open_domain(pipe_hnd, mem_ctx, &sam_pol,
461                                          SEC_RIGHTS_MAXIMUM_ALLOWED,
462                                          r->in.domain_sid,
463                                          &domain_pol);
464         if (!NT_STATUS_IS_OK(status)) {
465                 goto done;
466         }
467
468         acct_name = talloc_asprintf(mem_ctx, "%s$", global_myname());
469         strlower_m(acct_name);
470         const_acct_name = acct_name;
471
472         status = rpccli_samr_lookup_names(pipe_hnd, mem_ctx,
473                                           &domain_pol, flags, 1,
474                                           &const_acct_name,
475                                           &num_rids, &user_rids, &name_types);
476         if (!NT_STATUS_IS_OK(status)) {
477                 goto done;
478         }
479
480         if (name_types[0] != SID_NAME_USER) {
481                 status = NT_STATUS_INVALID_WORKSTATION;
482                 goto done;
483         }
484
485         user_rid = user_rids[0];
486
487         status = rpccli_samr_open_user(pipe_hnd, mem_ctx, &domain_pol,
488                                        SEC_RIGHTS_MAXIMUM_ALLOWED,
489                                        user_rid, &user_pol);
490         if (!NT_STATUS_IS_OK(status)) {
491                 goto done;
492         }
493
494         status = rpccli_samr_query_userinfo(pipe_hnd, mem_ctx,
495                                             &user_pol, 16, &qctr);
496         if (!NT_STATUS_IS_OK(status)) {
497                 rpccli_samr_close(pipe_hnd, mem_ctx, &user_pol);
498                 goto done;
499         }
500
501         ZERO_STRUCT(ctr);
502         ctr.switch_value = 16;
503         ctr.info.id16 = &p16;
504
505         p16.acb_info = qctr->info.id16->acb_info | ACB_DISABLED;
506
507         status = rpccli_samr_set_userinfo2(pipe_hnd, mem_ctx, &user_pol, 16,
508                                            &cli->user_session_key, &ctr);
509
510         rpccli_samr_close(pipe_hnd, mem_ctx, &user_pol);
511
512 done:
513         if (pipe_hnd) {
514                 rpccli_samr_close(pipe_hnd, mem_ctx, &domain_pol);
515                 rpccli_samr_close(pipe_hnd, mem_ctx, &sam_pol);
516                 cli_rpc_pipe_close(pipe_hnd);
517         }
518
519         if (cli) {
520                 cli_shutdown(cli);
521         }
522
523         return status;
524 }
525
526 /****************************************************************
527 ****************************************************************/
528
529 static WERROR do_join_modify_vals_config(struct libnet_JoinCtx *r)
530 {
531         WERROR werr;
532         bool is_ad = false;
533
534         if (!(r->in.join_flags & WKSSVC_JOIN_FLAGS_JOIN_TYPE)) {
535
536                 werr = libnet_conf_set_global_parameter("security", "user");
537                 W_ERROR_NOT_OK_RETURN(werr);
538
539                 werr = libnet_conf_set_global_parameter("workgroup",
540                                                         r->in.domain_name);
541                 return werr;
542         }
543
544         if (r->out.dns_domain_name) {
545                 is_ad = true;
546         }
547
548         werr = libnet_conf_set_global_parameter("security", "domain");
549         W_ERROR_NOT_OK_RETURN(werr);
550
551         werr = libnet_conf_set_global_parameter("workgroup",
552                                                 r->out.netbios_domain_name);
553         W_ERROR_NOT_OK_RETURN(werr);
554
555         if (is_ad) {
556                 werr = libnet_conf_set_global_parameter("security", "ads");
557                 W_ERROR_NOT_OK_RETURN(werr);
558
559                 werr = libnet_conf_set_global_parameter("realm",
560                                                        r->out.dns_domain_name);
561                 W_ERROR_NOT_OK_RETURN(werr);
562         }
563
564         return werr;
565 }
566
567 /****************************************************************
568 ****************************************************************/
569
570 static WERROR do_unjoin_modify_vals_config(struct libnet_UnjoinCtx *r)
571 {
572         WERROR werr = WERR_OK;
573
574         if (r->in.unjoin_flags & WKSSVC_JOIN_FLAGS_JOIN_TYPE) {
575
576                 werr = libnet_conf_set_global_parameter("security", "user");
577                 W_ERROR_NOT_OK_RETURN(werr);
578         }
579
580         werr = libnet_conf_delete_parameter(GLOBAL_NAME, "realm");
581
582         return werr;
583 }
584
585 /****************************************************************
586 ****************************************************************/
587
588 static WERROR do_JoinConfig(struct libnet_JoinCtx *r)
589 {
590         WERROR werr;
591
592         if (!W_ERROR_IS_OK(r->out.result)) {
593                 return r->out.result;
594         }
595
596         if (!r->in.modify_config) {
597                 return WERR_OK;
598         }
599
600         werr = do_join_modify_vals_config(r);
601         if (!W_ERROR_IS_OK(werr)) {
602                 return werr;
603         }
604
605         r->out.modified_config = true;
606         r->out.result = werr;
607
608         return werr;
609 }
610
611 /****************************************************************
612 ****************************************************************/
613
614 static WERROR do_UnjoinConfig(struct libnet_UnjoinCtx *r)
615 {
616         WERROR werr;
617
618         if (!W_ERROR_IS_OK(r->out.result)) {
619                 return r->out.result;
620         }
621
622         if (!r->in.modify_config) {
623                 return WERR_OK;
624         }
625
626         werr = do_unjoin_modify_vals_config(r);
627         if (!W_ERROR_IS_OK(werr)) {
628                 return werr;
629         }
630
631         r->out.modified_config = true;
632         r->out.result = werr;
633
634         return werr;
635 }
636
637 /****************************************************************
638 ****************************************************************/
639
640 static int libnet_destroy_JoinCtx(struct libnet_JoinCtx *r)
641 {
642         if (r->in.ads) {
643                 ads_destroy(&r->in.ads);
644         }
645
646         return 0;
647 }
648
649 /****************************************************************
650 ****************************************************************/
651
652 static int libnet_destroy_UnjoinCtx(struct libnet_UnjoinCtx *r)
653 {
654         if (r->in.ads) {
655                 ads_destroy(&r->in.ads);
656         }
657
658         return 0;
659 }
660
661 /****************************************************************
662 ****************************************************************/
663
664 WERROR libnet_init_JoinCtx(TALLOC_CTX *mem_ctx,
665                            struct libnet_JoinCtx **r)
666 {
667         struct libnet_JoinCtx *ctx;
668
669         ctx = talloc_zero(mem_ctx, struct libnet_JoinCtx);
670         if (!ctx) {
671                 return WERR_NOMEM;
672         }
673
674         talloc_set_destructor(ctx, libnet_destroy_JoinCtx);
675
676         ctx->in.machine_name = talloc_strdup(mem_ctx, global_myname());
677         W_ERROR_HAVE_NO_MEMORY(ctx->in.machine_name);
678
679         *r = ctx;
680
681         return WERR_OK;
682 }
683
684 /****************************************************************
685 ****************************************************************/
686
687 WERROR libnet_init_UnjoinCtx(TALLOC_CTX *mem_ctx,
688                              struct libnet_UnjoinCtx **r)
689 {
690         struct libnet_UnjoinCtx *ctx;
691
692         ctx = talloc_zero(mem_ctx, struct libnet_UnjoinCtx);
693         if (!ctx) {
694                 return WERR_NOMEM;
695         }
696
697         talloc_set_destructor(ctx, libnet_destroy_UnjoinCtx);
698
699         ctx->in.machine_name = talloc_strdup(mem_ctx, global_myname());
700         W_ERROR_HAVE_NO_MEMORY(ctx->in.machine_name);
701
702         *r = ctx;
703
704         return WERR_OK;
705 }
706
707 /****************************************************************
708 ****************************************************************/
709
710 static WERROR libnet_DomainJoin(TALLOC_CTX *mem_ctx,
711                                 struct libnet_JoinCtx *r)
712 {
713         NTSTATUS status;
714         ADS_STATUS ads_status;
715
716         if (r->in.account_ou) {
717                 ads_status = libnet_join_connect_ads(mem_ctx, r);
718                 if (!ADS_ERR_OK(ads_status)) {
719                         return WERR_GENERAL_FAILURE;
720                 }
721                 ads_status = libnet_join_precreate_machine_acct(mem_ctx, r);
722                 if (!ADS_ERR_OK(ads_status)) {
723                         libnet_join_set_error_string(mem_ctx, r,
724                                 "failed to precreate account in ou %s: %s\n",
725                                 r->in.account_ou,
726                                 ads_errstr(ads_status));
727                         return WERR_GENERAL_FAILURE;
728                 }
729
730                 r->in.join_flags &= ~WKSSVC_JOIN_FLAGS_ACCOUNT_CREATE;
731         }
732
733         status = libnet_join_joindomain_rpc(mem_ctx, r);
734         if (!NT_STATUS_IS_OK(status)) {
735                 if (NT_STATUS_EQUAL(status, NT_STATUS_USER_EXISTS)) {
736                         return WERR_SETUP_ALREADY_JOINED;
737                 }
738                 return ntstatus_to_werror(status);
739         }
740
741         if (!libnet_join_joindomain_store_secrets(mem_ctx, r)) {
742                 return WERR_SETUP_NOT_JOINED;
743         }
744
745         return WERR_OK;
746 }
747
748 /****************************************************************
749 ****************************************************************/
750
751 WERROR libnet_Join(TALLOC_CTX *mem_ctx,
752                    struct libnet_JoinCtx *r)
753 {
754         WERROR werr;
755
756         if (!r->in.domain_name) {
757                 return WERR_INVALID_PARAM;
758         }
759
760         if (r->in.modify_config && !lp_include_registry_globals()) {
761                 return WERR_NOT_SUPPORTED;
762         }
763
764         if (IS_DC) {
765                 return WERR_SETUP_DOMAIN_CONTROLLER;
766         }
767
768         if (r->in.join_flags & WKSSVC_JOIN_FLAGS_JOIN_TYPE) {
769                 werr = libnet_DomainJoin(mem_ctx, r);
770                 if (!W_ERROR_IS_OK(werr)) {
771                         return werr;
772                 }
773         }
774
775         werr = do_JoinConfig(r);
776         if (!W_ERROR_IS_OK(werr)) {
777                 return werr;
778         }
779
780         return werr;
781 }
782
783 WERROR libnet_Unjoin(TALLOC_CTX *mem_ctx,
784                      struct libnet_UnjoinCtx *r)
785 {
786         WERROR werr;
787         NTSTATUS status;
788
789         if (r->in.modify_config && !lp_include_registry_globals()) {
790                 return WERR_NOT_SUPPORTED;
791         }
792
793         if (r->in.unjoin_flags & WKSSVC_JOIN_FLAGS_JOIN_TYPE) {
794
795                 status = libnet_join_unjoindomain_rpc(mem_ctx, r);
796                 if (!NT_STATUS_IS_OK(status)) {
797                         if (NT_STATUS_EQUAL(status, NT_STATUS_NO_SUCH_USER)) {
798                                 return WERR_SETUP_NOT_JOINED;
799                         }
800                         return ntstatus_to_werror(status);
801                 }
802
803                 libnet_join_unjoindomain_remove_secrets(mem_ctx, r);
804         }
805
806         werr = do_UnjoinConfig(r);
807         if (!W_ERROR_IS_OK(werr)) {
808                 return werr;
809         }
810
811         return werr;
812 }