Remove '\n' from error strings in libnet_join context.
[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_join.h"
23 #include "libnet/libnet_proto.h"
24
25 /****************************************************************
26 ****************************************************************/
27
28 static void libnet_join_set_error_string(TALLOC_CTX *mem_ctx,
29                                          struct libnet_JoinCtx *r,
30                                          const char *format, ...)
31 {
32         va_list args;
33         char *tmp = NULL;
34
35         va_start(args, format);
36         tmp = talloc_vasprintf(mem_ctx, format, args);
37         va_end(args);
38
39         TALLOC_FREE(r->out.error_string);
40         r->out.error_string = tmp;
41 }
42
43 /****************************************************************
44 ****************************************************************/
45
46 static void libnet_unjoin_set_error_string(TALLOC_CTX *mem_ctx,
47                                            struct libnet_UnjoinCtx *r,
48                                            const char *format, ...)
49 {
50         va_list args;
51         char *tmp = NULL;
52
53         va_start(args, format);
54         tmp = talloc_vasprintf(mem_ctx, format, args);
55         va_end(args);
56
57         TALLOC_FREE(r->out.error_string);
58         r->out.error_string = tmp;
59 }
60
61 #ifdef HAVE_LDAP
62
63 /****************************************************************
64 ****************************************************************/
65
66 static ADS_STATUS libnet_connect_ads(const char *dns_domain_name,
67                                      const char *netbios_domain_name,
68                                      const char *dc_name,
69                                      const char *user_name,
70                                      const char *password,
71                                      ADS_STRUCT **ads)
72 {
73         ADS_STATUS status;
74         ADS_STRUCT *my_ads = NULL;
75
76         my_ads = ads_init(dns_domain_name,
77                           netbios_domain_name,
78                           dc_name);
79         if (!my_ads) {
80                 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
81         }
82
83         if (user_name) {
84                 SAFE_FREE(my_ads->auth.user_name);
85                 my_ads->auth.user_name = SMB_STRDUP(user_name);
86         }
87
88         if (password) {
89                 SAFE_FREE(my_ads->auth.password);
90                 my_ads->auth.password = SMB_STRDUP(password);
91         }
92
93         status = ads_connect(my_ads);
94         if (!ADS_ERR_OK(status)) {
95                 ads_destroy(&my_ads);
96                 return status;
97         }
98
99         *ads = my_ads;
100         return ADS_SUCCESS;
101 }
102
103 /****************************************************************
104 ****************************************************************/
105
106 static ADS_STATUS libnet_join_connect_ads(TALLOC_CTX *mem_ctx,
107                                           struct libnet_JoinCtx *r)
108 {
109         ADS_STATUS status;
110
111         if (r->in.ads) {
112                 ads_destroy(&r->in.ads);
113         }
114
115         status = libnet_connect_ads(r->in.domain_name,
116                                     r->in.domain_name,
117                                     r->in.dc_name,
118                                     r->in.admin_account,
119                                     r->in.admin_password,
120                                     &r->in.ads);
121         if (!ADS_ERR_OK(status)) {
122                 libnet_join_set_error_string(mem_ctx, r,
123                         "failed to connect to AD: %s",
124                         ads_errstr(status));
125         }
126
127         return status;
128 }
129
130 /****************************************************************
131 ****************************************************************/
132
133 static ADS_STATUS libnet_unjoin_connect_ads(TALLOC_CTX *mem_ctx,
134                                             struct libnet_UnjoinCtx *r)
135 {
136         ADS_STATUS status;
137
138         if (r->in.ads) {
139                 ads_destroy(&r->in.ads);
140         }
141
142         status = libnet_connect_ads(r->in.domain_name,
143                                     r->in.domain_name,
144                                     r->in.dc_name,
145                                     r->in.admin_account,
146                                     r->in.admin_password,
147                                     &r->in.ads);
148         if (!ADS_ERR_OK(status)) {
149                 libnet_unjoin_set_error_string(mem_ctx, r,
150                         "failed to connect to AD: %s",
151                         ads_errstr(status));
152         }
153
154         return status;
155 }
156
157 /****************************************************************
158 ****************************************************************/
159
160 static ADS_STATUS libnet_join_precreate_machine_acct(TALLOC_CTX *mem_ctx,
161                                                      struct libnet_JoinCtx *r)
162 {
163         ADS_STATUS status;
164         LDAPMessage *res = NULL;
165         const char *attrs[] = { "dn", NULL };
166
167         status = ads_search_dn(r->in.ads, &res, r->in.account_ou, attrs);
168         if (!ADS_ERR_OK(status)) {
169                 return status;
170         }
171
172         if (ads_count_replies(r->in.ads, res) != 1) {
173                 ads_msgfree(r->in.ads, res);
174                 return ADS_ERROR_LDAP(LDAP_NO_SUCH_OBJECT);
175         }
176
177         status = ads_create_machine_acct(r->in.ads,
178                                          r->in.machine_name,
179                                          r->in.account_ou);
180         ads_msgfree(r->in.ads, res);
181
182         if ((status.error_type == ENUM_ADS_ERROR_LDAP) &&
183             (status.err.rc == LDAP_ALREADY_EXISTS)) {
184                 status = ADS_SUCCESS;
185         }
186
187         return status;
188 }
189
190 /****************************************************************
191 ****************************************************************/
192
193 static ADS_STATUS libnet_unjoin_remove_machine_acct(TALLOC_CTX *mem_ctx,
194                                                     struct libnet_UnjoinCtx *r)
195 {
196         ADS_STATUS status;
197
198         if (!r->in.ads) {
199                 status = libnet_unjoin_connect_ads(mem_ctx, r);
200                 if (!ADS_ERR_OK(status)) {
201                         return status;
202                 }
203         }
204
205         status = ads_leave_realm(r->in.ads, r->in.machine_name);
206         if (!ADS_ERR_OK(status)) {
207                 libnet_unjoin_set_error_string(mem_ctx, r,
208                         "failed to leave realm: %s",
209                         ads_errstr(status));
210                 return status;
211         }
212
213         return ADS_SUCCESS;
214 }
215
216 /****************************************************************
217 ****************************************************************/
218
219 static ADS_STATUS libnet_join_find_machine_acct(TALLOC_CTX *mem_ctx,
220                                                 struct libnet_JoinCtx *r)
221 {
222         ADS_STATUS status;
223         LDAPMessage *res = NULL;
224         char *dn = NULL;
225
226         if (!r->in.machine_name) {
227                 return ADS_ERROR(LDAP_NO_MEMORY);
228         }
229
230         status = ads_find_machine_acct(r->in.ads,
231                                        &res,
232                                        r->in.machine_name);
233         if (!ADS_ERR_OK(status)) {
234                 return status;
235         }
236
237         if (ads_count_replies(r->in.ads, res) != 1) {
238                 status = ADS_ERROR_LDAP(LDAP_NO_MEMORY);
239                 goto done;
240         }
241
242         dn = ads_get_dn(r->in.ads, res);
243         if (!dn) {
244                 status = ADS_ERROR_LDAP(LDAP_NO_MEMORY);
245                 goto done;
246         }
247
248         TALLOC_FREE(r->out.dn);
249         r->out.dn = talloc_strdup(mem_ctx, dn);
250         if (!r->out.dn) {
251                 status = ADS_ERROR_LDAP(LDAP_NO_MEMORY);
252                 goto done;
253         }
254
255  done:
256         ads_msgfree(r->in.ads, res);
257         ads_memfree(r->in.ads, dn);
258
259         return status;
260 }
261
262 /****************************************************************
263 ****************************************************************/
264
265 static ADS_STATUS libnet_join_set_machine_spn(TALLOC_CTX *mem_ctx,
266                                               struct libnet_JoinCtx *r)
267 {
268         ADS_STATUS status;
269         ADS_MODLIST mods;
270         fstring my_fqdn;
271         const char *spn_array[3] = {NULL, NULL, NULL};
272         char *spn = NULL;
273
274         if (!r->in.ads) {
275                 status = libnet_join_connect_ads(mem_ctx, r);
276                 if (!ADS_ERR_OK(status)) {
277                         return status;
278                 }
279         }
280
281         status = libnet_join_find_machine_acct(mem_ctx, r);
282         if (!ADS_ERR_OK(status)) {
283                 return status;
284         }
285
286         spn = talloc_asprintf(mem_ctx, "HOST/%s", r->in.machine_name);
287         if (!spn) {
288                 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
289         }
290         strupper_m(spn);
291         spn_array[0] = spn;
292
293         if (name_to_fqdn(my_fqdn, r->in.machine_name) &&
294             !strequal(my_fqdn, r->in.machine_name)) {
295
296                 strlower_m(my_fqdn);
297                 spn = talloc_asprintf(mem_ctx, "HOST/%s", my_fqdn);
298                 if (!spn) {
299                         return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
300                 }
301                 spn_array[1] = spn;
302         }
303
304         mods = ads_init_mods(mem_ctx);
305         if (!mods) {
306                 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
307         }
308
309         status = ads_mod_str(mem_ctx, &mods, "dNSHostName", my_fqdn);
310         if (!ADS_ERR_OK(status)) {
311                 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
312         }
313
314         status = ads_mod_strlist(mem_ctx, &mods, "servicePrincipalName",
315                                  spn_array);
316         if (!ADS_ERR_OK(status)) {
317                 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
318         }
319
320         return ads_gen_mod(r->in.ads, r->out.dn, mods);
321 }
322
323 /****************************************************************
324 ****************************************************************/
325
326 static ADS_STATUS libnet_join_set_machine_upn(TALLOC_CTX *mem_ctx,
327                                               struct libnet_JoinCtx *r)
328 {
329         ADS_STATUS status;
330         ADS_MODLIST mods;
331
332         if (!r->in.create_upn) {
333                 return ADS_SUCCESS;
334         }
335
336         if (!r->in.ads) {
337                 status = libnet_join_connect_ads(mem_ctx, r);
338                 if (!ADS_ERR_OK(status)) {
339                         return status;
340                 }
341         }
342
343         status = libnet_join_find_machine_acct(mem_ctx, r);
344         if (!ADS_ERR_OK(status)) {
345                 return status;
346         }
347
348         if (!r->in.upn) {
349                 r->in.upn = talloc_asprintf(mem_ctx,
350                                             "host/%s@%s",
351                                             r->in.machine_name,
352                                             r->out.dns_domain_name);
353                 if (!r->in.upn) {
354                         return ADS_ERROR(LDAP_NO_MEMORY);
355                 }
356         }
357
358         mods = ads_init_mods(mem_ctx);
359         if (!mods) {
360                 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
361         }
362
363         status = ads_mod_str(mem_ctx, &mods, "userPrincipalName", r->in.upn);
364         if (!ADS_ERR_OK(status)) {
365                 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
366         }
367
368         return ads_gen_mod(r->in.ads, r->out.dn, mods);
369 }
370
371
372 /****************************************************************
373 ****************************************************************/
374
375 static ADS_STATUS libnet_join_set_os_attributes(TALLOC_CTX *mem_ctx,
376                                                 struct libnet_JoinCtx *r)
377 {
378         ADS_STATUS status;
379         ADS_MODLIST mods;
380         char *os_sp = NULL;
381
382         if (!r->in.os_name || !r->in.os_version ) {
383                 return ADS_SUCCESS;
384         }
385
386         if (!r->in.ads) {
387                 status = libnet_join_connect_ads(mem_ctx, r);
388                 if (!ADS_ERR_OK(status)) {
389                         return status;
390                 }
391         }
392
393         status = libnet_join_find_machine_acct(mem_ctx, r);
394         if (!ADS_ERR_OK(status)) {
395                 return status;
396         }
397
398         mods = ads_init_mods(mem_ctx);
399         if (!mods) {
400                 return ADS_ERROR(LDAP_NO_MEMORY);
401         }
402
403         os_sp = talloc_asprintf(mem_ctx, "Samba %s", SAMBA_VERSION_STRING);
404         if (!os_sp) {
405                 return ADS_ERROR(LDAP_NO_MEMORY);
406         }
407
408         status = ads_mod_str(mem_ctx, &mods, "operatingSystem",
409                              r->in.os_name);
410         if (!ADS_ERR_OK(status)) {
411                 return status;
412         }
413
414         status = ads_mod_str(mem_ctx, &mods, "operatingSystemVersion",
415                              r->in.os_version);
416         if (!ADS_ERR_OK(status)) {
417                 return status;
418         }
419
420         status = ads_mod_str(mem_ctx, &mods, "operatingSystemServicePack",
421                              os_sp);
422         if (!ADS_ERR_OK(status)) {
423                 return status;
424         }
425
426         return ads_gen_mod(r->in.ads, r->out.dn, mods);
427 }
428
429 #endif /* HAVE_LDAP */
430
431 /****************************************************************
432 ****************************************************************/
433
434 static bool libnet_join_create_keytab(TALLOC_CTX *mem_ctx,
435                                       struct libnet_JoinCtx *r)
436 {
437         if (!lp_use_kerberos_keytab()) {
438                 return true;
439         }
440 #ifdef HAVE_KRB5
441         if (!ads_keytab_create_default(r->in.ads)) {
442                 return false;
443         }
444 #endif /* HAVE_KRB5 */
445         return true;
446 }
447
448 #ifdef HAVE_KRB5
449
450 /****************************************************************
451 ****************************************************************/
452
453 static bool libnet_join_derive_salting_principal(TALLOC_CTX *mem_ctx,
454                                                  struct libnet_JoinCtx *r)
455 {
456         uint32_t domain_func;
457         ADS_STATUS status;
458         const char *salt = NULL;
459         char *std_salt = NULL;
460
461         status = ads_domain_func_level(r->in.ads, &domain_func);
462         if (!ADS_ERR_OK(status)) {
463                 libnet_join_set_error_string(mem_ctx, r,
464                         "Failed to determine domain functional level!");
465                 return false;
466         }
467
468         std_salt = kerberos_standard_des_salt();
469         if (!std_salt) {
470                 libnet_join_set_error_string(mem_ctx, r,
471                         "failed to obtain standard DES salt");
472                 return false;
473         }
474
475         salt = talloc_strdup(mem_ctx, std_salt);
476         if (!salt) {
477                 return false;
478         }
479
480         SAFE_FREE(std_salt);
481
482         if (domain_func == DS_DOMAIN_FUNCTION_2000) {
483                 char *upn;
484
485                 upn = ads_get_upn(r->in.ads, mem_ctx,
486                                   r->in.machine_name);
487                 if (upn) {
488                         salt = talloc_strdup(mem_ctx, upn);
489                         if (!salt) {
490                                 return false;
491                         }
492                 }
493         }
494
495         return kerberos_secrets_store_des_salt(salt);
496 }
497
498 #endif /* HAVE_KRB5 */
499
500 /****************************************************************
501 ****************************************************************/
502
503 static bool libnet_join_joindomain_store_secrets(TALLOC_CTX *mem_ctx,
504                                                  struct libnet_JoinCtx *r)
505 {
506         if (!secrets_store_domain_sid(r->out.netbios_domain_name,
507                                       r->out.domain_sid))
508         {
509                 return false;
510         }
511
512         if (!secrets_store_machine_password(r->in.machine_password,
513                                             r->out.netbios_domain_name,
514                                             SEC_CHAN_WKSTA))
515         {
516                 return false;
517         }
518
519         return true;
520 }
521
522 /****************************************************************
523 ****************************************************************/
524
525 static NTSTATUS libnet_join_joindomain_rpc(TALLOC_CTX *mem_ctx,
526                                            struct libnet_JoinCtx *r)
527 {
528         struct cli_state *cli = NULL;
529         struct rpc_pipe_client *pipe_hnd = NULL;
530         POLICY_HND sam_pol, domain_pol, user_pol, lsa_pol;
531         NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
532         char *acct_name;
533         const char *const_acct_name;
534         uint32 user_rid;
535         uint32 num_rids, *name_types, *user_rids;
536         uint32 flags = 0x3e8;
537         uint32 acb_info = ACB_WSTRUST;
538         uint32 fields_present;
539         uchar pwbuf[532];
540         SAM_USERINFO_CTR ctr;
541         SAM_USER_INFO_25 p25;
542         const int infolevel = 25;
543         struct MD5Context md5ctx;
544         uchar md5buffer[16];
545         DATA_BLOB digested_session_key;
546         uchar md4_trust_password[16];
547
548         if (!r->in.machine_password) {
549                 r->in.machine_password = talloc_strdup(mem_ctx, generate_random_str(DEFAULT_TRUST_ACCOUNT_PASSWORD_LENGTH));
550                 NT_STATUS_HAVE_NO_MEMORY(r->in.machine_password);
551         }
552
553         status = cli_full_connection(&cli, NULL,
554                                      r->in.dc_name,
555                                      NULL, 0,
556                                      "IPC$", "IPC",
557                                      r->in.admin_account,
558                                      NULL,
559                                      r->in.admin_password,
560                                      0,
561                                      Undefined, NULL);
562
563         if (!NT_STATUS_IS_OK(status)) {
564                 goto done;
565         }
566
567         pipe_hnd = cli_rpc_pipe_open_noauth(cli, PI_LSARPC, &status);
568         if (!pipe_hnd) {
569                 goto done;
570         }
571
572         status = rpccli_lsa_open_policy(pipe_hnd, mem_ctx, True,
573                                         SEC_RIGHTS_MAXIMUM_ALLOWED, &lsa_pol);
574         if (!NT_STATUS_IS_OK(status)) {
575                 goto done;
576         }
577
578         status = rpccli_lsa_query_info_policy2(pipe_hnd, mem_ctx, &lsa_pol,
579                                                12,
580                                                &r->out.netbios_domain_name,
581                                                &r->out.dns_domain_name,
582                                                NULL,
583                                                NULL,
584                                                &r->out.domain_sid);
585
586         if (NT_STATUS_IS_OK(status)) {
587                 r->out.domain_is_ad = true;
588         }
589
590         if (!NT_STATUS_IS_OK(status)) {
591                 status = rpccli_lsa_query_info_policy(pipe_hnd, mem_ctx, &lsa_pol,
592                                                       5,
593                                                       &r->out.netbios_domain_name,
594                                                       &r->out.domain_sid);
595                 if (!NT_STATUS_IS_OK(status)) {
596                         goto done;
597                 }
598         }
599
600         rpccli_lsa_Close(pipe_hnd, mem_ctx, &lsa_pol);
601         cli_rpc_pipe_close(pipe_hnd);
602
603         pipe_hnd = cli_rpc_pipe_open_noauth(cli, PI_SAMR, &status);
604         if (!pipe_hnd) {
605                 goto done;
606         }
607
608         status = rpccli_samr_connect(pipe_hnd, mem_ctx,
609                                      SEC_RIGHTS_MAXIMUM_ALLOWED, &sam_pol);
610         if (!NT_STATUS_IS_OK(status)) {
611                 goto done;
612         }
613
614         status = rpccli_samr_open_domain(pipe_hnd, mem_ctx, &sam_pol,
615                                          SEC_RIGHTS_MAXIMUM_ALLOWED,
616                                          r->out.domain_sid,
617                                          &domain_pol);
618         if (!NT_STATUS_IS_OK(status)) {
619                 goto done;
620         }
621
622         acct_name = talloc_asprintf(mem_ctx, "%s$", r->in.machine_name);
623         strlower_m(acct_name);
624         const_acct_name = acct_name;
625
626         if (r->in.join_flags & WKSSVC_JOIN_FLAGS_ACCOUNT_CREATE) {
627                 status = rpccli_samr_create_dom_user(pipe_hnd, mem_ctx,
628                                                      &domain_pol,
629                                                      acct_name, ACB_WSTRUST,
630                                                      0xe005000b, &user_pol,
631                                                      &user_rid);
632                 if (NT_STATUS_EQUAL(status, NT_STATUS_USER_EXISTS)) {
633                         if (!(r->in.join_flags & WKSSVC_JOIN_FLAGS_DOMAIN_JOIN_IF_JOINED)) {
634                                 goto done;
635                         }
636                 }
637
638                 if (NT_STATUS_IS_OK(status)) {
639                         rpccli_samr_close(pipe_hnd, mem_ctx, &user_pol);
640                 }
641         }
642
643         status = rpccli_samr_lookup_names(pipe_hnd, mem_ctx,
644                                           &domain_pol, flags, 1,
645                                           &const_acct_name,
646                                           &num_rids, &user_rids, &name_types);
647         if (!NT_STATUS_IS_OK(status)) {
648                 goto done;
649         }
650
651         if (name_types[0] != SID_NAME_USER) {
652                 status = NT_STATUS_INVALID_WORKSTATION;
653                 goto done;
654         }
655
656         user_rid = user_rids[0];
657
658         status = rpccli_samr_open_user(pipe_hnd, mem_ctx, &domain_pol,
659                                        SEC_RIGHTS_MAXIMUM_ALLOWED, user_rid,
660                                        &user_pol);
661         if (!NT_STATUS_IS_OK(status)) {
662                 goto done;
663         }
664
665         E_md4hash(r->in.machine_password, md4_trust_password);
666         encode_pw_buffer(pwbuf, r->in.machine_password, STR_UNICODE);
667
668         generate_random_buffer((uint8*)md5buffer, sizeof(md5buffer));
669         digested_session_key = data_blob_talloc(mem_ctx, 0, 16);
670
671         MD5Init(&md5ctx);
672         MD5Update(&md5ctx, md5buffer, sizeof(md5buffer));
673         MD5Update(&md5ctx, cli->user_session_key.data,
674                   cli->user_session_key.length);
675         MD5Final(digested_session_key.data, &md5ctx);
676
677         SamOEMhashBlob(pwbuf, sizeof(pwbuf), &digested_session_key);
678         memcpy(&pwbuf[516], md5buffer, sizeof(md5buffer));
679
680         acb_info |= ACB_PWNOEXP;
681 #if 0
682         if ( dom_type == ND_TYPE_AD ) {
683 #if !defined(ENCTYPE_ARCFOUR_HMAC)
684                 acb_info |= ACB_USE_DES_KEY_ONLY;
685 #endif
686                 ;;
687         }
688 #endif
689         ZERO_STRUCT(ctr);
690         ZERO_STRUCT(p25);
691
692         fields_present = ACCT_NT_PWD_SET | ACCT_LM_PWD_SET | ACCT_FLAGS;
693         init_sam_user_info25P(&p25, fields_present, acb_info, (char *)pwbuf);
694
695         ctr.switch_value = infolevel;
696         ctr.info.id25    = &p25;
697
698         status = rpccli_samr_set_userinfo2(pipe_hnd, mem_ctx, &user_pol,
699                                            infolevel, &cli->user_session_key,
700                                            &ctr);
701         if (!NT_STATUS_IS_OK(status)) {
702                 goto done;
703         }
704
705         rpccli_samr_close(pipe_hnd, mem_ctx, &user_pol);
706         cli_rpc_pipe_close(pipe_hnd);
707
708         status = NT_STATUS_OK;
709  done:
710         if (cli) {
711                 cli_shutdown(cli);
712         }
713
714         return status;
715 }
716
717 /****************************************************************
718 ****************************************************************/
719
720 static bool libnet_join_unjoindomain_remove_secrets(TALLOC_CTX *mem_ctx,
721                                                     struct libnet_UnjoinCtx *r)
722 {
723         if (!secrets_delete_machine_password_ex(lp_workgroup())) {
724                 return false;
725         }
726
727         if (!secrets_delete_domain_sid(lp_workgroup())) {
728                 return false;
729         }
730
731         return true;
732 }
733
734 /****************************************************************
735 ****************************************************************/
736
737 static NTSTATUS libnet_join_unjoindomain_rpc(TALLOC_CTX *mem_ctx,
738                                              struct libnet_UnjoinCtx *r)
739 {
740         struct cli_state *cli = NULL;
741         struct rpc_pipe_client *pipe_hnd = NULL;
742         POLICY_HND sam_pol, domain_pol, user_pol;
743         NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
744         char *acct_name;
745         uint32 flags = 0x3e8;
746         const char *const_acct_name;
747         uint32 user_rid;
748         uint32 num_rids, *name_types, *user_rids;
749         SAM_USERINFO_CTR ctr, *qctr = NULL;
750         SAM_USER_INFO_16 p16;
751
752         status = cli_full_connection(&cli, NULL,
753                                      r->in.dc_name,
754                                      NULL, 0,
755                                      "IPC$", "IPC",
756                                      r->in.admin_account,
757                                      NULL,
758                                      r->in.admin_password,
759                                      0, Undefined, NULL);
760
761         if (!NT_STATUS_IS_OK(status)) {
762                 goto done;
763         }
764
765         pipe_hnd = cli_rpc_pipe_open_noauth(cli, PI_SAMR, &status);
766         if (!pipe_hnd) {
767                 goto done;
768         }
769
770         status = rpccli_samr_connect(pipe_hnd, mem_ctx,
771                                      SEC_RIGHTS_MAXIMUM_ALLOWED, &sam_pol);
772         if (!NT_STATUS_IS_OK(status)) {
773                 goto done;
774         }
775
776         status = rpccli_samr_open_domain(pipe_hnd, mem_ctx, &sam_pol,
777                                          SEC_RIGHTS_MAXIMUM_ALLOWED,
778                                          r->in.domain_sid,
779                                          &domain_pol);
780         if (!NT_STATUS_IS_OK(status)) {
781                 goto done;
782         }
783
784         acct_name = talloc_asprintf(mem_ctx, "%s$", r->in.machine_name);
785         strlower_m(acct_name);
786         const_acct_name = acct_name;
787
788         status = rpccli_samr_lookup_names(pipe_hnd, mem_ctx,
789                                           &domain_pol, flags, 1,
790                                           &const_acct_name,
791                                           &num_rids, &user_rids, &name_types);
792         if (!NT_STATUS_IS_OK(status)) {
793                 goto done;
794         }
795
796         if (name_types[0] != SID_NAME_USER) {
797                 status = NT_STATUS_INVALID_WORKSTATION;
798                 goto done;
799         }
800
801         user_rid = user_rids[0];
802
803         status = rpccli_samr_open_user(pipe_hnd, mem_ctx, &domain_pol,
804                                        SEC_RIGHTS_MAXIMUM_ALLOWED,
805                                        user_rid, &user_pol);
806         if (!NT_STATUS_IS_OK(status)) {
807                 goto done;
808         }
809
810         status = rpccli_samr_query_userinfo(pipe_hnd, mem_ctx,
811                                             &user_pol, 16, &qctr);
812         if (!NT_STATUS_IS_OK(status)) {
813                 rpccli_samr_close(pipe_hnd, mem_ctx, &user_pol);
814                 goto done;
815         }
816
817         ZERO_STRUCT(ctr);
818         ctr.switch_value = 16;
819         ctr.info.id16 = &p16;
820
821         p16.acb_info = qctr->info.id16->acb_info | ACB_DISABLED;
822
823         status = rpccli_samr_set_userinfo2(pipe_hnd, mem_ctx, &user_pol, 16,
824                                            &cli->user_session_key, &ctr);
825
826         rpccli_samr_close(pipe_hnd, mem_ctx, &user_pol);
827
828 done:
829         if (pipe_hnd) {
830                 rpccli_samr_close(pipe_hnd, mem_ctx, &domain_pol);
831                 rpccli_samr_close(pipe_hnd, mem_ctx, &sam_pol);
832                 cli_rpc_pipe_close(pipe_hnd);
833         }
834
835         if (cli) {
836                 cli_shutdown(cli);
837         }
838
839         return status;
840 }
841
842 /****************************************************************
843 ****************************************************************/
844
845 static WERROR do_join_modify_vals_config(struct libnet_JoinCtx *r)
846 {
847         WERROR werr;
848
849         if (!(r->in.join_flags & WKSSVC_JOIN_FLAGS_JOIN_TYPE)) {
850
851                 werr = libnet_conf_set_global_parameter("security", "user");
852                 W_ERROR_NOT_OK_RETURN(werr);
853
854                 werr = libnet_conf_set_global_parameter("workgroup",
855                                                         r->in.domain_name);
856                 return werr;
857         }
858
859         werr = libnet_conf_set_global_parameter("security", "domain");
860         W_ERROR_NOT_OK_RETURN(werr);
861
862         werr = libnet_conf_set_global_parameter("workgroup",
863                                                 r->out.netbios_domain_name);
864         W_ERROR_NOT_OK_RETURN(werr);
865
866         if (r->out.domain_is_ad) {
867                 werr = libnet_conf_set_global_parameter("security", "ads");
868                 W_ERROR_NOT_OK_RETURN(werr);
869
870                 werr = libnet_conf_set_global_parameter("realm",
871                                                        r->out.dns_domain_name);
872                 W_ERROR_NOT_OK_RETURN(werr);
873         }
874
875         return werr;
876 }
877
878 /****************************************************************
879 ****************************************************************/
880
881 static WERROR do_unjoin_modify_vals_config(struct libnet_UnjoinCtx *r)
882 {
883         WERROR werr = WERR_OK;
884
885         if (r->in.unjoin_flags & WKSSVC_JOIN_FLAGS_JOIN_TYPE) {
886
887                 werr = libnet_conf_set_global_parameter("security", "user");
888                 W_ERROR_NOT_OK_RETURN(werr);
889         }
890
891         libnet_conf_delete_parameter(GLOBAL_NAME, "realm");
892
893         return werr;
894 }
895
896 /****************************************************************
897 ****************************************************************/
898
899 static WERROR do_JoinConfig(struct libnet_JoinCtx *r)
900 {
901         WERROR werr;
902
903         if (!W_ERROR_IS_OK(r->out.result)) {
904                 return r->out.result;
905         }
906
907         if (!r->in.modify_config) {
908                 return WERR_OK;
909         }
910
911         werr = do_join_modify_vals_config(r);
912         if (!W_ERROR_IS_OK(werr)) {
913                 return werr;
914         }
915
916         r->out.modified_config = true;
917         r->out.result = werr;
918
919         return werr;
920 }
921
922 /****************************************************************
923 ****************************************************************/
924
925 static WERROR do_UnjoinConfig(struct libnet_UnjoinCtx *r)
926 {
927         WERROR werr;
928
929         if (!W_ERROR_IS_OK(r->out.result)) {
930                 return r->out.result;
931         }
932
933         if (!r->in.modify_config) {
934                 return WERR_OK;
935         }
936
937         werr = do_unjoin_modify_vals_config(r);
938         if (!W_ERROR_IS_OK(werr)) {
939                 return werr;
940         }
941
942         r->out.modified_config = true;
943         r->out.result = werr;
944
945         return werr;
946 }
947
948 /****************************************************************
949 ****************************************************************/
950
951 static int libnet_destroy_JoinCtx(struct libnet_JoinCtx *r)
952 {
953         if (r->in.ads) {
954                 ads_destroy(&r->in.ads);
955         }
956
957         return 0;
958 }
959
960 /****************************************************************
961 ****************************************************************/
962
963 static int libnet_destroy_UnjoinCtx(struct libnet_UnjoinCtx *r)
964 {
965         if (r->in.ads) {
966                 ads_destroy(&r->in.ads);
967         }
968
969         return 0;
970 }
971
972 /****************************************************************
973 ****************************************************************/
974
975 WERROR libnet_init_JoinCtx(TALLOC_CTX *mem_ctx,
976                            struct libnet_JoinCtx **r)
977 {
978         struct libnet_JoinCtx *ctx;
979
980         ctx = talloc_zero(mem_ctx, struct libnet_JoinCtx);
981         if (!ctx) {
982                 return WERR_NOMEM;
983         }
984
985         talloc_set_destructor(ctx, libnet_destroy_JoinCtx);
986
987         ctx->in.machine_name = talloc_strdup(mem_ctx, global_myname());
988         W_ERROR_HAVE_NO_MEMORY(ctx->in.machine_name);
989
990         *r = ctx;
991
992         return WERR_OK;
993 }
994
995 /****************************************************************
996 ****************************************************************/
997
998 WERROR libnet_init_UnjoinCtx(TALLOC_CTX *mem_ctx,
999                              struct libnet_UnjoinCtx **r)
1000 {
1001         struct libnet_UnjoinCtx *ctx;
1002
1003         ctx = talloc_zero(mem_ctx, struct libnet_UnjoinCtx);
1004         if (!ctx) {
1005                 return WERR_NOMEM;
1006         }
1007
1008         talloc_set_destructor(ctx, libnet_destroy_UnjoinCtx);
1009
1010         ctx->in.machine_name = talloc_strdup(mem_ctx, global_myname());
1011         W_ERROR_HAVE_NO_MEMORY(ctx->in.machine_name);
1012
1013         *r = ctx;
1014
1015         return WERR_OK;
1016 }
1017
1018 /****************************************************************
1019 ****************************************************************/
1020
1021 static WERROR libnet_DomainJoin(TALLOC_CTX *mem_ctx,
1022                                 struct libnet_JoinCtx *r)
1023 {
1024         NTSTATUS status;
1025 #ifdef HAVE_LDAP
1026         ADS_STATUS ads_status;
1027
1028         if (r->in.account_ou) {
1029                 ads_status = libnet_join_connect_ads(mem_ctx, r);
1030                 if (!ADS_ERR_OK(ads_status)) {
1031                         return WERR_GENERAL_FAILURE;
1032                 }
1033                 ads_status = libnet_join_precreate_machine_acct(mem_ctx, r);
1034                 if (!ADS_ERR_OK(ads_status)) {
1035                         libnet_join_set_error_string(mem_ctx, r,
1036                                 "failed to precreate account in ou %s: %s",
1037                                 r->in.account_ou,
1038                                 ads_errstr(ads_status));
1039                         return WERR_GENERAL_FAILURE;
1040                 }
1041
1042                 r->in.join_flags &= ~WKSSVC_JOIN_FLAGS_ACCOUNT_CREATE;
1043         }
1044 #endif /* HAVE_LDAP */
1045         status = libnet_join_joindomain_rpc(mem_ctx, r);
1046         if (!NT_STATUS_IS_OK(status)) {
1047                 if (NT_STATUS_EQUAL(status, NT_STATUS_USER_EXISTS)) {
1048                         return WERR_SETUP_ALREADY_JOINED;
1049                 }
1050                 return ntstatus_to_werror(status);
1051         }
1052
1053         if (!libnet_join_joindomain_store_secrets(mem_ctx, r)) {
1054                 return WERR_SETUP_NOT_JOINED;
1055         }
1056
1057 #ifdef HAVE_LDAP
1058         ads_status = libnet_join_set_machine_spn(mem_ctx, r);
1059         if (!ADS_ERR_OK(ads_status)) {
1060                 libnet_join_set_error_string(mem_ctx, r,
1061                         "failed to set machine spn: %s",
1062                         ads_errstr(ads_status));
1063                 return WERR_GENERAL_FAILURE;
1064         }
1065
1066         ads_status = libnet_join_set_os_attributes(mem_ctx, r);
1067         if (!ADS_ERR_OK(ads_status)) {
1068                 libnet_join_set_error_string(mem_ctx, r,
1069                         "failed to set machine os attributes: %s",
1070                         ads_errstr(ads_status));
1071                 return WERR_GENERAL_FAILURE;
1072         }
1073
1074         ads_status = libnet_join_set_machine_upn(mem_ctx, r);
1075         if (!ADS_ERR_OK(ads_status)) {
1076                 libnet_join_set_error_string(mem_ctx, r,
1077                         "failed to set machine upn: %s",
1078                         ads_errstr(ads_status));
1079                 return WERR_GENERAL_FAILURE;
1080         }
1081
1082 #ifdef HAVE_KRB5
1083         if (!libnet_join_derive_salting_principal(mem_ctx, r)) {
1084                 return WERR_GENERAL_FAILURE;
1085         }
1086 #endif /* HAVE_KRB5 */
1087
1088 #endif /* HAVE_LDAP */
1089         if (!libnet_join_create_keytab(mem_ctx, r)) {
1090                 libnet_join_set_error_string(mem_ctx, r,
1091                         "failed to create kerberos keytab");
1092                 return WERR_GENERAL_FAILURE;
1093         }
1094
1095         return WERR_OK;
1096 }
1097
1098 /****************************************************************
1099 ****************************************************************/
1100
1101 WERROR libnet_Join(TALLOC_CTX *mem_ctx,
1102                    struct libnet_JoinCtx *r)
1103 {
1104         WERROR werr;
1105
1106         if (!r->in.domain_name) {
1107                 return WERR_INVALID_PARAM;
1108         }
1109
1110         if (r->in.modify_config && !lp_include_registry_globals()) {
1111                 return WERR_NOT_SUPPORTED;
1112         }
1113
1114         if (IS_DC) {
1115                 return WERR_SETUP_DOMAIN_CONTROLLER;
1116         }
1117
1118         if (r->in.join_flags & WKSSVC_JOIN_FLAGS_JOIN_TYPE) {
1119                 werr = libnet_DomainJoin(mem_ctx, r);
1120                 if (!W_ERROR_IS_OK(werr)) {
1121                         return werr;
1122                 }
1123         }
1124
1125         werr = do_JoinConfig(r);
1126         if (!W_ERROR_IS_OK(werr)) {
1127                 return werr;
1128         }
1129
1130         return werr;
1131 }
1132
1133 /****************************************************************
1134 ****************************************************************/
1135
1136 static WERROR libnet_DomainUnjoin(TALLOC_CTX *mem_ctx,
1137                                   struct libnet_UnjoinCtx *r)
1138 {
1139         NTSTATUS status;
1140
1141         status = libnet_join_unjoindomain_rpc(mem_ctx, r);
1142         if (!NT_STATUS_IS_OK(status)) {
1143                 libnet_unjoin_set_error_string(mem_ctx, r,
1144                         "failed to unjoin domain: %s",
1145                         nt_errstr(status));
1146                 if (NT_STATUS_EQUAL(status, NT_STATUS_NO_SUCH_USER)) {
1147                         return WERR_SETUP_NOT_JOINED;
1148                 }
1149                 return ntstatus_to_werror(status);
1150         }
1151
1152 #ifdef HAVE_LDAP
1153         if (r->in.unjoin_flags & WKSSVC_JOIN_FLAGS_ACCOUNT_DELETE) {
1154                 ADS_STATUS ads_status;
1155                 libnet_unjoin_connect_ads(mem_ctx, r);
1156                 ads_status = libnet_unjoin_remove_machine_acct(mem_ctx, r);
1157                 if (!ADS_ERR_OK(ads_status)) {
1158                         libnet_unjoin_set_error_string(mem_ctx, r,
1159                                 "failed to remove machine account from AD: %s",
1160                                 ads_errstr(ads_status));
1161                 }
1162         }
1163 #endif /* HAVE_LDAP */
1164         libnet_join_unjoindomain_remove_secrets(mem_ctx, r);
1165
1166         return WERR_OK;
1167 }
1168
1169 /****************************************************************
1170 ****************************************************************/
1171
1172 WERROR libnet_Unjoin(TALLOC_CTX *mem_ctx,
1173                      struct libnet_UnjoinCtx *r)
1174 {
1175         WERROR werr;
1176
1177         if (r->in.modify_config && !lp_include_registry_globals()) {
1178                 return WERR_NOT_SUPPORTED;
1179         }
1180
1181         if (r->in.unjoin_flags & WKSSVC_JOIN_FLAGS_JOIN_TYPE) {
1182                 werr = libnet_DomainUnjoin(mem_ctx, r);
1183                 if (!W_ERROR_IS_OK(werr)) {
1184                         do_UnjoinConfig(r);
1185                         return werr;
1186                 }
1187         }
1188
1189         werr = do_UnjoinConfig(r);
1190         if (!W_ERROR_IS_OK(werr)) {
1191                 return werr;
1192         }
1193
1194         return werr;
1195 }