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