Let dsgetdcname() return a struct netr_DsRGetDCNameInfo.
[jra/samba/.git] / source3 / libnet / libnet_join.c
1 /*
2  *  Unix SMB/CIFS implementation.
3  *  libnet Join Support
4  *  Copyright (C) Gerald (Jerry) Carter 2006
5  *  Copyright (C) Guenther Deschner 2007-2008
6  *
7  *  This program is free software; you can redistribute it and/or modify
8  *  it under the terms of the GNU General Public License as published by
9  *  the Free Software Foundation; either version 3 of the License, or
10  *  (at your option) any later version.
11  *
12  *  This program is distributed in the hope that it will be useful,
13  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  *  GNU General Public License for more details.
16  *
17  *  You should have received a copy of the GNU General Public License
18  *  along with this program; if not, see <http://www.gnu.org/licenses/>.
19  */
20
21 #include "includes.h"
22 #include "libnet/libnet.h"
23
24 /****************************************************************
25 ****************************************************************/
26
27 #define LIBNET_JOIN_DUMP_CTX(ctx, r, f) \
28         do { \
29                 char *str = NULL; \
30                 str = NDR_PRINT_FUNCTION_STRING(ctx, libnet_JoinCtx, f, r); \
31                 DEBUG(1,("libnet_Join:\n%s", str)); \
32                 talloc_free(str); \
33         } while (0)
34
35 #define LIBNET_JOIN_IN_DUMP_CTX(ctx, r) \
36         LIBNET_JOIN_DUMP_CTX(ctx, r, NDR_IN | NDR_SET_VALUES)
37 #define LIBNET_JOIN_OUT_DUMP_CTX(ctx, r) \
38         LIBNET_JOIN_DUMP_CTX(ctx, r, NDR_OUT)
39
40 #define LIBNET_UNJOIN_DUMP_CTX(ctx, r, f) \
41         do { \
42                 char *str = NULL; \
43                 str = NDR_PRINT_FUNCTION_STRING(ctx, libnet_UnjoinCtx, f, r); \
44                 DEBUG(1,("libnet_Unjoin:\n%s", str)); \
45                 talloc_free(str); \
46         } while (0)
47
48 #define LIBNET_UNJOIN_IN_DUMP_CTX(ctx, r) \
49         LIBNET_UNJOIN_DUMP_CTX(ctx, r, NDR_IN | NDR_SET_VALUES)
50 #define LIBNET_UNJOIN_OUT_DUMP_CTX(ctx, r) \
51         LIBNET_UNJOIN_DUMP_CTX(ctx, r, NDR_OUT)
52
53 #define W_ERROR_NOT_OK_GOTO_DONE(x) do { \
54         if (!W_ERROR_IS_OK(x)) {\
55                 goto done;\
56         }\
57 } while (0)
58
59 /****************************************************************
60 ****************************************************************/
61
62 static void libnet_join_set_error_string(TALLOC_CTX *mem_ctx,
63                                          struct libnet_JoinCtx *r,
64                                          const char *format, ...)
65 {
66         va_list args;
67
68         if (r->out.error_string) {
69                 return;
70         }
71
72         va_start(args, format);
73         r->out.error_string = talloc_vasprintf(mem_ctx, format, args);
74         va_end(args);
75 }
76
77 /****************************************************************
78 ****************************************************************/
79
80 static void libnet_unjoin_set_error_string(TALLOC_CTX *mem_ctx,
81                                            struct libnet_UnjoinCtx *r,
82                                            const char *format, ...)
83 {
84         va_list args;
85
86         if (r->out.error_string) {
87                 return;
88         }
89
90         va_start(args, format);
91         r->out.error_string = talloc_vasprintf(mem_ctx, format, args);
92         va_end(args);
93 }
94
95 #ifdef WITH_ADS
96
97 /****************************************************************
98 ****************************************************************/
99
100 static ADS_STATUS libnet_connect_ads(const char *dns_domain_name,
101                                      const char *netbios_domain_name,
102                                      const char *dc_name,
103                                      const char *user_name,
104                                      const char *password,
105                                      ADS_STRUCT **ads)
106 {
107         ADS_STATUS status;
108         ADS_STRUCT *my_ads = NULL;
109
110         my_ads = ads_init(dns_domain_name,
111                           netbios_domain_name,
112                           dc_name);
113         if (!my_ads) {
114                 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
115         }
116
117         if (user_name) {
118                 SAFE_FREE(my_ads->auth.user_name);
119                 my_ads->auth.user_name = SMB_STRDUP(user_name);
120         }
121
122         if (password) {
123                 SAFE_FREE(my_ads->auth.password);
124                 my_ads->auth.password = SMB_STRDUP(password);
125         }
126
127         status = ads_connect(my_ads);
128         if (!ADS_ERR_OK(status)) {
129                 ads_destroy(&my_ads);
130                 return status;
131         }
132
133         *ads = my_ads;
134         return ADS_SUCCESS;
135 }
136
137 /****************************************************************
138 ****************************************************************/
139
140 static ADS_STATUS libnet_join_connect_ads(TALLOC_CTX *mem_ctx,
141                                           struct libnet_JoinCtx *r)
142 {
143         ADS_STATUS status;
144
145         status = libnet_connect_ads(r->in.domain_name,
146                                     r->in.domain_name,
147                                     r->in.dc_name,
148                                     r->in.admin_account,
149                                     r->in.admin_password,
150                                     &r->in.ads);
151         if (!ADS_ERR_OK(status)) {
152                 libnet_join_set_error_string(mem_ctx, r,
153                         "failed to connect to AD: %s",
154                         ads_errstr(status));
155                 return status;
156         }
157
158         if (!r->out.netbios_domain_name) {
159                 r->out.netbios_domain_name = talloc_strdup(mem_ctx,
160                                                            r->in.ads->server.workgroup);
161                 ADS_ERROR_HAVE_NO_MEMORY(r->out.netbios_domain_name);
162         }
163
164         if (!r->out.dns_domain_name) {
165                 r->out.dns_domain_name = talloc_strdup(mem_ctx,
166                                                        r->in.ads->config.realm);
167                 ADS_ERROR_HAVE_NO_MEMORY(r->out.dns_domain_name);
168         }
169
170         r->out.domain_is_ad = true;
171
172         return ADS_SUCCESS;
173 }
174
175 /****************************************************************
176 ****************************************************************/
177
178 static ADS_STATUS libnet_unjoin_connect_ads(TALLOC_CTX *mem_ctx,
179                                             struct libnet_UnjoinCtx *r)
180 {
181         ADS_STATUS status;
182
183         status = libnet_connect_ads(r->in.domain_name,
184                                     r->in.domain_name,
185                                     r->in.dc_name,
186                                     r->in.admin_account,
187                                     r->in.admin_password,
188                                     &r->in.ads);
189         if (!ADS_ERR_OK(status)) {
190                 libnet_unjoin_set_error_string(mem_ctx, r,
191                         "failed to connect to AD: %s",
192                         ads_errstr(status));
193         }
194
195         return status;
196 }
197
198 /****************************************************************
199 ****************************************************************/
200
201 static ADS_STATUS libnet_join_precreate_machine_acct(TALLOC_CTX *mem_ctx,
202                                                      struct libnet_JoinCtx *r)
203 {
204         ADS_STATUS status;
205         LDAPMessage *res = NULL;
206         const char *attrs[] = { "dn", NULL };
207
208         status = ads_search_dn(r->in.ads, &res, r->in.account_ou, attrs);
209         if (!ADS_ERR_OK(status)) {
210                 return status;
211         }
212
213         if (ads_count_replies(r->in.ads, res) != 1) {
214                 ads_msgfree(r->in.ads, res);
215                 return ADS_ERROR_LDAP(LDAP_NO_SUCH_OBJECT);
216         }
217
218         status = ads_create_machine_acct(r->in.ads,
219                                          r->in.machine_name,
220                                          r->in.account_ou);
221         ads_msgfree(r->in.ads, res);
222
223         if ((status.error_type == ENUM_ADS_ERROR_LDAP) &&
224             (status.err.rc == LDAP_ALREADY_EXISTS)) {
225                 status = ADS_SUCCESS;
226         }
227
228         return status;
229 }
230
231 /****************************************************************
232 ****************************************************************/
233
234 static ADS_STATUS libnet_unjoin_remove_machine_acct(TALLOC_CTX *mem_ctx,
235                                                     struct libnet_UnjoinCtx *r)
236 {
237         ADS_STATUS status;
238
239         if (!r->in.ads) {
240                 status = libnet_unjoin_connect_ads(mem_ctx, r);
241                 if (!ADS_ERR_OK(status)) {
242                         return status;
243                 }
244         }
245
246         status = ads_leave_realm(r->in.ads, r->in.machine_name);
247         if (!ADS_ERR_OK(status)) {
248                 libnet_unjoin_set_error_string(mem_ctx, r,
249                         "failed to leave realm: %s",
250                         ads_errstr(status));
251                 return status;
252         }
253
254         return ADS_SUCCESS;
255 }
256
257 /****************************************************************
258 ****************************************************************/
259
260 static ADS_STATUS libnet_join_find_machine_acct(TALLOC_CTX *mem_ctx,
261                                                 struct libnet_JoinCtx *r)
262 {
263         ADS_STATUS status;
264         LDAPMessage *res = NULL;
265         char *dn = NULL;
266
267         if (!r->in.machine_name) {
268                 return ADS_ERROR(LDAP_NO_MEMORY);
269         }
270
271         status = ads_find_machine_acct(r->in.ads,
272                                        &res,
273                                        r->in.machine_name);
274         if (!ADS_ERR_OK(status)) {
275                 return status;
276         }
277
278         if (ads_count_replies(r->in.ads, res) != 1) {
279                 status = ADS_ERROR_LDAP(LDAP_NO_MEMORY);
280                 goto done;
281         }
282
283         dn = ads_get_dn(r->in.ads, res);
284         if (!dn) {
285                 status = ADS_ERROR_LDAP(LDAP_NO_MEMORY);
286                 goto done;
287         }
288
289         r->out.dn = talloc_strdup(mem_ctx, dn);
290         if (!r->out.dn) {
291                 status = ADS_ERROR_LDAP(LDAP_NO_MEMORY);
292                 goto done;
293         }
294
295  done:
296         ads_msgfree(r->in.ads, res);
297         ads_memfree(r->in.ads, dn);
298
299         return status;
300 }
301
302 /****************************************************************
303 ****************************************************************/
304
305 static ADS_STATUS libnet_join_set_machine_spn(TALLOC_CTX *mem_ctx,
306                                               struct libnet_JoinCtx *r)
307 {
308         ADS_STATUS status;
309         ADS_MODLIST mods;
310         fstring my_fqdn;
311         const char *spn_array[3] = {NULL, NULL, NULL};
312         char *spn = NULL;
313
314         status = libnet_join_find_machine_acct(mem_ctx, r);
315         if (!ADS_ERR_OK(status)) {
316                 return status;
317         }
318
319         spn = talloc_asprintf(mem_ctx, "HOST/%s", r->in.machine_name);
320         if (!spn) {
321                 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
322         }
323         strupper_m(spn);
324         spn_array[0] = spn;
325
326         if (name_to_fqdn(my_fqdn, r->in.machine_name) &&
327             !strequal(my_fqdn, r->in.machine_name)) {
328
329                 strlower_m(my_fqdn);
330                 spn = talloc_asprintf(mem_ctx, "HOST/%s", my_fqdn);
331                 if (!spn) {
332                         return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
333                 }
334                 spn_array[1] = spn;
335         }
336
337         mods = ads_init_mods(mem_ctx);
338         if (!mods) {
339                 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
340         }
341
342         status = ads_mod_str(mem_ctx, &mods, "dNSHostName", my_fqdn);
343         if (!ADS_ERR_OK(status)) {
344                 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
345         }
346
347         status = ads_mod_strlist(mem_ctx, &mods, "servicePrincipalName",
348                                  spn_array);
349         if (!ADS_ERR_OK(status)) {
350                 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
351         }
352
353         return ads_gen_mod(r->in.ads, r->out.dn, mods);
354 }
355
356 /****************************************************************
357 ****************************************************************/
358
359 static ADS_STATUS libnet_join_set_machine_upn(TALLOC_CTX *mem_ctx,
360                                               struct libnet_JoinCtx *r)
361 {
362         ADS_STATUS status;
363         ADS_MODLIST mods;
364
365         if (!r->in.create_upn) {
366                 return ADS_SUCCESS;
367         }
368
369         status = libnet_join_find_machine_acct(mem_ctx, r);
370         if (!ADS_ERR_OK(status)) {
371                 return status;
372         }
373
374         if (!r->in.upn) {
375                 r->in.upn = talloc_asprintf(mem_ctx,
376                                             "host/%s@%s",
377                                             r->in.machine_name,
378                                             r->out.dns_domain_name);
379                 if (!r->in.upn) {
380                         return ADS_ERROR(LDAP_NO_MEMORY);
381                 }
382         }
383
384         mods = ads_init_mods(mem_ctx);
385         if (!mods) {
386                 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
387         }
388
389         status = ads_mod_str(mem_ctx, &mods, "userPrincipalName", r->in.upn);
390         if (!ADS_ERR_OK(status)) {
391                 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
392         }
393
394         return ads_gen_mod(r->in.ads, r->out.dn, mods);
395 }
396
397
398 /****************************************************************
399 ****************************************************************/
400
401 static ADS_STATUS libnet_join_set_os_attributes(TALLOC_CTX *mem_ctx,
402                                                 struct libnet_JoinCtx *r)
403 {
404         ADS_STATUS status;
405         ADS_MODLIST mods;
406         char *os_sp = NULL;
407
408         if (!r->in.os_name || !r->in.os_version ) {
409                 return ADS_SUCCESS;
410         }
411
412         status = libnet_join_find_machine_acct(mem_ctx, r);
413         if (!ADS_ERR_OK(status)) {
414                 return status;
415         }
416
417         mods = ads_init_mods(mem_ctx);
418         if (!mods) {
419                 return ADS_ERROR(LDAP_NO_MEMORY);
420         }
421
422         os_sp = talloc_asprintf(mem_ctx, "Samba %s", SAMBA_VERSION_STRING);
423         if (!os_sp) {
424                 return ADS_ERROR(LDAP_NO_MEMORY);
425         }
426
427         status = ads_mod_str(mem_ctx, &mods, "operatingSystem",
428                              r->in.os_name);
429         if (!ADS_ERR_OK(status)) {
430                 return status;
431         }
432
433         status = ads_mod_str(mem_ctx, &mods, "operatingSystemVersion",
434                              r->in.os_version);
435         if (!ADS_ERR_OK(status)) {
436                 return status;
437         }
438
439         status = ads_mod_str(mem_ctx, &mods, "operatingSystemServicePack",
440                              os_sp);
441         if (!ADS_ERR_OK(status)) {
442                 return status;
443         }
444
445         return ads_gen_mod(r->in.ads, r->out.dn, mods);
446 }
447
448 /****************************************************************
449 ****************************************************************/
450
451 static bool libnet_join_create_keytab(TALLOC_CTX *mem_ctx,
452                                       struct libnet_JoinCtx *r)
453 {
454         if (!lp_use_kerberos_keytab()) {
455                 return true;
456         }
457
458         if (!ads_keytab_create_default(r->in.ads)) {
459                 return false;
460         }
461
462         return true;
463 }
464
465 /****************************************************************
466 ****************************************************************/
467
468 static bool libnet_join_derive_salting_principal(TALLOC_CTX *mem_ctx,
469                                                  struct libnet_JoinCtx *r)
470 {
471         uint32_t domain_func;
472         ADS_STATUS status;
473         const char *salt = NULL;
474         char *std_salt = NULL;
475
476         status = ads_domain_func_level(r->in.ads, &domain_func);
477         if (!ADS_ERR_OK(status)) {
478                 libnet_join_set_error_string(mem_ctx, r,
479                         "failed to determine domain functional level: %s",
480                         ads_errstr(status));
481                 return false;
482         }
483
484         std_salt = kerberos_standard_des_salt();
485         if (!std_salt) {
486                 libnet_join_set_error_string(mem_ctx, r,
487                         "failed to obtain standard DES salt");
488                 return false;
489         }
490
491         salt = talloc_strdup(mem_ctx, std_salt);
492         if (!salt) {
493                 return false;
494         }
495
496         SAFE_FREE(std_salt);
497
498         if (domain_func == DS_DOMAIN_FUNCTION_2000) {
499                 char *upn;
500
501                 upn = ads_get_upn(r->in.ads, mem_ctx,
502                                   r->in.machine_name);
503                 if (upn) {
504                         salt = talloc_strdup(mem_ctx, upn);
505                         if (!salt) {
506                                 return false;
507                         }
508                 }
509         }
510
511         return kerberos_secrets_store_des_salt(salt);
512 }
513
514 /****************************************************************
515 ****************************************************************/
516
517 static ADS_STATUS libnet_join_post_processing_ads(TALLOC_CTX *mem_ctx,
518                                                   struct libnet_JoinCtx *r)
519 {
520         ADS_STATUS status;
521
522         if (!r->in.ads) {
523                 status = libnet_join_connect_ads(mem_ctx, r);
524                 if (!ADS_ERR_OK(status)) {
525                         return status;
526                 }
527         }
528
529         status = libnet_join_set_machine_spn(mem_ctx, r);
530         if (!ADS_ERR_OK(status)) {
531                 libnet_join_set_error_string(mem_ctx, r,
532                         "failed to set machine spn: %s",
533                         ads_errstr(status));
534                 return status;
535         }
536
537         status = libnet_join_set_os_attributes(mem_ctx, r);
538         if (!ADS_ERR_OK(status)) {
539                 libnet_join_set_error_string(mem_ctx, r,
540                         "failed to set machine os attributes: %s",
541                         ads_errstr(status));
542                 return status;
543         }
544
545         status = libnet_join_set_machine_upn(mem_ctx, r);
546         if (!ADS_ERR_OK(status)) {
547                 libnet_join_set_error_string(mem_ctx, r,
548                         "failed to set machine upn: %s",
549                         ads_errstr(status));
550                 return status;
551         }
552
553         if (!libnet_join_derive_salting_principal(mem_ctx, r)) {
554                 return ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL);
555         }
556
557         if (!libnet_join_create_keytab(mem_ctx, r)) {
558                 libnet_join_set_error_string(mem_ctx, r,
559                         "failed to create kerberos keytab");
560                 return ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL);
561         }
562
563         return ADS_SUCCESS;
564 }
565 #endif /* WITH_ADS */
566
567 /****************************************************************
568 ****************************************************************/
569
570 static bool libnet_join_joindomain_store_secrets(TALLOC_CTX *mem_ctx,
571                                                  struct libnet_JoinCtx *r)
572 {
573         if (!secrets_store_domain_sid(r->out.netbios_domain_name,
574                                       r->out.domain_sid))
575         {
576                 return false;
577         }
578
579         if (!secrets_store_machine_password(r->in.machine_password,
580                                             r->out.netbios_domain_name,
581                                             SEC_CHAN_WKSTA))
582         {
583                 return false;
584         }
585
586         return true;
587 }
588
589 /****************************************************************
590 ****************************************************************/
591
592 static NTSTATUS libnet_join_joindomain_rpc(TALLOC_CTX *mem_ctx,
593                                            struct libnet_JoinCtx *r)
594 {
595         struct cli_state *cli = NULL;
596         struct rpc_pipe_client *pipe_hnd = NULL;
597         POLICY_HND sam_pol, domain_pol, user_pol, lsa_pol;
598         NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
599         char *acct_name;
600         struct lsa_String lsa_acct_name;
601         uint32 user_rid;
602         uint32 acb_info = ACB_WSTRUST;
603         uchar pwbuf[532];
604         struct MD5Context md5ctx;
605         uchar md5buffer[16];
606         DATA_BLOB digested_session_key;
607         uchar md4_trust_password[16];
608         union lsa_PolicyInformation *info = NULL;
609         struct samr_Ids user_rids;
610         struct samr_Ids name_types;
611         union samr_UserInfo user_info;
612
613         if (!r->in.machine_password) {
614                 r->in.machine_password = talloc_strdup(mem_ctx, generate_random_str(DEFAULT_TRUST_ACCOUNT_PASSWORD_LENGTH));
615                 NT_STATUS_HAVE_NO_MEMORY(r->in.machine_password);
616         }
617
618         status = cli_full_connection(&cli, NULL,
619                                      r->in.dc_name,
620                                      NULL, 0,
621                                      "IPC$", "IPC",
622                                      r->in.admin_account,
623                                      NULL,
624                                      r->in.admin_password,
625                                      0,
626                                      Undefined, NULL);
627
628         if (!NT_STATUS_IS_OK(status)) {
629                 goto done;
630         }
631
632         pipe_hnd = cli_rpc_pipe_open_noauth(cli, PI_LSARPC, &status);
633         if (!pipe_hnd) {
634                 goto done;
635         }
636
637         status = rpccli_lsa_open_policy(pipe_hnd, mem_ctx, True,
638                                         SEC_RIGHTS_MAXIMUM_ALLOWED, &lsa_pol);
639         if (!NT_STATUS_IS_OK(status)) {
640                 goto done;
641         }
642
643         status = rpccli_lsa_QueryInfoPolicy2(pipe_hnd, mem_ctx,
644                                              &lsa_pol,
645                                              LSA_POLICY_INFO_DNS,
646                                              &info);
647         if (NT_STATUS_IS_OK(status)) {
648                 r->out.domain_is_ad = true;
649                 r->out.netbios_domain_name = info->dns.name.string;
650                 r->out.dns_domain_name = info->dns.dns_domain.string;
651                 r->out.domain_sid = info->dns.sid;
652         }
653
654         if (!NT_STATUS_IS_OK(status)) {
655                 status = rpccli_lsa_QueryInfoPolicy(pipe_hnd, mem_ctx,
656                                                     &lsa_pol,
657                                                     LSA_POLICY_INFO_ACCOUNT_DOMAIN,
658                                                     &info);
659                 if (!NT_STATUS_IS_OK(status)) {
660                         goto done;
661                 }
662
663                 r->out.netbios_domain_name = info->account_domain.name.string;
664                 r->out.domain_sid = info->account_domain.sid;
665         }
666
667         rpccli_lsa_Close(pipe_hnd, mem_ctx, &lsa_pol);
668         cli_rpc_pipe_close(pipe_hnd);
669
670         pipe_hnd = cli_rpc_pipe_open_noauth(cli, PI_SAMR, &status);
671         if (!pipe_hnd) {
672                 goto done;
673         }
674
675         status = rpccli_samr_Connect2(pipe_hnd, mem_ctx,
676                                       pipe_hnd->cli->desthost,
677                                       SEC_RIGHTS_MAXIMUM_ALLOWED,
678                                       &sam_pol);
679         if (!NT_STATUS_IS_OK(status)) {
680                 goto done;
681         }
682
683         status = rpccli_samr_OpenDomain(pipe_hnd, mem_ctx,
684                                         &sam_pol,
685                                         SEC_RIGHTS_MAXIMUM_ALLOWED,
686                                         r->out.domain_sid,
687                                         &domain_pol);
688         if (!NT_STATUS_IS_OK(status)) {
689                 goto done;
690         }
691
692         acct_name = talloc_asprintf(mem_ctx, "%s$", r->in.machine_name);
693         strlower_m(acct_name);
694
695         init_lsa_String(&lsa_acct_name, acct_name);
696
697         if (r->in.join_flags & WKSSVC_JOIN_FLAGS_ACCOUNT_CREATE) {
698                 uint32_t acct_flags =
699                         SEC_GENERIC_READ | SEC_GENERIC_WRITE | SEC_GENERIC_EXECUTE |
700                         SEC_STD_WRITE_DAC | SEC_STD_DELETE |
701                         SAMR_USER_ACCESS_SET_PASSWORD |
702                         SAMR_USER_ACCESS_GET_ATTRIBUTES |
703                         SAMR_USER_ACCESS_SET_ATTRIBUTES;
704                 uint32_t access_granted = 0;
705
706                 status = rpccli_samr_CreateUser2(pipe_hnd, mem_ctx,
707                                                  &domain_pol,
708                                                  &lsa_acct_name,
709                                                  ACB_WSTRUST,
710                                                  acct_flags,
711                                                  &user_pol,
712                                                  &access_granted,
713                                                  &user_rid);
714                 if (NT_STATUS_EQUAL(status, NT_STATUS_USER_EXISTS)) {
715                         if (!(r->in.join_flags &
716                               WKSSVC_JOIN_FLAGS_DOMAIN_JOIN_IF_JOINED)) {
717                                 goto done;
718                         }
719                 }
720
721                 if (NT_STATUS_IS_OK(status)) {
722                         rpccli_samr_Close(pipe_hnd, mem_ctx, &user_pol);
723                 }
724         }
725
726         status = rpccli_samr_LookupNames(pipe_hnd, mem_ctx,
727                                          &domain_pol,
728                                          1,
729                                          &lsa_acct_name,
730                                          &user_rids,
731                                          &name_types);
732         if (!NT_STATUS_IS_OK(status)) {
733                 goto done;
734         }
735
736         if (name_types.ids[0] != SID_NAME_USER) {
737                 status = NT_STATUS_INVALID_WORKSTATION;
738                 goto done;
739         }
740
741         user_rid = user_rids.ids[0];
742
743         status = rpccli_samr_OpenUser(pipe_hnd, mem_ctx,
744                                       &domain_pol,
745                                       SEC_RIGHTS_MAXIMUM_ALLOWED,
746                                       user_rid,
747                                       &user_pol);
748         if (!NT_STATUS_IS_OK(status)) {
749                 goto done;
750         }
751
752         E_md4hash(r->in.machine_password, md4_trust_password);
753         encode_pw_buffer(pwbuf, r->in.machine_password, STR_UNICODE);
754
755         generate_random_buffer((uint8*)md5buffer, sizeof(md5buffer));
756         digested_session_key = data_blob_talloc(mem_ctx, 0, 16);
757
758         MD5Init(&md5ctx);
759         MD5Update(&md5ctx, md5buffer, sizeof(md5buffer));
760         MD5Update(&md5ctx, cli->user_session_key.data,
761                   cli->user_session_key.length);
762         MD5Final(digested_session_key.data, &md5ctx);
763
764         SamOEMhashBlob(pwbuf, sizeof(pwbuf), &digested_session_key);
765         memcpy(&pwbuf[516], md5buffer, sizeof(md5buffer));
766
767         acb_info |= ACB_PWNOEXP;
768         if (r->out.domain_is_ad) {
769 #if !defined(ENCTYPE_ARCFOUR_HMAC)
770                 acb_info |= ACB_USE_DES_KEY_ONLY;
771 #endif
772                 ;;
773         }
774
775         ZERO_STRUCT(user_info.info25);
776
777         user_info.info25.info.fields_present = ACCT_NT_PWD_SET |
778                                                ACCT_LM_PWD_SET |
779                                                SAMR_FIELD_ACCT_FLAGS;
780         user_info.info25.info.acct_flags = acb_info;
781         memcpy(&user_info.info25.password.data, pwbuf, sizeof(pwbuf));
782
783         status = rpccli_samr_SetUserInfo(pipe_hnd, mem_ctx,
784                                          &user_pol,
785                                          25,
786                                          &user_info);
787         if (!NT_STATUS_IS_OK(status)) {
788                 goto done;
789         }
790
791         rpccli_samr_Close(pipe_hnd, mem_ctx, &user_pol);
792         cli_rpc_pipe_close(pipe_hnd);
793
794         status = NT_STATUS_OK;
795  done:
796         if (cli) {
797                 cli_shutdown(cli);
798         }
799
800         return status;
801 }
802
803 /****************************************************************
804 ****************************************************************/
805
806 NTSTATUS libnet_join_ok(const char *netbios_domain_name,
807                         const char *machine_name,
808                         const char *dc_name)
809 {
810         uint32_t neg_flags = NETLOGON_NEG_AUTH2_FLAGS |
811                              NETLOGON_NEG_SCHANNEL;
812         /* FIXME: NETLOGON_NEG_SELECT_AUTH2_FLAGS */
813         struct cli_state *cli = NULL;
814         struct rpc_pipe_client *pipe_hnd = NULL;
815         struct rpc_pipe_client *netlogon_pipe = NULL;
816         NTSTATUS status;
817         char *machine_password = NULL;
818         char *machine_account = NULL;
819
820         if (!dc_name) {
821                 return NT_STATUS_INVALID_PARAMETER;
822         }
823
824         if (!secrets_init()) {
825                 return NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
826         }
827
828         machine_password = secrets_fetch_machine_password(netbios_domain_name,
829                                                           NULL, NULL);
830         if (!machine_password) {
831                 return NT_STATUS_NO_TRUST_LSA_SECRET;
832         }
833
834         asprintf(&machine_account, "%s$", machine_name);
835         if (!machine_account) {
836                 SAFE_FREE(machine_password);
837                 return NT_STATUS_NO_MEMORY;
838         }
839
840         status = cli_full_connection(&cli, NULL,
841                                      dc_name,
842                                      NULL, 0,
843                                      "IPC$", "IPC",
844                                      machine_account,
845                                      NULL,
846                                      machine_password,
847                                      0,
848                                      Undefined, NULL);
849         free(machine_account);
850         free(machine_password);
851
852         if (!NT_STATUS_IS_OK(status)) {
853                 status = cli_full_connection(&cli, NULL,
854                                              dc_name,
855                                              NULL, 0,
856                                              "IPC$", "IPC",
857                                              "",
858                                              NULL,
859                                              "",
860                                              0,
861                                              Undefined, NULL);
862         }
863
864         if (!NT_STATUS_IS_OK(status)) {
865                 return status;
866         }
867
868         netlogon_pipe = get_schannel_session_key(cli,
869                                                  netbios_domain_name,
870                                                  &neg_flags, &status);
871         if (!netlogon_pipe) {
872                 if (NT_STATUS_EQUAL(status, NT_STATUS_INVALID_NETWORK_RESPONSE)) {
873                         cli_shutdown(cli);
874                         return NT_STATUS_OK;
875                 }
876
877                 DEBUG(0,("libnet_join_ok: failed to get schannel session "
878                         "key from server %s for domain %s. Error was %s\n",
879                 cli->desthost, netbios_domain_name, nt_errstr(status)));
880                 cli_shutdown(cli);
881                 return status;
882         }
883
884         if (!lp_client_schannel()) {
885                 cli_shutdown(cli);
886                 return NT_STATUS_OK;
887         }
888
889         pipe_hnd = cli_rpc_pipe_open_schannel_with_key(cli, PI_NETLOGON,
890                                                        PIPE_AUTH_LEVEL_PRIVACY,
891                                                        netbios_domain_name,
892                                                        netlogon_pipe->dc,
893                                                        &status);
894
895         cli_shutdown(cli);
896
897         if (!pipe_hnd) {
898                 DEBUG(0,("libnet_join_ok: failed to open schannel session "
899                         "on netlogon pipe to server %s for domain %s. "
900                         "Error was %s\n",
901                         cli->desthost, netbios_domain_name, nt_errstr(status)));
902                 return status;
903         }
904
905         return NT_STATUS_OK;
906 }
907
908 /****************************************************************
909 ****************************************************************/
910
911 static WERROR libnet_join_post_verify(TALLOC_CTX *mem_ctx,
912                                       struct libnet_JoinCtx *r)
913 {
914         NTSTATUS status;
915
916         status = libnet_join_ok(r->out.netbios_domain_name,
917                                 r->in.machine_name,
918                                 r->in.dc_name);
919         if (!NT_STATUS_IS_OK(status)) {
920                 libnet_join_set_error_string(mem_ctx, r,
921                         "failed to verify domain membership after joining: %s",
922                         get_friendly_nt_error_msg(status));
923                 return WERR_SETUP_NOT_JOINED;
924         }
925
926         return WERR_OK;
927 }
928
929 /****************************************************************
930 ****************************************************************/
931
932 static bool libnet_join_unjoindomain_remove_secrets(TALLOC_CTX *mem_ctx,
933                                                     struct libnet_UnjoinCtx *r)
934 {
935         if (!secrets_delete_machine_password_ex(lp_workgroup())) {
936                 return false;
937         }
938
939         if (!secrets_delete_domain_sid(lp_workgroup())) {
940                 return false;
941         }
942
943         return true;
944 }
945
946 /****************************************************************
947 ****************************************************************/
948
949 static NTSTATUS libnet_join_unjoindomain_rpc(TALLOC_CTX *mem_ctx,
950                                              struct libnet_UnjoinCtx *r)
951 {
952         struct cli_state *cli = NULL;
953         struct rpc_pipe_client *pipe_hnd = NULL;
954         POLICY_HND sam_pol, domain_pol, user_pol;
955         NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
956         char *acct_name;
957         uint32 user_rid;
958         struct lsa_String lsa_acct_name;
959         struct samr_Ids user_rids;
960         struct samr_Ids name_types;
961         union samr_UserInfo *info = NULL;
962
963         status = cli_full_connection(&cli, NULL,
964                                      r->in.dc_name,
965                                      NULL, 0,
966                                      "IPC$", "IPC",
967                                      r->in.admin_account,
968                                      NULL,
969                                      r->in.admin_password,
970                                      0, Undefined, NULL);
971
972         if (!NT_STATUS_IS_OK(status)) {
973                 goto done;
974         }
975
976         pipe_hnd = cli_rpc_pipe_open_noauth(cli, PI_SAMR, &status);
977         if (!pipe_hnd) {
978                 goto done;
979         }
980
981         status = rpccli_samr_Connect2(pipe_hnd, mem_ctx,
982                                       pipe_hnd->cli->desthost,
983                                       SEC_RIGHTS_MAXIMUM_ALLOWED,
984                                       &sam_pol);
985         if (!NT_STATUS_IS_OK(status)) {
986                 goto done;
987         }
988
989         status = rpccli_samr_OpenDomain(pipe_hnd, mem_ctx,
990                                         &sam_pol,
991                                         SEC_RIGHTS_MAXIMUM_ALLOWED,
992                                         r->in.domain_sid,
993                                         &domain_pol);
994         if (!NT_STATUS_IS_OK(status)) {
995                 goto done;
996         }
997
998         acct_name = talloc_asprintf(mem_ctx, "%s$", r->in.machine_name);
999         strlower_m(acct_name);
1000
1001         init_lsa_String(&lsa_acct_name, acct_name);
1002
1003         status = rpccli_samr_LookupNames(pipe_hnd, mem_ctx,
1004                                          &domain_pol,
1005                                          1,
1006                                          &lsa_acct_name,
1007                                          &user_rids,
1008                                          &name_types);
1009
1010         if (!NT_STATUS_IS_OK(status)) {
1011                 goto done;
1012         }
1013
1014         if (name_types.ids[0] != SID_NAME_USER) {
1015                 status = NT_STATUS_INVALID_WORKSTATION;
1016                 goto done;
1017         }
1018
1019         user_rid = user_rids.ids[0];
1020
1021         status = rpccli_samr_OpenUser(pipe_hnd, mem_ctx,
1022                                       &domain_pol,
1023                                       SEC_RIGHTS_MAXIMUM_ALLOWED,
1024                                       user_rid,
1025                                       &user_pol);
1026         if (!NT_STATUS_IS_OK(status)) {
1027                 goto done;
1028         }
1029
1030         status = rpccli_samr_QueryUserInfo(pipe_hnd, mem_ctx,
1031                                            &user_pol,
1032                                            16,
1033                                            &info);
1034         if (!NT_STATUS_IS_OK(status)) {
1035                 rpccli_samr_Close(pipe_hnd, mem_ctx, &user_pol);
1036                 goto done;
1037         }
1038
1039         info->info16.acct_flags |= ACB_DISABLED;
1040
1041         status = rpccli_samr_SetUserInfo(pipe_hnd, mem_ctx,
1042                                          &user_pol,
1043                                          16,
1044                                          info);
1045
1046         rpccli_samr_Close(pipe_hnd, mem_ctx, &user_pol);
1047
1048 done:
1049         if (pipe_hnd) {
1050                 rpccli_samr_Close(pipe_hnd, mem_ctx, &domain_pol);
1051                 rpccli_samr_Close(pipe_hnd, mem_ctx, &sam_pol);
1052                 cli_rpc_pipe_close(pipe_hnd);
1053         }
1054
1055         if (cli) {
1056                 cli_shutdown(cli);
1057         }
1058
1059         return status;
1060 }
1061
1062 /****************************************************************
1063 ****************************************************************/
1064
1065 static WERROR do_join_modify_vals_config(struct libnet_JoinCtx *r)
1066 {
1067         WERROR werr;
1068         struct libnet_conf_ctx *ctx;
1069
1070         werr = libnet_conf_open(r, &ctx);
1071         if (!W_ERROR_IS_OK(werr)) {
1072                 goto done;
1073         }
1074
1075         if (!(r->in.join_flags & WKSSVC_JOIN_FLAGS_JOIN_TYPE)) {
1076
1077                 werr = libnet_conf_set_global_parameter(ctx, "security", "user");
1078                 W_ERROR_NOT_OK_GOTO_DONE(werr);
1079
1080                 werr = libnet_conf_set_global_parameter(ctx, "workgroup",
1081                                                         r->in.domain_name);
1082                 goto done;
1083         }
1084
1085         werr = libnet_conf_set_global_parameter(ctx, "security", "domain");
1086         W_ERROR_NOT_OK_GOTO_DONE(werr);
1087
1088         werr = libnet_conf_set_global_parameter(ctx, "workgroup",
1089                                                 r->out.netbios_domain_name);
1090         W_ERROR_NOT_OK_GOTO_DONE(werr);
1091
1092         if (r->out.domain_is_ad) {
1093                 werr = libnet_conf_set_global_parameter(ctx, "security", "ads");
1094                 W_ERROR_NOT_OK_GOTO_DONE(werr);
1095
1096                 werr = libnet_conf_set_global_parameter(ctx, "realm",
1097                                                         r->out.dns_domain_name);
1098                 W_ERROR_NOT_OK_GOTO_DONE(werr);
1099         }
1100
1101  done:
1102         libnet_conf_close(ctx);
1103         return werr;
1104 }
1105
1106 /****************************************************************
1107 ****************************************************************/
1108
1109 static WERROR do_unjoin_modify_vals_config(struct libnet_UnjoinCtx *r)
1110 {
1111         WERROR werr = WERR_OK;
1112         struct libnet_conf_ctx *ctx;
1113
1114         werr = libnet_conf_open(r, &ctx);
1115         if (!W_ERROR_IS_OK(werr)) {
1116                 goto done;
1117         }
1118
1119         if (r->in.unjoin_flags & WKSSVC_JOIN_FLAGS_JOIN_TYPE) {
1120
1121                 werr = libnet_conf_set_global_parameter(ctx, "security", "user");
1122                 W_ERROR_NOT_OK_GOTO_DONE(werr);
1123                 libnet_conf_delete_global_parameter(ctx, "realm");
1124         }
1125
1126  done:
1127         libnet_conf_close(ctx);
1128         return werr;
1129 }
1130
1131 /****************************************************************
1132 ****************************************************************/
1133
1134 static WERROR do_JoinConfig(struct libnet_JoinCtx *r)
1135 {
1136         WERROR werr;
1137
1138         if (!W_ERROR_IS_OK(r->out.result)) {
1139                 return r->out.result;
1140         }
1141
1142         if (!r->in.modify_config) {
1143                 return WERR_OK;
1144         }
1145
1146         werr = do_join_modify_vals_config(r);
1147         if (!W_ERROR_IS_OK(werr)) {
1148                 return werr;
1149         }
1150
1151         r->out.modified_config = true;
1152         r->out.result = werr;
1153
1154         return werr;
1155 }
1156
1157 /****************************************************************
1158 ****************************************************************/
1159
1160 static WERROR libnet_unjoin_config(struct libnet_UnjoinCtx *r)
1161 {
1162         WERROR werr;
1163
1164         if (!W_ERROR_IS_OK(r->out.result)) {
1165                 return r->out.result;
1166         }
1167
1168         if (!r->in.modify_config) {
1169                 return WERR_OK;
1170         }
1171
1172         werr = do_unjoin_modify_vals_config(r);
1173         if (!W_ERROR_IS_OK(werr)) {
1174                 return werr;
1175         }
1176
1177         r->out.modified_config = true;
1178         r->out.result = werr;
1179
1180         return werr;
1181 }
1182
1183 /****************************************************************
1184 ****************************************************************/
1185
1186 static WERROR libnet_join_pre_processing(TALLOC_CTX *mem_ctx,
1187                                          struct libnet_JoinCtx *r)
1188 {
1189         if (!r->in.domain_name) {
1190                 libnet_join_set_error_string(mem_ctx, r,
1191                         "No domain name defined");
1192                 return WERR_INVALID_PARAM;
1193         }
1194
1195         if (r->in.modify_config && !lp_config_backend_is_registry()) {
1196                 return WERR_NOT_SUPPORTED;
1197         }
1198
1199         if (IS_DC) {
1200                 return WERR_SETUP_DOMAIN_CONTROLLER;
1201         }
1202
1203         if (!secrets_init()) {
1204                 libnet_join_set_error_string(mem_ctx, r,
1205                         "Unable to open secrets database");
1206                 return WERR_CAN_NOT_COMPLETE;
1207         }
1208
1209         return WERR_OK;
1210 }
1211
1212 /****************************************************************
1213 ****************************************************************/
1214
1215 static WERROR libnet_join_post_processing(TALLOC_CTX *mem_ctx,
1216                                           struct libnet_JoinCtx *r)
1217 {
1218         WERROR werr;
1219
1220         if (!W_ERROR_IS_OK(r->out.result)) {
1221                 return r->out.result;
1222         }
1223
1224         werr = do_JoinConfig(r);
1225         if (!W_ERROR_IS_OK(werr)) {
1226                 return werr;
1227         }
1228
1229         if (r->in.join_flags & WKSSVC_JOIN_FLAGS_JOIN_TYPE) {
1230                 saf_store(r->in.domain_name, r->in.dc_name);
1231         }
1232
1233         return WERR_OK;
1234 }
1235
1236 /****************************************************************
1237 ****************************************************************/
1238
1239 static int libnet_destroy_JoinCtx(struct libnet_JoinCtx *r)
1240 {
1241         if (r->in.ads) {
1242                 ads_destroy(&r->in.ads);
1243         }
1244
1245         return 0;
1246 }
1247
1248 /****************************************************************
1249 ****************************************************************/
1250
1251 static int libnet_destroy_UnjoinCtx(struct libnet_UnjoinCtx *r)
1252 {
1253         if (r->in.ads) {
1254                 ads_destroy(&r->in.ads);
1255         }
1256
1257         return 0;
1258 }
1259
1260 /****************************************************************
1261 ****************************************************************/
1262
1263 WERROR libnet_init_JoinCtx(TALLOC_CTX *mem_ctx,
1264                            struct libnet_JoinCtx **r)
1265 {
1266         struct libnet_JoinCtx *ctx;
1267
1268         ctx = talloc_zero(mem_ctx, struct libnet_JoinCtx);
1269         if (!ctx) {
1270                 return WERR_NOMEM;
1271         }
1272
1273         talloc_set_destructor(ctx, libnet_destroy_JoinCtx);
1274
1275         ctx->in.machine_name = talloc_strdup(mem_ctx, global_myname());
1276         W_ERROR_HAVE_NO_MEMORY(ctx->in.machine_name);
1277
1278         *r = ctx;
1279
1280         return WERR_OK;
1281 }
1282
1283 /****************************************************************
1284 ****************************************************************/
1285
1286 WERROR libnet_init_UnjoinCtx(TALLOC_CTX *mem_ctx,
1287                              struct libnet_UnjoinCtx **r)
1288 {
1289         struct libnet_UnjoinCtx *ctx;
1290
1291         ctx = talloc_zero(mem_ctx, struct libnet_UnjoinCtx);
1292         if (!ctx) {
1293                 return WERR_NOMEM;
1294         }
1295
1296         talloc_set_destructor(ctx, libnet_destroy_UnjoinCtx);
1297
1298         ctx->in.machine_name = talloc_strdup(mem_ctx, global_myname());
1299         W_ERROR_HAVE_NO_MEMORY(ctx->in.machine_name);
1300
1301         *r = ctx;
1302
1303         return WERR_OK;
1304 }
1305
1306 /****************************************************************
1307 ****************************************************************/
1308
1309 static WERROR libnet_DomainJoin(TALLOC_CTX *mem_ctx,
1310                                 struct libnet_JoinCtx *r)
1311 {
1312         NTSTATUS status;
1313 #ifdef WITH_ADS
1314         ADS_STATUS ads_status;
1315 #endif /* WITH_ADS */
1316
1317         if (!r->in.dc_name) {
1318                 struct netr_DsRGetDCNameInfo *info;
1319                 status = dsgetdcname(mem_ctx,
1320                                      r->in.domain_name,
1321                                      NULL,
1322                                      NULL,
1323                                      DS_DIRECTORY_SERVICE_REQUIRED |
1324                                      DS_WRITABLE_REQUIRED |
1325                                      DS_RETURN_DNS_NAME,
1326                                      &info);
1327                 if (!NT_STATUS_IS_OK(status)) {
1328                         libnet_join_set_error_string(mem_ctx, r,
1329                                 "failed to find DC for domain %s",
1330                                 r->in.domain_name,
1331                                 get_friendly_nt_error_msg(status));
1332                         return WERR_DOMAIN_CONTROLLER_NOT_FOUND;
1333                 }
1334
1335                 r->in.dc_name = talloc_strdup(mem_ctx,
1336                                               info->dc_unc);
1337                 W_ERROR_HAVE_NO_MEMORY(r->in.dc_name);
1338         }
1339
1340 #ifdef WITH_ADS
1341         if (r->in.account_ou) {
1342
1343                 ads_status = libnet_join_connect_ads(mem_ctx, r);
1344                 if (!ADS_ERR_OK(ads_status)) {
1345                         return WERR_DEFAULT_JOIN_REQUIRED;
1346                 }
1347
1348                 ads_status = libnet_join_precreate_machine_acct(mem_ctx, r);
1349                 if (!ADS_ERR_OK(ads_status)) {
1350                         libnet_join_set_error_string(mem_ctx, r,
1351                                 "failed to precreate account in ou %s: %s",
1352                                 r->in.account_ou,
1353                                 ads_errstr(ads_status));
1354                         return WERR_DEFAULT_JOIN_REQUIRED;
1355                 }
1356
1357                 r->in.join_flags &= ~WKSSVC_JOIN_FLAGS_ACCOUNT_CREATE;
1358         }
1359 #endif /* WITH_ADS */
1360
1361         status = libnet_join_joindomain_rpc(mem_ctx, r);
1362         if (!NT_STATUS_IS_OK(status)) {
1363                 libnet_join_set_error_string(mem_ctx, r,
1364                         "failed to join domain over rpc: %s",
1365                         get_friendly_nt_error_msg(status));
1366                 if (NT_STATUS_EQUAL(status, NT_STATUS_USER_EXISTS)) {
1367                         return WERR_SETUP_ALREADY_JOINED;
1368                 }
1369                 return ntstatus_to_werror(status);
1370         }
1371
1372         if (!libnet_join_joindomain_store_secrets(mem_ctx, r)) {
1373                 return WERR_SETUP_NOT_JOINED;
1374         }
1375
1376 #ifdef WITH_ADS
1377         if (r->out.domain_is_ad) {
1378                 ads_status  = libnet_join_post_processing_ads(mem_ctx, r);
1379                 if (!ADS_ERR_OK(ads_status)) {
1380                         return WERR_GENERAL_FAILURE;
1381                 }
1382         }
1383 #endif /* WITH_ADS */
1384
1385         return WERR_OK;
1386 }
1387
1388 /****************************************************************
1389 ****************************************************************/
1390
1391 WERROR libnet_Join(TALLOC_CTX *mem_ctx,
1392                    struct libnet_JoinCtx *r)
1393 {
1394         WERROR werr;
1395
1396         if (r->in.debug) {
1397                 LIBNET_JOIN_IN_DUMP_CTX(mem_ctx, r);
1398         }
1399
1400         werr = libnet_join_pre_processing(mem_ctx, r);
1401         if (!W_ERROR_IS_OK(werr)) {
1402                 goto done;
1403         }
1404
1405         if (r->in.join_flags & WKSSVC_JOIN_FLAGS_JOIN_TYPE) {
1406                 werr = libnet_DomainJoin(mem_ctx, r);
1407                 if (!W_ERROR_IS_OK(werr)) {
1408                         goto done;
1409                 }
1410
1411                 werr = libnet_join_post_verify(mem_ctx, r);
1412                 if (!W_ERROR_IS_OK(werr)) {
1413                         goto done;
1414                 }
1415         }
1416
1417         werr = libnet_join_post_processing(mem_ctx, r);
1418         if (!W_ERROR_IS_OK(werr)) {
1419                 goto done;
1420         }
1421  done:
1422         r->out.result = werr;
1423
1424         if (r->in.debug) {
1425                 LIBNET_JOIN_OUT_DUMP_CTX(mem_ctx, r);
1426         }
1427         return werr;
1428 }
1429
1430 /****************************************************************
1431 ****************************************************************/
1432
1433 static WERROR libnet_DomainUnjoin(TALLOC_CTX *mem_ctx,
1434                                   struct libnet_UnjoinCtx *r)
1435 {
1436         NTSTATUS status;
1437
1438         if (!r->in.domain_sid) {
1439                 struct dom_sid sid;
1440                 if (!secrets_fetch_domain_sid(lp_workgroup(), &sid)) {
1441                         libnet_unjoin_set_error_string(mem_ctx, r,
1442                                 "Unable to fetch domain sid: are we joined?");
1443                         return WERR_SETUP_NOT_JOINED;
1444                 }
1445                 r->in.domain_sid = sid_dup_talloc(mem_ctx, &sid);
1446                 W_ERROR_HAVE_NO_MEMORY(r->in.domain_sid);
1447         }
1448
1449         if (!r->in.dc_name) {
1450                 struct netr_DsRGetDCNameInfo *info;
1451                 status = dsgetdcname(mem_ctx,
1452                                      r->in.domain_name,
1453                                      NULL,
1454                                      NULL,
1455                                      DS_DIRECTORY_SERVICE_REQUIRED |
1456                                      DS_WRITABLE_REQUIRED |
1457                                      DS_RETURN_DNS_NAME,
1458                                      &info);
1459                 if (!NT_STATUS_IS_OK(status)) {
1460                         libnet_unjoin_set_error_string(mem_ctx, r,
1461                                 "failed to find DC for domain %s",
1462                                 r->in.domain_name,
1463                                 get_friendly_nt_error_msg(status));
1464                         return WERR_DOMAIN_CONTROLLER_NOT_FOUND;
1465                 }
1466
1467                 r->in.dc_name = talloc_strdup(mem_ctx,
1468                                               info->dc_unc);
1469                 W_ERROR_HAVE_NO_MEMORY(r->in.dc_name);
1470         }
1471
1472         status = libnet_join_unjoindomain_rpc(mem_ctx, r);
1473         if (!NT_STATUS_IS_OK(status)) {
1474                 libnet_unjoin_set_error_string(mem_ctx, r,
1475                         "failed to disable machine account via rpc: %s",
1476                         get_friendly_nt_error_msg(status));
1477                 if (NT_STATUS_EQUAL(status, NT_STATUS_NO_SUCH_USER)) {
1478                         return WERR_SETUP_NOT_JOINED;
1479                 }
1480                 return ntstatus_to_werror(status);
1481         }
1482
1483         r->out.disabled_machine_account = true;
1484
1485 #ifdef WITH_ADS
1486         if (r->in.unjoin_flags & WKSSVC_JOIN_FLAGS_ACCOUNT_DELETE) {
1487                 ADS_STATUS ads_status;
1488                 libnet_unjoin_connect_ads(mem_ctx, r);
1489                 ads_status = libnet_unjoin_remove_machine_acct(mem_ctx, r);
1490                 if (!ADS_ERR_OK(ads_status)) {
1491                         libnet_unjoin_set_error_string(mem_ctx, r,
1492                                 "failed to remove machine account from AD: %s",
1493                                 ads_errstr(ads_status));
1494                 } else {
1495                         r->out.deleted_machine_account = true;
1496                         /* dirty hack */
1497                         r->out.dns_domain_name = talloc_strdup(mem_ctx,
1498                                                                r->in.ads->server.realm);
1499                         W_ERROR_HAVE_NO_MEMORY(r->out.dns_domain_name);
1500                 }
1501         }
1502 #endif /* WITH_ADS */
1503
1504         libnet_join_unjoindomain_remove_secrets(mem_ctx, r);
1505
1506         return WERR_OK;
1507 }
1508
1509 /****************************************************************
1510 ****************************************************************/
1511
1512 static WERROR libnet_unjoin_pre_processing(TALLOC_CTX *mem_ctx,
1513                                            struct libnet_UnjoinCtx *r)
1514 {
1515         if (!r->in.domain_name) {
1516                 libnet_unjoin_set_error_string(mem_ctx, r,
1517                         "No domain name defined");
1518                 return WERR_INVALID_PARAM;
1519         }
1520
1521         if (r->in.modify_config && !lp_config_backend_is_registry()) {
1522                 return WERR_NOT_SUPPORTED;
1523         }
1524
1525         if (!secrets_init()) {
1526                 libnet_unjoin_set_error_string(mem_ctx, r,
1527                         "Unable to open secrets database");
1528                 return WERR_CAN_NOT_COMPLETE;
1529         }
1530
1531         return WERR_OK;
1532 }
1533
1534 /****************************************************************
1535 ****************************************************************/
1536
1537 static WERROR libnet_unjoin_post_processing(TALLOC_CTX *mem_ctx,
1538                                             struct libnet_UnjoinCtx *r)
1539 {
1540         saf_delete(r->out.netbios_domain_name);
1541         saf_delete(r->out.dns_domain_name);
1542
1543         return libnet_unjoin_config(r);
1544 }
1545
1546 /****************************************************************
1547 ****************************************************************/
1548
1549 WERROR libnet_Unjoin(TALLOC_CTX *mem_ctx,
1550                      struct libnet_UnjoinCtx *r)
1551 {
1552         WERROR werr;
1553
1554         if (r->in.debug) {
1555                 LIBNET_UNJOIN_IN_DUMP_CTX(mem_ctx, r);
1556         }
1557
1558         werr = libnet_unjoin_pre_processing(mem_ctx, r);
1559         if (!W_ERROR_IS_OK(werr)) {
1560                 goto done;
1561         }
1562
1563         if (r->in.unjoin_flags & WKSSVC_JOIN_FLAGS_JOIN_TYPE) {
1564                 werr = libnet_DomainUnjoin(mem_ctx, r);
1565                 if (!W_ERROR_IS_OK(werr)) {
1566                         libnet_unjoin_config(r);
1567                         goto done;
1568                 }
1569         }
1570
1571         werr = libnet_unjoin_post_processing(mem_ctx, r);
1572         if (!W_ERROR_IS_OK(werr)) {
1573                 goto done;
1574         }
1575
1576  done:
1577         r->out.result = werr;
1578
1579         if (r->in.debug) {
1580                 LIBNET_UNJOIN_OUT_DUMP_CTX(mem_ctx, r);
1581         }
1582
1583         return werr;
1584 }