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