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