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