Merge branch 'v3-2-test' of ssh://jra@git.samba.org/data/git/samba into v3-2-test
[jra/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
34         if (r->out.error_string) {
35                 return;
36         }
37
38         va_start(args, format);
39         r->out.error_string = talloc_vasprintf(mem_ctx, format, args);
40         va_end(args);
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
52         if (r->out.error_string) {
53                 return;
54         }
55
56         va_start(args, format);
57         r->out.error_string = talloc_vasprintf(mem_ctx, format, args);
58         va_end(args);
59 }
60
61 #ifdef WITH_ADS
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 /****************************************************************
430 ****************************************************************/
431
432 static bool libnet_join_create_keytab(TALLOC_CTX *mem_ctx,
433                                       struct libnet_JoinCtx *r)
434 {
435         if (!lp_use_kerberos_keytab()) {
436                 return true;
437         }
438
439         if (!ads_keytab_create_default(r->in.ads)) {
440                 return false;
441         }
442
443         return true;
444 }
445
446 /****************************************************************
447 ****************************************************************/
448
449 static bool libnet_join_derive_salting_principal(TALLOC_CTX *mem_ctx,
450                                                  struct libnet_JoinCtx *r)
451 {
452         uint32_t domain_func;
453         ADS_STATUS status;
454         const char *salt = NULL;
455         char *std_salt = NULL;
456
457         status = ads_domain_func_level(r->in.ads, &domain_func);
458         if (!ADS_ERR_OK(status)) {
459                 libnet_join_set_error_string(mem_ctx, r,
460                         "failed to determine domain functional level: %s",
461                         ads_errstr(status));
462                 return false;
463         }
464
465         std_salt = kerberos_standard_des_salt();
466         if (!std_salt) {
467                 libnet_join_set_error_string(mem_ctx, r,
468                         "failed to obtain standard DES salt");
469                 return false;
470         }
471
472         salt = talloc_strdup(mem_ctx, std_salt);
473         if (!salt) {
474                 return false;
475         }
476
477         SAFE_FREE(std_salt);
478
479         if (domain_func == DS_DOMAIN_FUNCTION_2000) {
480                 char *upn;
481
482                 upn = ads_get_upn(r->in.ads, mem_ctx,
483                                   r->in.machine_name);
484                 if (upn) {
485                         salt = talloc_strdup(mem_ctx, upn);
486                         if (!salt) {
487                                 return false;
488                         }
489                 }
490         }
491
492         return kerberos_secrets_store_des_salt(salt);
493 }
494
495 /****************************************************************
496 ****************************************************************/
497
498 static ADS_STATUS libnet_join_post_processing_ads(TALLOC_CTX *mem_ctx,
499                                                   struct libnet_JoinCtx *r)
500 {
501         ADS_STATUS status;
502
503         status = libnet_join_set_machine_spn(mem_ctx, r);
504         if (!ADS_ERR_OK(status)) {
505                 libnet_join_set_error_string(mem_ctx, r,
506                         "failed to set machine spn: %s",
507                         ads_errstr(status));
508                 return status;
509         }
510
511         status = libnet_join_set_os_attributes(mem_ctx, r);
512         if (!ADS_ERR_OK(status)) {
513                 libnet_join_set_error_string(mem_ctx, r,
514                         "failed to set machine os attributes: %s",
515                         ads_errstr(status));
516                 return status;
517         }
518
519         status = libnet_join_set_machine_upn(mem_ctx, r);
520         if (!ADS_ERR_OK(status)) {
521                 libnet_join_set_error_string(mem_ctx, r,
522                         "failed to set machine upn: %s",
523                         ads_errstr(status));
524                 return status;
525         }
526
527         if (!libnet_join_derive_salting_principal(mem_ctx, r)) {
528                 return ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL);
529         }
530
531         if (!libnet_join_create_keytab(mem_ctx, r)) {
532                 libnet_join_set_error_string(mem_ctx, r,
533                         "failed to create kerberos keytab");
534                 return ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL);
535         }
536
537         return ADS_SUCCESS;
538 }
539 #endif /* WITH_ADS */
540
541 /****************************************************************
542 ****************************************************************/
543
544 static bool libnet_join_joindomain_store_secrets(TALLOC_CTX *mem_ctx,
545                                                  struct libnet_JoinCtx *r)
546 {
547         if (!secrets_store_domain_sid(r->out.netbios_domain_name,
548                                       r->out.domain_sid))
549         {
550                 return false;
551         }
552
553         if (!secrets_store_machine_password(r->in.machine_password,
554                                             r->out.netbios_domain_name,
555                                             SEC_CHAN_WKSTA))
556         {
557                 return false;
558         }
559
560         return true;
561 }
562
563 /****************************************************************
564 ****************************************************************/
565
566 static NTSTATUS libnet_join_joindomain_rpc(TALLOC_CTX *mem_ctx,
567                                            struct libnet_JoinCtx *r)
568 {
569         struct cli_state *cli = NULL;
570         struct rpc_pipe_client *pipe_hnd = NULL;
571         POLICY_HND sam_pol, domain_pol, user_pol, lsa_pol;
572         NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
573         char *acct_name;
574         const char *const_acct_name;
575         uint32 user_rid;
576         uint32 num_rids, *name_types, *user_rids;
577         uint32 flags = 0x3e8;
578         uint32 acb_info = ACB_WSTRUST;
579         uint32 fields_present;
580         uchar pwbuf[532];
581         SAM_USERINFO_CTR ctr;
582         SAM_USER_INFO_25 p25;
583         const int infolevel = 25;
584         struct MD5Context md5ctx;
585         uchar md5buffer[16];
586         DATA_BLOB digested_session_key;
587         uchar md4_trust_password[16];
588
589         if (!r->in.machine_password) {
590                 r->in.machine_password = talloc_strdup(mem_ctx, generate_random_str(DEFAULT_TRUST_ACCOUNT_PASSWORD_LENGTH));
591                 NT_STATUS_HAVE_NO_MEMORY(r->in.machine_password);
592         }
593
594         status = cli_full_connection(&cli, NULL,
595                                      r->in.dc_name,
596                                      NULL, 0,
597                                      "IPC$", "IPC",
598                                      r->in.admin_account,
599                                      NULL,
600                                      r->in.admin_password,
601                                      0,
602                                      Undefined, NULL);
603
604         if (!NT_STATUS_IS_OK(status)) {
605                 goto done;
606         }
607
608         pipe_hnd = cli_rpc_pipe_open_noauth(cli, PI_LSARPC, &status);
609         if (!pipe_hnd) {
610                 goto done;
611         }
612
613         status = rpccli_lsa_open_policy(pipe_hnd, mem_ctx, True,
614                                         SEC_RIGHTS_MAXIMUM_ALLOWED, &lsa_pol);
615         if (!NT_STATUS_IS_OK(status)) {
616                 goto done;
617         }
618
619         status = rpccli_lsa_query_info_policy2(pipe_hnd, mem_ctx, &lsa_pol,
620                                                12,
621                                                &r->out.netbios_domain_name,
622                                                &r->out.dns_domain_name,
623                                                NULL,
624                                                NULL,
625                                                &r->out.domain_sid);
626
627         if (NT_STATUS_IS_OK(status)) {
628                 r->out.domain_is_ad = true;
629         }
630
631         if (!NT_STATUS_IS_OK(status)) {
632                 status = rpccli_lsa_query_info_policy(pipe_hnd, mem_ctx, &lsa_pol,
633                                                       5,
634                                                       &r->out.netbios_domain_name,
635                                                       &r->out.domain_sid);
636                 if (!NT_STATUS_IS_OK(status)) {
637                         goto done;
638                 }
639         }
640
641         rpccli_lsa_Close(pipe_hnd, mem_ctx, &lsa_pol);
642         cli_rpc_pipe_close(pipe_hnd);
643
644         pipe_hnd = cli_rpc_pipe_open_noauth(cli, PI_SAMR, &status);
645         if (!pipe_hnd) {
646                 goto done;
647         }
648
649         status = rpccli_samr_connect(pipe_hnd, mem_ctx,
650                                      SEC_RIGHTS_MAXIMUM_ALLOWED, &sam_pol);
651         if (!NT_STATUS_IS_OK(status)) {
652                 goto done;
653         }
654
655         status = rpccli_samr_open_domain(pipe_hnd, mem_ctx, &sam_pol,
656                                          SEC_RIGHTS_MAXIMUM_ALLOWED,
657                                          r->out.domain_sid,
658                                          &domain_pol);
659         if (!NT_STATUS_IS_OK(status)) {
660                 goto done;
661         }
662
663         acct_name = talloc_asprintf(mem_ctx, "%s$", r->in.machine_name);
664         strlower_m(acct_name);
665         const_acct_name = acct_name;
666
667         if (r->in.join_flags & WKSSVC_JOIN_FLAGS_ACCOUNT_CREATE) {
668                 status = rpccli_samr_create_dom_user(pipe_hnd, mem_ctx,
669                                                      &domain_pol,
670                                                      acct_name, ACB_WSTRUST,
671                                                      0xe005000b, &user_pol,
672                                                      &user_rid);
673                 if (NT_STATUS_EQUAL(status, NT_STATUS_USER_EXISTS)) {
674                         if (!(r->in.join_flags &
675                               WKSSVC_JOIN_FLAGS_DOMAIN_JOIN_IF_JOINED)) {
676                                 goto done;
677                         }
678                 }
679
680                 if (NT_STATUS_IS_OK(status)) {
681                         rpccli_samr_close(pipe_hnd, mem_ctx, &user_pol);
682                 }
683         }
684
685         status = rpccli_samr_lookup_names(pipe_hnd, mem_ctx,
686                                           &domain_pol, flags, 1,
687                                           &const_acct_name,
688                                           &num_rids, &user_rids, &name_types);
689         if (!NT_STATUS_IS_OK(status)) {
690                 goto done;
691         }
692
693         if (name_types[0] != SID_NAME_USER) {
694                 status = NT_STATUS_INVALID_WORKSTATION;
695                 goto done;
696         }
697
698         user_rid = user_rids[0];
699
700         status = rpccli_samr_open_user(pipe_hnd, mem_ctx, &domain_pol,
701                                        SEC_RIGHTS_MAXIMUM_ALLOWED, user_rid,
702                                        &user_pol);
703         if (!NT_STATUS_IS_OK(status)) {
704                 goto done;
705         }
706
707         E_md4hash(r->in.machine_password, md4_trust_password);
708         encode_pw_buffer(pwbuf, r->in.machine_password, STR_UNICODE);
709
710         generate_random_buffer((uint8*)md5buffer, sizeof(md5buffer));
711         digested_session_key = data_blob_talloc(mem_ctx, 0, 16);
712
713         MD5Init(&md5ctx);
714         MD5Update(&md5ctx, md5buffer, sizeof(md5buffer));
715         MD5Update(&md5ctx, cli->user_session_key.data,
716                   cli->user_session_key.length);
717         MD5Final(digested_session_key.data, &md5ctx);
718
719         SamOEMhashBlob(pwbuf, sizeof(pwbuf), &digested_session_key);
720         memcpy(&pwbuf[516], md5buffer, sizeof(md5buffer));
721
722         acb_info |= ACB_PWNOEXP;
723         if (r->out.domain_is_ad) {
724 #if !defined(ENCTYPE_ARCFOUR_HMAC)
725                 acb_info |= ACB_USE_DES_KEY_ONLY;
726 #endif
727                 ;;
728         }
729
730         ZERO_STRUCT(ctr);
731         ZERO_STRUCT(p25);
732
733         fields_present = ACCT_NT_PWD_SET | ACCT_LM_PWD_SET | ACCT_FLAGS;
734         init_sam_user_info25P(&p25, fields_present, acb_info, (char *)pwbuf);
735
736         ctr.switch_value = infolevel;
737         ctr.info.id25    = &p25;
738
739         status = rpccli_samr_set_userinfo2(pipe_hnd, mem_ctx, &user_pol,
740                                            infolevel, &cli->user_session_key,
741                                            &ctr);
742         if (!NT_STATUS_IS_OK(status)) {
743                 goto done;
744         }
745
746         rpccli_samr_close(pipe_hnd, mem_ctx, &user_pol);
747         cli_rpc_pipe_close(pipe_hnd);
748
749         status = NT_STATUS_OK;
750  done:
751         if (cli) {
752                 cli_shutdown(cli);
753         }
754
755         return status;
756 }
757
758 /****************************************************************
759 ****************************************************************/
760
761 static bool libnet_join_unjoindomain_remove_secrets(TALLOC_CTX *mem_ctx,
762                                                     struct libnet_UnjoinCtx *r)
763 {
764         if (!secrets_delete_machine_password_ex(lp_workgroup())) {
765                 return false;
766         }
767
768         if (!secrets_delete_domain_sid(lp_workgroup())) {
769                 return false;
770         }
771
772         return true;
773 }
774
775 /****************************************************************
776 ****************************************************************/
777
778 static NTSTATUS libnet_join_unjoindomain_rpc(TALLOC_CTX *mem_ctx,
779                                              struct libnet_UnjoinCtx *r)
780 {
781         struct cli_state *cli = NULL;
782         struct rpc_pipe_client *pipe_hnd = NULL;
783         POLICY_HND sam_pol, domain_pol, user_pol;
784         NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
785         char *acct_name;
786         uint32 flags = 0x3e8;
787         const char *const_acct_name;
788         uint32 user_rid;
789         uint32 num_rids, *name_types, *user_rids;
790         SAM_USERINFO_CTR ctr, *qctr = NULL;
791         SAM_USER_INFO_16 p16;
792
793         status = cli_full_connection(&cli, NULL,
794                                      r->in.dc_name,
795                                      NULL, 0,
796                                      "IPC$", "IPC",
797                                      r->in.admin_account,
798                                      NULL,
799                                      r->in.admin_password,
800                                      0, Undefined, NULL);
801
802         if (!NT_STATUS_IS_OK(status)) {
803                 goto done;
804         }
805
806         pipe_hnd = cli_rpc_pipe_open_noauth(cli, PI_SAMR, &status);
807         if (!pipe_hnd) {
808                 goto done;
809         }
810
811         status = rpccli_samr_connect(pipe_hnd, mem_ctx,
812                                      SEC_RIGHTS_MAXIMUM_ALLOWED, &sam_pol);
813         if (!NT_STATUS_IS_OK(status)) {
814                 goto done;
815         }
816
817         status = rpccli_samr_open_domain(pipe_hnd, mem_ctx, &sam_pol,
818                                          SEC_RIGHTS_MAXIMUM_ALLOWED,
819                                          r->in.domain_sid,
820                                          &domain_pol);
821         if (!NT_STATUS_IS_OK(status)) {
822                 goto done;
823         }
824
825         acct_name = talloc_asprintf(mem_ctx, "%s$", r->in.machine_name);
826         strlower_m(acct_name);
827         const_acct_name = acct_name;
828
829         status = rpccli_samr_lookup_names(pipe_hnd, mem_ctx,
830                                           &domain_pol, flags, 1,
831                                           &const_acct_name,
832                                           &num_rids, &user_rids, &name_types);
833         if (!NT_STATUS_IS_OK(status)) {
834                 goto done;
835         }
836
837         if (name_types[0] != SID_NAME_USER) {
838                 status = NT_STATUS_INVALID_WORKSTATION;
839                 goto done;
840         }
841
842         user_rid = user_rids[0];
843
844         status = rpccli_samr_open_user(pipe_hnd, mem_ctx, &domain_pol,
845                                        SEC_RIGHTS_MAXIMUM_ALLOWED,
846                                        user_rid, &user_pol);
847         if (!NT_STATUS_IS_OK(status)) {
848                 goto done;
849         }
850
851         status = rpccli_samr_query_userinfo(pipe_hnd, mem_ctx,
852                                             &user_pol, 16, &qctr);
853         if (!NT_STATUS_IS_OK(status)) {
854                 rpccli_samr_close(pipe_hnd, mem_ctx, &user_pol);
855                 goto done;
856         }
857
858         ZERO_STRUCT(ctr);
859         ctr.switch_value = 16;
860         ctr.info.id16 = &p16;
861
862         p16.acb_info = qctr->info.id16->acb_info | ACB_DISABLED;
863
864         status = rpccli_samr_set_userinfo2(pipe_hnd, mem_ctx, &user_pol, 16,
865                                            &cli->user_session_key, &ctr);
866
867         rpccli_samr_close(pipe_hnd, mem_ctx, &user_pol);
868
869 done:
870         if (pipe_hnd) {
871                 rpccli_samr_close(pipe_hnd, mem_ctx, &domain_pol);
872                 rpccli_samr_close(pipe_hnd, mem_ctx, &sam_pol);
873                 cli_rpc_pipe_close(pipe_hnd);
874         }
875
876         if (cli) {
877                 cli_shutdown(cli);
878         }
879
880         return status;
881 }
882
883 /****************************************************************
884 ****************************************************************/
885
886 static WERROR do_join_modify_vals_config(struct libnet_JoinCtx *r)
887 {
888         WERROR werr;
889
890         if (!(r->in.join_flags & WKSSVC_JOIN_FLAGS_JOIN_TYPE)) {
891
892                 werr = libnet_conf_set_global_parameter("security", "user");
893                 W_ERROR_NOT_OK_RETURN(werr);
894
895                 werr = libnet_conf_set_global_parameter("workgroup",
896                                                         r->in.domain_name);
897                 return werr;
898         }
899
900         werr = libnet_conf_set_global_parameter("security", "domain");
901         W_ERROR_NOT_OK_RETURN(werr);
902
903         werr = libnet_conf_set_global_parameter("workgroup",
904                                                 r->out.netbios_domain_name);
905         W_ERROR_NOT_OK_RETURN(werr);
906
907         if (r->out.domain_is_ad) {
908                 werr = libnet_conf_set_global_parameter("security", "ads");
909                 W_ERROR_NOT_OK_RETURN(werr);
910
911                 werr = libnet_conf_set_global_parameter("realm",
912                                                         r->out.dns_domain_name);
913                 W_ERROR_NOT_OK_RETURN(werr);
914         }
915
916         return werr;
917 }
918
919 /****************************************************************
920 ****************************************************************/
921
922 static WERROR do_unjoin_modify_vals_config(struct libnet_UnjoinCtx *r)
923 {
924         WERROR werr = WERR_OK;
925
926         if (r->in.unjoin_flags & WKSSVC_JOIN_FLAGS_JOIN_TYPE) {
927
928                 werr = libnet_conf_set_global_parameter("security", "user");
929                 W_ERROR_NOT_OK_RETURN(werr);
930         }
931
932         libnet_conf_delete_parameter(GLOBAL_NAME, "realm");
933
934         return werr;
935 }
936
937 /****************************************************************
938 ****************************************************************/
939
940 static WERROR do_JoinConfig(struct libnet_JoinCtx *r)
941 {
942         WERROR werr;
943
944         if (!W_ERROR_IS_OK(r->out.result)) {
945                 return r->out.result;
946         }
947
948         if (!r->in.modify_config) {
949                 return WERR_OK;
950         }
951
952         werr = do_join_modify_vals_config(r);
953         if (!W_ERROR_IS_OK(werr)) {
954                 return werr;
955         }
956
957         r->out.modified_config = true;
958         r->out.result = werr;
959
960         return werr;
961 }
962
963 /****************************************************************
964 ****************************************************************/
965
966 static WERROR do_UnjoinConfig(struct libnet_UnjoinCtx *r)
967 {
968         WERROR werr;
969
970         if (!W_ERROR_IS_OK(r->out.result)) {
971                 return r->out.result;
972         }
973
974         if (!r->in.modify_config) {
975                 return WERR_OK;
976         }
977
978         werr = do_unjoin_modify_vals_config(r);
979         if (!W_ERROR_IS_OK(werr)) {
980                 return werr;
981         }
982
983         r->out.modified_config = true;
984         r->out.result = werr;
985
986         return werr;
987 }
988
989 /****************************************************************
990 ****************************************************************/
991
992 static int libnet_destroy_JoinCtx(struct libnet_JoinCtx *r)
993 {
994         if (r->in.ads) {
995                 ads_destroy(&r->in.ads);
996         }
997
998         return 0;
999 }
1000
1001 /****************************************************************
1002 ****************************************************************/
1003
1004 static int libnet_destroy_UnjoinCtx(struct libnet_UnjoinCtx *r)
1005 {
1006         if (r->in.ads) {
1007                 ads_destroy(&r->in.ads);
1008         }
1009
1010         return 0;
1011 }
1012
1013 /****************************************************************
1014 ****************************************************************/
1015
1016 WERROR libnet_init_JoinCtx(TALLOC_CTX *mem_ctx,
1017                            struct libnet_JoinCtx **r)
1018 {
1019         struct libnet_JoinCtx *ctx;
1020
1021         ctx = talloc_zero(mem_ctx, struct libnet_JoinCtx);
1022         if (!ctx) {
1023                 return WERR_NOMEM;
1024         }
1025
1026         talloc_set_destructor(ctx, libnet_destroy_JoinCtx);
1027
1028         ctx->in.machine_name = talloc_strdup(mem_ctx, global_myname());
1029         W_ERROR_HAVE_NO_MEMORY(ctx->in.machine_name);
1030
1031         *r = ctx;
1032
1033         return WERR_OK;
1034 }
1035
1036 /****************************************************************
1037 ****************************************************************/
1038
1039 WERROR libnet_init_UnjoinCtx(TALLOC_CTX *mem_ctx,
1040                              struct libnet_UnjoinCtx **r)
1041 {
1042         struct libnet_UnjoinCtx *ctx;
1043
1044         ctx = talloc_zero(mem_ctx, struct libnet_UnjoinCtx);
1045         if (!ctx) {
1046                 return WERR_NOMEM;
1047         }
1048
1049         talloc_set_destructor(ctx, libnet_destroy_UnjoinCtx);
1050
1051         ctx->in.machine_name = talloc_strdup(mem_ctx, global_myname());
1052         W_ERROR_HAVE_NO_MEMORY(ctx->in.machine_name);
1053
1054         *r = ctx;
1055
1056         return WERR_OK;
1057 }
1058
1059 /****************************************************************
1060 ****************************************************************/
1061
1062 static WERROR libnet_DomainJoin(TALLOC_CTX *mem_ctx,
1063                                 struct libnet_JoinCtx *r)
1064 {
1065         NTSTATUS status;
1066 #ifdef WITH_ADS
1067         ADS_STATUS ads_status;
1068 #endif /* WITH_ADS */
1069
1070         if (!r->in.dc_name) {
1071                 struct DS_DOMAIN_CONTROLLER_INFO *info;
1072                 status = dsgetdcname(mem_ctx,
1073                                      NULL,
1074                                      r->in.domain_name,
1075                                      NULL,
1076                                      NULL,
1077                                      DS_DIRECTORY_SERVICE_REQUIRED |
1078                                      DS_WRITABLE_REQUIRED |
1079                                      DS_RETURN_DNS_NAME,
1080                                      &info);
1081                 if (!NT_STATUS_IS_OK(status)) {
1082                         libnet_join_set_error_string(mem_ctx, r,
1083                                 "failed to find DC: %s",
1084                                 nt_errstr(status));
1085                         return WERR_DOMAIN_CONTROLLER_NOT_FOUND;
1086                 }
1087
1088                 r->in.dc_name = talloc_strdup(mem_ctx,
1089                                               info->domain_controller_name);
1090                 W_ERROR_HAVE_NO_MEMORY(r->in.dc_name);
1091         }
1092
1093 #ifdef WITH_ADS
1094         if (r->in.account_ou) {
1095
1096                 ads_status = libnet_join_connect_ads(mem_ctx, r);
1097                 if (!ADS_ERR_OK(ads_status)) {
1098                         return WERR_DEFAULT_JOIN_REQUIRED;
1099                 }
1100
1101                 ads_status = libnet_join_precreate_machine_acct(mem_ctx, r);
1102                 if (!ADS_ERR_OK(ads_status)) {
1103                         libnet_join_set_error_string(mem_ctx, r,
1104                                 "failed to precreate account in ou %s: %s",
1105                                 r->in.account_ou,
1106                                 ads_errstr(ads_status));
1107                         return WERR_DEFAULT_JOIN_REQUIRED;
1108                 }
1109
1110                 r->in.join_flags &= ~WKSSVC_JOIN_FLAGS_ACCOUNT_CREATE;
1111         }
1112 #endif /* WITH_ADS */
1113
1114         status = libnet_join_joindomain_rpc(mem_ctx, r);
1115         if (!NT_STATUS_IS_OK(status)) {
1116                 libnet_join_set_error_string(mem_ctx, r,
1117                         "failed to join domain over rpc: %s",
1118                         nt_errstr(status));
1119                 if (NT_STATUS_EQUAL(status, NT_STATUS_USER_EXISTS)) {
1120                         return WERR_SETUP_ALREADY_JOINED;
1121                 }
1122                 return ntstatus_to_werror(status);
1123         }
1124
1125         if (!libnet_join_joindomain_store_secrets(mem_ctx, r)) {
1126                 return WERR_SETUP_NOT_JOINED;
1127         }
1128
1129 #ifdef WITH_ADS
1130         if (r->out.domain_is_ad) {
1131                 ads_status  = libnet_join_post_processing_ads(mem_ctx, r);
1132                 if (!ADS_ERR_OK(ads_status)) {
1133                         return WERR_GENERAL_FAILURE;
1134                 }
1135         }
1136 #endif /* WITH_ADS */
1137
1138         return WERR_OK;
1139 }
1140
1141 /****************************************************************
1142 ****************************************************************/
1143
1144 WERROR libnet_Join(TALLOC_CTX *mem_ctx,
1145                    struct libnet_JoinCtx *r)
1146 {
1147         WERROR werr;
1148
1149         if (!r->in.domain_name) {
1150                 return WERR_INVALID_PARAM;
1151         }
1152
1153         if (r->in.modify_config && !lp_include_registry_globals()) {
1154                 return WERR_NOT_SUPPORTED;
1155         }
1156
1157         if (IS_DC) {
1158                 return WERR_SETUP_DOMAIN_CONTROLLER;
1159         }
1160
1161         if (r->in.join_flags & WKSSVC_JOIN_FLAGS_JOIN_TYPE) {
1162                 werr = libnet_DomainJoin(mem_ctx, r);
1163                 if (!W_ERROR_IS_OK(werr)) {
1164                         return werr;
1165                 }
1166         }
1167
1168         werr = do_JoinConfig(r);
1169         if (!W_ERROR_IS_OK(werr)) {
1170                 return werr;
1171         }
1172
1173         return werr;
1174 }
1175
1176 /****************************************************************
1177 ****************************************************************/
1178
1179 static WERROR libnet_DomainUnjoin(TALLOC_CTX *mem_ctx,
1180                                   struct libnet_UnjoinCtx *r)
1181 {
1182         NTSTATUS status;
1183
1184         status = libnet_join_unjoindomain_rpc(mem_ctx, r);
1185         if (!NT_STATUS_IS_OK(status)) {
1186                 libnet_unjoin_set_error_string(mem_ctx, r,
1187                         "failed to unjoin domain: %s",
1188                         nt_errstr(status));
1189                 if (NT_STATUS_EQUAL(status, NT_STATUS_NO_SUCH_USER)) {
1190                         return WERR_SETUP_NOT_JOINED;
1191                 }
1192                 return ntstatus_to_werror(status);
1193         }
1194
1195 #ifdef WITH_ADS
1196         if (r->in.unjoin_flags & WKSSVC_JOIN_FLAGS_ACCOUNT_DELETE) {
1197                 ADS_STATUS ads_status;
1198                 libnet_unjoin_connect_ads(mem_ctx, r);
1199                 ads_status = libnet_unjoin_remove_machine_acct(mem_ctx, r);
1200                 if (!ADS_ERR_OK(ads_status)) {
1201                         libnet_unjoin_set_error_string(mem_ctx, r,
1202                                 "failed to remove machine account from AD: %s",
1203                                 ads_errstr(ads_status));
1204                 }
1205         }
1206 #endif /* WITH_ADS */
1207
1208         libnet_join_unjoindomain_remove_secrets(mem_ctx, r);
1209
1210         return WERR_OK;
1211 }
1212
1213 /****************************************************************
1214 ****************************************************************/
1215
1216 WERROR libnet_Unjoin(TALLOC_CTX *mem_ctx,
1217                      struct libnet_UnjoinCtx *r)
1218 {
1219         WERROR werr;
1220
1221         if (r->in.modify_config && !lp_include_registry_globals()) {
1222                 return WERR_NOT_SUPPORTED;
1223         }
1224
1225         if (r->in.unjoin_flags & WKSSVC_JOIN_FLAGS_JOIN_TYPE) {
1226                 werr = libnet_DomainUnjoin(mem_ctx, r);
1227                 if (!W_ERROR_IS_OK(werr)) {
1228                         do_UnjoinConfig(r);
1229                         return werr;
1230                 }
1231         }
1232
1233         werr = do_UnjoinConfig(r);
1234         if (!W_ERROR_IS_OK(werr)) {
1235                 return werr;
1236         }
1237
1238         return werr;
1239 }