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