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