6edcdb89457d8fd6505d5bf84c2f4b3e4ad1cc98
[ira/wip.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
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 static NTSTATUS do_DomainJoin(TALLOC_CTX *mem_ctx,
26                               struct libnet_JoinCtx *r)
27 {
28         struct cli_state *cli = NULL;
29         struct rpc_pipe_client *pipe_hnd = NULL;
30         const char *password = NULL;
31         POLICY_HND sam_pol, domain_pol, user_pol, lsa_pol;
32         NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
33         char *acct_name;
34         const char *const_acct_name;
35         uint32 user_rid;
36         uint32 num_rids, *name_types, *user_rids;
37         uint32 flags = 0x3e8;
38         uint32 acb_info = ACB_WSTRUST;
39         uint32 fields_present;
40         uchar pwbuf[532];
41         SAM_USERINFO_CTR ctr;
42         SAM_USER_INFO_25 p25;
43         const int infolevel = 25;
44         struct MD5Context md5ctx;
45         uchar md5buffer[16];
46         DATA_BLOB digested_session_key;
47         uchar md4_trust_password[16];
48
49         password = talloc_strdup(mem_ctx,
50                 generate_random_str(DEFAULT_TRUST_ACCOUNT_PASSWORD_LENGTH));
51         NT_STATUS_HAVE_NO_MEMORY(password);
52
53         status = cli_full_connection(&cli, NULL, r->in.server_name,
54                                      NULL, 0,
55                                      "IPC$", "IPC",
56                                      r->in.admin_account,
57                                      NULL, //r->in.domain_name,
58                                      r->in.password,
59                                      0, Undefined, NULL);
60
61         if (!NT_STATUS_IS_OK(status)) {
62                 goto done;
63         }
64
65         pipe_hnd = cli_rpc_pipe_open_noauth(cli, PI_LSARPC, &status);
66         if (!pipe_hnd) {
67                 goto done;
68         }
69
70         status = rpccli_lsa_open_policy(pipe_hnd, mem_ctx, True,
71                                         SEC_RIGHTS_MAXIMUM_ALLOWED, &lsa_pol);
72         if (!NT_STATUS_IS_OK(status)) {
73                 goto done;
74         }
75
76         status = rpccli_lsa_query_info_policy2(pipe_hnd, mem_ctx, &lsa_pol,
77                                                12,
78                                                &r->out.netbios_domain_name,
79                                                &r->out.dns_domain_name,
80                                                NULL,
81                                                NULL,
82                                                &r->out.domain_sid);
83
84         if (!NT_STATUS_IS_OK(status)) {
85                 status = rpccli_lsa_query_info_policy(pipe_hnd, mem_ctx, &lsa_pol,
86                                                       5,
87                                                       &r->out.netbios_domain_name,
88                                                       &r->out.domain_sid);
89                 if (!NT_STATUS_IS_OK(status)) {
90                         goto done;
91                 }
92         }
93
94         rpccli_lsa_Close(pipe_hnd, mem_ctx, &lsa_pol);
95         cli_rpc_pipe_close(pipe_hnd);
96
97         pipe_hnd = cli_rpc_pipe_open_noauth(cli, PI_SAMR, &status);
98         if (!pipe_hnd) {
99                 goto done;
100         }
101
102         status = rpccli_samr_connect(pipe_hnd, mem_ctx,
103                                      SEC_RIGHTS_MAXIMUM_ALLOWED, &sam_pol);
104         if (!NT_STATUS_IS_OK(status)) {
105                 goto done;
106         }
107
108         status = rpccli_samr_open_domain(pipe_hnd, mem_ctx, &sam_pol,
109                                          SEC_RIGHTS_MAXIMUM_ALLOWED,
110                                          r->out.domain_sid,
111                                          &domain_pol);
112         if (!NT_STATUS_IS_OK(status)) {
113                 goto done;
114         }
115
116         acct_name = talloc_asprintf(mem_ctx, "%s$", global_myname());
117         strlower_m(acct_name);
118         const_acct_name = acct_name;
119
120         status = rpccli_samr_create_dom_user(pipe_hnd, mem_ctx, &domain_pol,
121                                              acct_name, ACB_WSTRUST,
122                                              0xe005000b, &user_pol, &user_rid);
123         if (NT_STATUS_EQUAL(status, NT_STATUS_USER_EXISTS)) {
124                 if (!(r->in.join_flags & WKSSVC_JOIN_FLAGS_DOMAIN_JOIN_IF_JOINED)) {
125                         goto done;
126                 }
127         }
128
129         if (NT_STATUS_IS_OK(status)) {
130                 rpccli_samr_close(pipe_hnd, mem_ctx, &user_pol);
131         }
132
133         status = rpccli_samr_lookup_names(pipe_hnd, mem_ctx,
134                                           &domain_pol, flags, 1,
135                                           &const_acct_name,
136                                           &num_rids, &user_rids, &name_types);
137         if (!NT_STATUS_IS_OK(status)) {
138                 goto done;
139         }
140
141         if (name_types[0] != SID_NAME_USER) {
142                 status = NT_STATUS_INVALID_WORKSTATION;
143                 goto done;
144         }
145
146         user_rid = user_rids[0];
147
148         status = rpccli_samr_open_user(pipe_hnd, mem_ctx, &domain_pol,
149                                        SEC_RIGHTS_MAXIMUM_ALLOWED, user_rid,
150                                        &user_pol);
151         if (!NT_STATUS_IS_OK(status)) {
152                 goto done;
153         }
154
155         E_md4hash(r->in.password, md4_trust_password);
156         encode_pw_buffer(pwbuf, r->in.password, STR_UNICODE);
157
158         generate_random_buffer((uint8*)md5buffer, sizeof(md5buffer));
159         digested_session_key = data_blob_talloc(mem_ctx, 0, 16);
160
161         MD5Init(&md5ctx);
162         MD5Update(&md5ctx, md5buffer, sizeof(md5buffer));
163         MD5Update(&md5ctx, cli->user_session_key.data, cli->user_session_key.length);
164         MD5Final(digested_session_key.data, &md5ctx);
165
166         SamOEMhashBlob(pwbuf, sizeof(pwbuf), &digested_session_key);
167         memcpy(&pwbuf[516], md5buffer, sizeof(md5buffer));
168
169         acb_info |= ACB_PWNOEXP;
170 #if 0
171         if ( dom_type == ND_TYPE_AD ) {
172 #if !defined(ENCTYPE_ARCFOUR_HMAC)
173                 acb_info |= ACB_USE_DES_KEY_ONLY;
174 #endif
175                 ;;
176         }
177 #endif
178         ZERO_STRUCT(ctr);
179         ZERO_STRUCT(p25);
180
181         fields_present = ACCT_NT_PWD_SET | ACCT_LM_PWD_SET | ACCT_FLAGS;
182         init_sam_user_info25P(&p25, fields_present, acb_info, (char *)pwbuf);
183
184         ctr.switch_value = infolevel;
185         ctr.info.id25    = &p25;
186
187         status = rpccli_samr_set_userinfo2(pipe_hnd, mem_ctx, &user_pol,
188                                            infolevel, &cli->user_session_key,
189                                            &ctr);
190         if (!NT_STATUS_IS_OK(status)) {
191                 goto done;
192         }
193
194         rpccli_samr_close(pipe_hnd, mem_ctx, &user_pol);
195         cli_rpc_pipe_close(pipe_hnd);
196
197         if (!secrets_store_domain_sid(r->out.netbios_domain_name,
198                                       r->out.domain_sid))
199         {
200                 status = NT_STATUS_INTERNAL_DB_ERROR;
201                 goto done;
202         }
203
204         if (!secrets_store_machine_password(password,
205                                             r->out.netbios_domain_name,
206                                             SEC_CHAN_WKSTA))
207         {
208                 status = NT_STATUS_INTERNAL_DB_ERROR;
209                 goto done;
210         }
211
212         status = NT_STATUS_OK;
213  done:
214         if (cli) {
215                 cli_shutdown(cli);
216         }
217
218         return status;
219 }
220
221 static NTSTATUS do_DomainUnjoin(TALLOC_CTX *mem_ctx,
222                                 struct libnet_UnjoinCtx *r)
223 {
224         struct cli_state *cli = NULL;
225         struct rpc_pipe_client *pipe_hnd = NULL;
226         POLICY_HND sam_pol, domain_pol, user_pol;
227         NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
228         char *acct_name;
229         uint32 flags = 0x3e8;
230         const char *const_acct_name;
231         uint32 user_rid;
232         uint32 num_rids, *name_types, *user_rids;
233         SAM_USERINFO_CTR ctr, *qctr = NULL;
234         SAM_USER_INFO_16 p16;
235
236         status = cli_full_connection(&cli, NULL, r->in.server_name,
237                                      NULL, 0,
238                                      "IPC$", "IPC",
239                                      r->in.admin_account,
240                                      NULL, //r->in.domain_name,
241                                      r->in.password,
242                                      0, Undefined, NULL);
243
244         if (!NT_STATUS_IS_OK(status)) {
245                 goto done;
246         }
247
248         pipe_hnd = cli_rpc_pipe_open_noauth(cli, PI_SAMR, &status);
249         if (!pipe_hnd) {
250                 goto done;
251         }
252
253         status = rpccli_samr_connect(pipe_hnd, mem_ctx,
254                                      SEC_RIGHTS_MAXIMUM_ALLOWED, &sam_pol);
255         if (!NT_STATUS_IS_OK(status)) {
256                 goto done;
257         }
258
259         status = rpccli_samr_open_domain(pipe_hnd, mem_ctx, &sam_pol,
260                                          SEC_RIGHTS_MAXIMUM_ALLOWED,
261                                          r->in.domain_sid,
262                                          &domain_pol);
263         if (!NT_STATUS_IS_OK(status)) {
264                 goto done;
265         }
266
267         acct_name = talloc_asprintf(mem_ctx, "%s$", global_myname());
268         strlower_m(acct_name);
269         const_acct_name = acct_name;
270
271         status = rpccli_samr_lookup_names(pipe_hnd, mem_ctx,
272                                           &domain_pol, flags, 1,
273                                           &const_acct_name,
274                                           &num_rids, &user_rids, &name_types);
275         if (!NT_STATUS_IS_OK(status)) {
276                 goto done;
277         }
278
279         if (name_types[0] != SID_NAME_USER) {
280                 status = NT_STATUS_INVALID_WORKSTATION;
281                 goto done;
282         }
283
284         user_rid = user_rids[0];
285
286         status = rpccli_samr_open_user(pipe_hnd, mem_ctx, &domain_pol,
287                                        SEC_RIGHTS_MAXIMUM_ALLOWED,
288                                        user_rid, &user_pol);
289         if (!NT_STATUS_IS_OK(status)) {
290                 goto done;
291         }
292
293         status = rpccli_samr_query_userinfo(pipe_hnd, mem_ctx,
294                                             &user_pol, 16, &qctr);
295         if (!NT_STATUS_IS_OK(status)) {
296                 rpccli_samr_close(pipe_hnd, mem_ctx, &user_pol);
297                 goto done;
298         }
299
300         ZERO_STRUCT(ctr);
301         ctr.switch_value = 16;
302         ctr.info.id16 = &p16;
303
304         p16.acb_info = qctr->info.id16->acb_info | ACB_DISABLED;
305
306         status = rpccli_samr_set_userinfo2(pipe_hnd, mem_ctx, &user_pol, 16,
307                                            &cli->user_session_key, &ctr);
308
309         rpccli_samr_close(pipe_hnd, mem_ctx, &user_pol);
310
311         if (!secrets_delete_machine_password_ex(lp_workgroup())) {
312                 status = NT_STATUS_INTERNAL_DB_ERROR;
313                 goto done;
314         }
315
316         if (!secrets_delete_domain_sid(lp_workgroup())) {
317                 status = NT_STATUS_INTERNAL_DB_ERROR;
318                 goto done;
319         }
320
321 done:
322         rpccli_samr_close(pipe_hnd, mem_ctx, &domain_pol);
323         rpccli_samr_close(pipe_hnd, mem_ctx, &sam_pol);
324
325         cli_rpc_pipe_close(pipe_hnd);
326
327         if (cli) {
328                 cli_shutdown(cli);
329         }
330
331         return status;
332 }
333
334 static WERROR do_join_modify_vals_config(struct libnet_JoinCtx *r)
335 {
336         WERROR werr;
337         bool is_ad = false;
338
339         if (!(r->in.join_flags & WKSSVC_JOIN_FLAGS_JOIN_TYPE)) {
340
341                 werr = libnet_smbconf_set_global_param("security", "user");
342                 W_ERROR_NOT_OK_RETURN(werr);
343
344                 werr = libnet_smbconf_set_global_param("workgroup",
345                                                        r->in.domain_name);
346                 return werr;
347         }
348
349         if (r->out.dns_domain_name) {
350                 is_ad = true;
351         }
352
353         werr = libnet_smbconf_set_global_param("security", "domain");
354         W_ERROR_NOT_OK_RETURN(werr);
355
356         werr = libnet_smbconf_set_global_param("workgroup",
357                                                r->out.netbios_domain_name);
358         W_ERROR_NOT_OK_RETURN(werr);
359
360         if (is_ad) {
361                 werr = libnet_smbconf_set_global_param("security", "ads");
362                 W_ERROR_NOT_OK_RETURN(werr);
363
364                 werr = libnet_smbconf_set_global_param("realm",
365                                                        r->out.dns_domain_name);
366                 W_ERROR_NOT_OK_RETURN(werr);
367         }
368
369         return werr;
370 }
371
372 static WERROR do_unjoin_modify_vals_config(struct libnet_UnjoinCtx *r)
373 {
374         WERROR werr = WERR_OK;
375
376         if (r->in.unjoin_flags & WKSSVC_JOIN_FLAGS_JOIN_TYPE) {
377
378                 werr = libnet_smbconf_set_global_param("security", "user");
379                 W_ERROR_NOT_OK_RETURN(werr);
380         }
381
382         werr = libnet_smbconf_delparm("GLOBAL", "realm");
383
384         return werr;
385 }
386
387
388 static WERROR do_JoinConfig(struct libnet_JoinCtx *r)
389 {
390         WERROR werr;
391
392         if (!W_ERROR_IS_OK(r->out.result)) {
393                 return r->out.result;
394         }
395
396         if (!r->in.modify_config) {
397                 return WERR_OK;
398         }
399
400         werr = do_join_modify_vals_config(r);
401         if (!W_ERROR_IS_OK(werr)) {
402                 return werr;
403         }
404
405         r->out.modified_config = true;
406         r->out.result = werr;
407
408         return werr;
409 }
410
411 static WERROR do_UnjoinConfig(struct libnet_UnjoinCtx *r)
412 {
413         WERROR werr;
414
415         if (!W_ERROR_IS_OK(r->out.result)) {
416                 return r->out.result;
417         }
418
419         if (!r->in.modify_config) {
420                 return WERR_OK;
421         }
422
423         werr = do_unjoin_modify_vals_config(r);
424         if (!W_ERROR_IS_OK(werr)) {
425                 return werr;
426         }
427
428         r->out.modified_config = true;
429         r->out.result = werr;
430
431         return werr;
432 }
433
434 WERROR libnet_init_JoinCtx(TALLOC_CTX *mem_ctx,
435                            struct libnet_JoinCtx **r)
436 {
437         struct libnet_JoinCtx *ctx;
438
439         ctx = talloc_zero(mem_ctx, struct libnet_JoinCtx);
440         if (!ctx) {
441                 return WERR_NOMEM;
442         }
443
444         *r = ctx;
445
446         return WERR_OK;
447 }
448
449 WERROR libnet_init_UnjoinCtx(TALLOC_CTX *mem_ctx,
450                              struct libnet_UnjoinCtx **r)
451 {
452         struct libnet_UnjoinCtx *ctx;
453
454         ctx = talloc_zero(mem_ctx, struct libnet_UnjoinCtx);
455         if (!ctx) {
456                 return WERR_NOMEM;
457         }
458
459         *r = ctx;
460
461         return WERR_OK;
462 }
463
464 WERROR libnet_Join(TALLOC_CTX *mem_ctx,
465                    struct libnet_JoinCtx *r)
466 {
467         WERROR werr;
468         NTSTATUS status;
469
470         if (!r->in.domain_name) {
471                 return WERR_INVALID_PARAM;
472         }
473
474         if (r->in.modify_config && !lp_include_registry_globals()) {
475                 return WERR_NOT_SUPPORTED;
476         }
477
478         if (IS_DC) {
479                 return WERR_SETUP_DOMAIN_CONTROLLER;
480         }
481
482         if (r->in.join_flags & WKSSVC_JOIN_FLAGS_JOIN_TYPE) {
483
484                 status = do_DomainJoin(mem_ctx, r);
485                 if (!NT_STATUS_IS_OK(status)) {
486                         if (NT_STATUS_EQUAL(status, NT_STATUS_USER_EXISTS)) {
487                                 return WERR_SETUP_ALREADY_JOINED;
488                         }
489                         return ntstatus_to_werror(status);
490                 }
491         }
492
493         werr = do_JoinConfig(r);
494         if (!W_ERROR_IS_OK(werr)) {
495                 return werr;
496         }
497
498         return werr;
499 }
500
501 WERROR libnet_Unjoin(TALLOC_CTX *mem_ctx,
502                      struct libnet_UnjoinCtx *r)
503 {
504         WERROR werr;
505         NTSTATUS status;
506
507         if (r->in.modify_config && !lp_include_registry_globals()) {
508                 return WERR_NOT_SUPPORTED;
509         }
510
511         if (r->in.unjoin_flags & WKSSVC_JOIN_FLAGS_JOIN_TYPE) {
512
513                 status = do_DomainUnjoin(mem_ctx, r);
514                 if (!NT_STATUS_IS_OK(status)) {
515                         if (NT_STATUS_EQUAL(status, NT_STATUS_NO_SUCH_USER)) {
516                                 return WERR_SETUP_NOT_JOINED;
517                         }
518                         return ntstatus_to_werror(status);
519                 }
520         }
521
522         werr = do_UnjoinConfig(r);
523         if (!W_ERROR_IS_OK(werr)) {
524                 return werr;
525         }
526
527         return werr;
528 }