Replace direct deletion of registry value by use of libnet_smbconf_delparm().
[ira/wip.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
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_modify_val_config(struct registry_key *key,
335                                    const char *val_name,
336                                    const char *val_data)
337 {
338         struct registry_value val;
339
340         ZERO_STRUCT(val);
341
342         val.type = REG_SZ;
343         val.v.sz.str = CONST_DISCARD(char *, val_data);
344         val.v.sz.len = strlen(val_data) + 1;
345
346         return reg_setvalue(key, val_name, &val);
347 }
348
349 static WERROR do_join_modify_vals_config(TALLOC_CTX *mem_ctx,
350                                          struct libnet_JoinCtx *r,
351                                          struct registry_key *key)
352 {
353         WERROR werr;
354         bool is_ad = false;
355
356         if (!(r->in.join_flags & WKSSVC_JOIN_FLAGS_JOIN_TYPE)) {
357
358                 werr = do_modify_val_config(key, "security", "user");
359                 W_ERROR_NOT_OK_RETURN(werr);
360
361                 werr = do_modify_val_config(key, "workgroup",
362                                             r->in.domain_name);
363                 return werr;
364         }
365
366         if (r->out.dns_domain_name) {
367                 is_ad = true;
368         }
369
370         werr = do_modify_val_config(key, "security", "domain");
371         W_ERROR_NOT_OK_RETURN(werr);
372
373         werr = do_modify_val_config(key, "workgroup",
374                                     r->out.netbios_domain_name);
375         W_ERROR_NOT_OK_RETURN(werr);
376
377         if (is_ad) {
378                 werr = do_modify_val_config(key, "security", "ads");
379                 W_ERROR_NOT_OK_RETURN(werr);
380
381                 werr = do_modify_val_config(key, "realm",
382                                             r->out.dns_domain_name);
383                 W_ERROR_NOT_OK_RETURN(werr);
384         }
385
386         return werr;
387 }
388
389 static WERROR do_unjoin_modify_vals_config(TALLOC_CTX *mem_ctx,
390                                            struct libnet_UnjoinCtx *r,
391                                            struct registry_key *key)
392 {
393         WERROR werr = WERR_OK;
394
395         if (r->in.unjoin_flags & WKSSVC_JOIN_FLAGS_JOIN_TYPE) {
396
397                 werr = do_modify_val_config(key, "security", "user");
398                 W_ERROR_NOT_OK_RETURN(werr);
399         }
400
401         werr = libnet_smbconf_delparm(mem_ctx, "GLOBAL", "realm");
402
403         return werr;
404 }
405
406
407 static WERROR do_JoinConfig(TALLOC_CTX *mem_ctx,
408                             struct libnet_JoinCtx *r)
409 {
410         WERROR werr;
411         struct registry_key *key = NULL;
412
413         if (!W_ERROR_IS_OK(r->out.result)) {
414                 return r->out.result;
415         }
416
417         if (!r->in.modify_config) {
418                 return WERR_OK;
419         }
420
421         if (!registry_init_regdb()) {
422                 return WERR_REG_IO_FAILURE;
423         }
424
425         if (!libnet_smbconf_key_exists(mem_ctx, GLOBAL_NAME)) {
426                 werr = libnet_reg_createkey_internal(mem_ctx,
427                                                      GLOBAL_NAME, &key);
428         } else {
429                 werr = libnet_smbconf_open_path(mem_ctx,
430                                                 GLOBAL_NAME,
431                                                 REG_KEY_WRITE, &key);
432         }
433         if (!W_ERROR_IS_OK(werr)) {
434                 return werr;
435         }
436
437         werr = do_join_modify_vals_config(mem_ctx, r, key);
438         if (!W_ERROR_IS_OK(werr)) {
439                 return werr;
440         }
441
442         r->out.modified_config = true;
443         r->out.result = werr;
444
445         return werr;
446 }
447
448 static WERROR do_UnjoinConfig(TALLOC_CTX *mem_ctx,
449                               struct libnet_UnjoinCtx *r)
450 {
451         WERROR werr;
452         struct registry_key *key = NULL;
453
454         if (!W_ERROR_IS_OK(r->out.result)) {
455                 return r->out.result;
456         }
457
458         if (!r->in.modify_config) {
459                 return WERR_OK;
460         }
461
462         if (!registry_init_regdb()) {
463                 return WERR_REG_IO_FAILURE;
464         }
465
466         if (!libnet_smbconf_key_exists(mem_ctx, GLOBAL_NAME)) {
467                 werr = libnet_reg_createkey_internal(mem_ctx,
468                                                      GLOBAL_NAME, &key);
469         } else {
470                 werr = libnet_smbconf_open_path(mem_ctx,
471                                                 GLOBAL_NAME,
472                                                 REG_KEY_WRITE, &key);
473         }
474         if (!W_ERROR_IS_OK(werr)) {
475                 return werr;
476         }
477
478         werr = do_unjoin_modify_vals_config(mem_ctx, r, key);
479         if (!W_ERROR_IS_OK(werr)) {
480                 return werr;
481         }
482
483         r->out.modified_config = true;
484         r->out.result = werr;
485
486         return werr;
487 }
488
489 WERROR libnet_init_JoinCtx(TALLOC_CTX *mem_ctx,
490                            struct libnet_JoinCtx **r)
491 {
492         struct libnet_JoinCtx *ctx;
493
494         ctx = talloc_zero(mem_ctx, struct libnet_JoinCtx);
495         if (!ctx) {
496                 return WERR_NOMEM;
497         }
498
499         *r = ctx;
500
501         return WERR_OK;
502 }
503
504 WERROR libnet_init_UnjoinCtx(TALLOC_CTX *mem_ctx,
505                              struct libnet_UnjoinCtx **r)
506 {
507         struct libnet_UnjoinCtx *ctx;
508
509         ctx = talloc_zero(mem_ctx, struct libnet_UnjoinCtx);
510         if (!ctx) {
511                 return WERR_NOMEM;
512         }
513
514         *r = ctx;
515
516         return WERR_OK;
517 }
518
519 WERROR libnet_Join(TALLOC_CTX *mem_ctx,
520                    struct libnet_JoinCtx *r)
521 {
522         WERROR werr;
523         NTSTATUS status;
524
525         if (!r->in.domain_name) {
526                 return WERR_INVALID_PARAM;
527         }
528
529         if (r->in.modify_config && !lp_include_registry_globals()) {
530                 return WERR_NOT_SUPPORTED;
531         }
532
533         if (IS_DC) {
534                 return WERR_SETUP_DOMAIN_CONTROLLER;
535         }
536
537         if (r->in.join_flags & WKSSVC_JOIN_FLAGS_JOIN_TYPE) {
538
539                 status = do_DomainJoin(mem_ctx, r);
540                 if (!NT_STATUS_IS_OK(status)) {
541                         if (NT_STATUS_EQUAL(status, NT_STATUS_USER_EXISTS)) {
542                                 return WERR_SETUP_ALREADY_JOINED;
543                         }
544                         return ntstatus_to_werror(status);
545                 }
546         }
547
548         werr = do_JoinConfig(mem_ctx, r);
549         if (!W_ERROR_IS_OK(werr)) {
550                 return werr;
551         }
552
553         return werr;
554 }
555
556 WERROR libnet_Unjoin(TALLOC_CTX *mem_ctx,
557                      struct libnet_UnjoinCtx *r)
558 {
559         WERROR werr;
560         NTSTATUS status;
561
562         if (r->in.modify_config && !lp_include_registry_globals()) {
563                 return WERR_NOT_SUPPORTED;
564         }
565
566         if (r->in.unjoin_flags & WKSSVC_JOIN_FLAGS_JOIN_TYPE) {
567
568                 status = do_DomainUnjoin(mem_ctx, r);
569                 if (!NT_STATUS_IS_OK(status)) {
570                         if (NT_STATUS_EQUAL(status, NT_STATUS_NO_SUCH_USER)) {
571                                 return WERR_SETUP_NOT_JOINED;
572                         }
573                         return ntstatus_to_werror(status);
574                 }
575         }
576
577         werr = do_UnjoinConfig(mem_ctx, r);
578         if (!W_ERROR_IS_OK(werr)) {
579                 return werr;
580         }
581
582         return werr;
583 }