ctdb-scripts: Support storing statd-callout state in cluster filesystem
[samba4-gss.git] / source4 / libnet / libnet_join.c
blobcc8a4f0e9d1e75f47b3e2c3c24189a6965619848
1 /*
2 Unix SMB/CIFS implementation.
4 Copyright (C) Stefan Metzmacher 2004
5 Copyright (C) Andrew Bartlett <abartlet@samba.org> 2005
6 Copyright (C) Brad Henry 2005
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 3 of the License, or
11 (at your option) any later version.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program. If not, see <http://www.gnu.org/licenses/>.
22 #include "includes.h"
23 #include "libnet/libnet.h"
24 #include "libnet/libnet_join_proto.h"
25 #include "librpc/gen_ndr/ndr_drsuapi_c.h"
26 #include <ldb.h>
27 #include <ldb_errors.h>
28 #include "dsdb/samdb/samdb.h"
29 #include "ldb_wrap.h"
30 #include "libcli/security/security.h"
31 #include "auth/credentials/credentials.h"
32 #include "auth/credentials/credentials_krb5.h"
33 #include "librpc/gen_ndr/ndr_samr_c.h"
34 #include "param/param.h"
35 #include "param/provision.h"
36 #include "system/kerberos.h"
37 #include "auth/kerberos/kerberos.h"
40 * complete a domain join, when joining to a AD domain:
41 * 1.) connect and bind to the DRSUAPI pipe
42 * 2.) do a DsCrackNames() to find the machine account dn
43 * 3.) connect to LDAP
44 * 4.) do an ldap search to find the "msDS-KeyVersionNumber" of the machine account
45 * 5.) set the servicePrincipalName's of the machine account via LDAP, (maybe we should use DsWriteAccountSpn()...)
46 * 6.) do a DsCrackNames() to find the domain dn
47 * 7.) find out Site specific stuff, look at libnet_JoinSite() for details
49 static NTSTATUS libnet_JoinADSDomain(struct libnet_context *ctx, struct libnet_JoinDomain *r)
51 NTSTATUS status;
53 TALLOC_CTX *tmp_ctx;
55 const char *realm = r->out.realm;
57 const struct dcerpc_binding *samr_binding = r->out.samr_binding;
59 struct dcerpc_pipe *drsuapi_pipe;
60 struct dcerpc_binding *drsuapi_binding;
61 enum dcerpc_transport_t transport;
62 struct drsuapi_DsBind r_drsuapi_bind;
63 struct drsuapi_DsCrackNames r_crack_names;
64 struct drsuapi_DsNameString names[1];
65 struct policy_handle drsuapi_bind_handle;
66 struct GUID drsuapi_bind_guid;
68 struct ldb_context *remote_ldb;
69 struct ldb_dn *account_dn;
70 const char *account_dn_str;
71 const char *remote_ldb_url;
72 struct ldb_result *res;
73 struct ldb_message *msg;
75 int ret, rtn;
77 const char * const attrs[] = {
78 "msDS-KeyVersionNumber",
79 "servicePrincipalName",
80 "dNSHostName",
81 "objectGUID",
82 NULL,
85 r->out.error_string = NULL;
87 /* We need to convert between a samAccountName and domain to a
88 * DN in the directory. The correct way to do this is with
89 * DRSUAPI CrackNames */
91 /* Fiddle with the bindings, so get to DRSUAPI on
92 * NCACN_IP_TCP, sealed */
93 tmp_ctx = talloc_named(r, 0, "libnet_JoinADSDomain temp context");
94 if (!tmp_ctx) {
95 r->out.error_string = NULL;
96 return NT_STATUS_NO_MEMORY;
99 drsuapi_binding = dcerpc_binding_dup(tmp_ctx, samr_binding);
100 if (!drsuapi_binding) {
101 r->out.error_string = NULL;
102 talloc_free(tmp_ctx);
103 return NT_STATUS_NO_MEMORY;
106 transport = dcerpc_binding_get_transport(drsuapi_binding);
108 /* DRSUAPI is only available on IP_TCP, and locally on NCALRPC */
109 if (transport != NCALRPC) {
110 status = dcerpc_binding_set_transport(drsuapi_binding, NCACN_IP_TCP);
111 if (!NT_STATUS_IS_OK(status)) {
112 r->out.error_string = talloc_asprintf(r,
113 "dcerpc_binding_set_transport failed: %s",
114 nt_errstr(status));
115 talloc_free(tmp_ctx);
116 return status;
120 status = dcerpc_binding_set_string_option(drsuapi_binding, "endpoint", NULL);
121 if (!NT_STATUS_IS_OK(status)) {
122 r->out.error_string = talloc_asprintf(r,
123 "dcerpc_binding_set_string_option failed: %s",
124 nt_errstr(status));
125 talloc_free(tmp_ctx);
126 return status;
129 status = dcerpc_binding_set_flags(drsuapi_binding, DCERPC_SEAL, 0);
130 if (!NT_STATUS_IS_OK(status)) {
131 r->out.error_string = talloc_asprintf(r,
132 "dcerpc_binding_set_flags failed: %s",
133 nt_errstr(status));
134 talloc_free(tmp_ctx);
135 return status;
138 status = dcerpc_pipe_connect_b(tmp_ctx,
139 &drsuapi_pipe,
140 drsuapi_binding,
141 &ndr_table_drsuapi,
142 ctx->cred,
143 ctx->event_ctx,
144 ctx->lp_ctx);
145 if (!NT_STATUS_IS_OK(status)) {
146 r->out.error_string = talloc_asprintf(r,
147 "Connection to DRSUAPI pipe of PDC of domain '%s' failed: %s",
148 r->out.domain_name,
149 nt_errstr(status));
150 talloc_free(tmp_ctx);
151 return status;
154 /* get a DRSUAPI pipe handle */
155 GUID_from_string(DRSUAPI_DS_BIND_GUID, &drsuapi_bind_guid);
157 r_drsuapi_bind.in.bind_guid = &drsuapi_bind_guid;
158 r_drsuapi_bind.in.bind_info = NULL;
159 r_drsuapi_bind.out.bind_handle = &drsuapi_bind_handle;
161 status = dcerpc_drsuapi_DsBind_r(drsuapi_pipe->binding_handle, tmp_ctx, &r_drsuapi_bind);
162 if (!NT_STATUS_IS_OK(status)) {
163 r->out.error_string
164 = talloc_asprintf(r,
165 "dcerpc_drsuapi_DsBind failed - %s",
166 nt_errstr(status));
167 talloc_free(tmp_ctx);
168 return status;
169 } else if (!W_ERROR_IS_OK(r_drsuapi_bind.out.result)) {
170 r->out.error_string
171 = talloc_asprintf(r,
172 "DsBind failed - %s",
173 win_errstr(r_drsuapi_bind.out.result));
174 talloc_free(tmp_ctx);
175 return NT_STATUS_UNSUCCESSFUL;
178 /* Actually 'crack' the names */
179 ZERO_STRUCT(r_crack_names);
180 r_crack_names.in.bind_handle = &drsuapi_bind_handle;
181 r_crack_names.in.level = 1;
182 r_crack_names.in.req = talloc(r, union drsuapi_DsNameRequest);
183 if (!r_crack_names.in.req) {
184 r->out.error_string = NULL;
185 talloc_free(tmp_ctx);
186 return NT_STATUS_NO_MEMORY;
188 r_crack_names.in.req->req1.codepage = 1252; /* western european */
189 r_crack_names.in.req->req1.language = 0x00000407; /* german */
190 r_crack_names.in.req->req1.count = 1;
191 r_crack_names.in.req->req1.names = names;
192 r_crack_names.in.req->req1.format_flags = DRSUAPI_DS_NAME_FLAG_NO_FLAGS;
193 r_crack_names.in.req->req1.format_offered = DRSUAPI_DS_NAME_FORMAT_SID_OR_SID_HISTORY;
194 r_crack_names.in.req->req1.format_desired = DRSUAPI_DS_NAME_FORMAT_FQDN_1779;
195 names[0].str = dom_sid_string(tmp_ctx, r->out.account_sid);
196 if (!names[0].str) {
197 r->out.error_string = NULL;
198 talloc_free(tmp_ctx);
199 return NT_STATUS_NO_MEMORY;
202 r_crack_names.out.ctr = talloc(r, union drsuapi_DsNameCtr);
203 r_crack_names.out.level_out = talloc(r, uint32_t);
204 if (!r_crack_names.out.ctr || !r_crack_names.out.level_out) {
205 r->out.error_string = NULL;
206 talloc_free(tmp_ctx);
207 return NT_STATUS_NO_MEMORY;
210 status = dcerpc_drsuapi_DsCrackNames_r(drsuapi_pipe->binding_handle, tmp_ctx, &r_crack_names);
211 if (!NT_STATUS_IS_OK(status)) {
212 r->out.error_string
213 = talloc_asprintf(r,
214 "dcerpc_drsuapi_DsCrackNames for [%s] failed - %s",
215 names[0].str,
216 nt_errstr(status));
217 talloc_free(tmp_ctx);
218 return status;
219 } else if (!W_ERROR_IS_OK(r_crack_names.out.result)) {
220 r->out.error_string
221 = talloc_asprintf(r,
222 "DsCrackNames failed - %s", win_errstr(r_crack_names.out.result));
223 talloc_free(tmp_ctx);
224 return NT_STATUS_UNSUCCESSFUL;
225 } else if (*r_crack_names.out.level_out != 1
226 || !r_crack_names.out.ctr->ctr1
227 || r_crack_names.out.ctr->ctr1->count != 1) {
228 r->out.error_string = talloc_asprintf(r, "DsCrackNames failed");
229 talloc_free(tmp_ctx);
230 return NT_STATUS_INVALID_PARAMETER;
231 } else if (r_crack_names.out.ctr->ctr1->array[0].status != DRSUAPI_DS_NAME_STATUS_OK) {
232 r->out.error_string = talloc_asprintf(r, "DsCrackNames failed: %d", r_crack_names.out.ctr->ctr1->array[0].status);
233 talloc_free(tmp_ctx);
234 return NT_STATUS_UNSUCCESSFUL;
235 } else if (r_crack_names.out.ctr->ctr1->array[0].result_name == NULL) {
236 r->out.error_string = talloc_asprintf(r, "DsCrackNames failed: no result name");
237 talloc_free(tmp_ctx);
238 return NT_STATUS_INVALID_PARAMETER;
241 /* Store the DN of our machine account. */
242 account_dn_str = r_crack_names.out.ctr->ctr1->array[0].result_name;
244 /* Now we know the user's DN, open with LDAP, read and modify a few things */
246 remote_ldb_url = talloc_asprintf(tmp_ctx, "ldap://%s",
247 dcerpc_binding_get_string_option(drsuapi_binding, "target_hostname"));
248 if (!remote_ldb_url) {
249 r->out.error_string = NULL;
250 talloc_free(tmp_ctx);
251 return NT_STATUS_NO_MEMORY;
254 remote_ldb = ldb_wrap_connect(tmp_ctx, ctx->event_ctx, ctx->lp_ctx,
255 remote_ldb_url,
256 NULL, ctx->cred, 0);
257 if (!remote_ldb) {
258 r->out.error_string = NULL;
259 talloc_free(tmp_ctx);
260 return NT_STATUS_UNSUCCESSFUL;
263 account_dn = ldb_dn_new(tmp_ctx, remote_ldb, account_dn_str);
264 if (account_dn == NULL) {
265 r->out.error_string = talloc_asprintf(r, "Invalid account dn: %s",
266 account_dn_str);
267 talloc_free(tmp_ctx);
268 return NT_STATUS_UNSUCCESSFUL;
271 /* search for the user's record */
272 ret = ldb_search(remote_ldb, tmp_ctx, &res,
273 account_dn, LDB_SCOPE_BASE, attrs, NULL);
274 if (ret != LDB_SUCCESS) {
275 r->out.error_string = talloc_asprintf(r, "ldb_search for %s failed - %s",
276 account_dn_str, ldb_errstring(remote_ldb));
277 talloc_free(tmp_ctx);
278 return NT_STATUS_UNSUCCESSFUL;
281 if (res->count != 1) {
282 r->out.error_string = talloc_asprintf(r, "ldb_search for %s failed - found %d entries",
283 account_dn_str, res->count);
284 talloc_free(tmp_ctx);
285 return NT_STATUS_UNSUCCESSFUL;
288 /* Prepare a new message, for the modify */
289 msg = ldb_msg_new(tmp_ctx);
290 if (!msg) {
291 r->out.error_string = NULL;
292 talloc_free(tmp_ctx);
293 return NT_STATUS_NO_MEMORY;
295 msg->dn = res->msgs[0]->dn;
298 unsigned int i;
299 const char *service_principal_name[2];
300 const char *dns_host_name = strlower_talloc(msg,
301 talloc_asprintf(msg,
302 "%s.%s",
303 r->in.netbios_name,
304 realm));
306 if (!dns_host_name) {
307 r->out.error_string = NULL;
308 talloc_free(tmp_ctx);
309 return NT_STATUS_NO_MEMORY;
312 service_principal_name[0] = talloc_asprintf(msg, "HOST/%s",
313 dns_host_name);
314 service_principal_name[1] = talloc_asprintf(msg, "HOST/%s",
315 r->in.netbios_name);
317 for (i=0; i < ARRAY_SIZE(service_principal_name); i++) {
318 if (!service_principal_name[i]) {
319 r->out.error_string = NULL;
320 talloc_free(tmp_ctx);
321 return NT_STATUS_NO_MEMORY;
323 rtn = ldb_msg_add_string(msg, "servicePrincipalName",
324 service_principal_name[i]);
325 if (rtn != LDB_SUCCESS) {
326 r->out.error_string = NULL;
327 talloc_free(tmp_ctx);
328 return NT_STATUS_NO_MEMORY;
332 rtn = ldb_msg_add_string(msg, "dNSHostName", dns_host_name);
333 if (rtn != LDB_SUCCESS) {
334 r->out.error_string = NULL;
335 talloc_free(tmp_ctx);
336 return NT_STATUS_NO_MEMORY;
339 rtn = dsdb_replace(remote_ldb, msg, 0);
340 if (rtn != LDB_SUCCESS) {
341 r->out.error_string
342 = talloc_asprintf(r,
343 "Failed to replace entries on %s",
344 ldb_dn_get_linearized(msg->dn));
345 talloc_free(tmp_ctx);
346 return NT_STATUS_INTERNAL_DB_CORRUPTION;
350 msg = ldb_msg_new(tmp_ctx);
351 if (!msg) {
352 r->out.error_string = NULL;
353 talloc_free(tmp_ctx);
354 return NT_STATUS_NO_MEMORY;
356 msg->dn = res->msgs[0]->dn;
358 rtn = samdb_msg_add_uint(remote_ldb, msg, msg,
359 "msDS-SupportedEncryptionTypes", ENC_ALL_TYPES);
360 if (rtn != LDB_SUCCESS) {
361 r->out.error_string = NULL;
362 talloc_free(tmp_ctx);
363 return NT_STATUS_NO_MEMORY;
366 rtn = dsdb_replace(remote_ldb, msg, 0);
367 /* The remote server may not support this attribute, if it
368 * isn't a modern schema */
369 if (rtn != LDB_SUCCESS && rtn != LDB_ERR_NO_SUCH_ATTRIBUTE) {
370 r->out.error_string
371 = talloc_asprintf(r,
372 "Failed to replace msDS-SupportedEncryptionTypes on %s",
373 ldb_dn_get_linearized(msg->dn));
374 talloc_free(tmp_ctx);
375 return NT_STATUS_INTERNAL_DB_CORRUPTION;
378 /* DsCrackNames to find out the DN of the domain. */
379 r_crack_names.in.req->req1.format_offered = DRSUAPI_DS_NAME_FORMAT_NT4_ACCOUNT;
380 r_crack_names.in.req->req1.format_desired = DRSUAPI_DS_NAME_FORMAT_FQDN_1779;
381 names[0].str = talloc_asprintf(tmp_ctx, "%s\\", r->out.domain_name);
382 if (!names[0].str) {
383 r->out.error_string = NULL;
384 talloc_free(tmp_ctx);
385 return NT_STATUS_NO_MEMORY;
388 status = dcerpc_drsuapi_DsCrackNames_r(drsuapi_pipe->binding_handle, tmp_ctx, &r_crack_names);
389 if (!NT_STATUS_IS_OK(status)) {
390 r->out.error_string
391 = talloc_asprintf(r,
392 "dcerpc_drsuapi_DsCrackNames for [%s] failed - %s",
393 r->in.domain_name,
394 nt_errstr(status));
395 talloc_free(tmp_ctx);
396 return status;
397 } else if (!W_ERROR_IS_OK(r_crack_names.out.result)) {
398 r->out.error_string
399 = talloc_asprintf(r,
400 "DsCrackNames failed - %s", win_errstr(r_crack_names.out.result));
401 talloc_free(tmp_ctx);
402 return NT_STATUS_UNSUCCESSFUL;
403 } else if (*r_crack_names.out.level_out != 1
404 || !r_crack_names.out.ctr->ctr1
405 || r_crack_names.out.ctr->ctr1->count != 1
406 || !r_crack_names.out.ctr->ctr1->array[0].result_name
407 || r_crack_names.out.ctr->ctr1->array[0].status != DRSUAPI_DS_NAME_STATUS_OK) {
408 r->out.error_string = talloc_asprintf(r, "DsCrackNames failed");
409 talloc_free(tmp_ctx);
410 return NT_STATUS_UNSUCCESSFUL;
413 /* Store the account DN. */
414 r->out.account_dn_str = account_dn_str;
415 talloc_steal(r, account_dn_str);
417 /* Store the domain DN. */
418 r->out.domain_dn_str = r_crack_names.out.ctr->ctr1->array[0].result_name;
419 talloc_steal(r, r_crack_names.out.ctr->ctr1->array[0].result_name);
421 /* Store the KVNO of the account, critical for some kerberos
422 * operations */
423 r->out.kvno = ldb_msg_find_attr_as_uint(res->msgs[0], "msDS-KeyVersionNumber", 0);
425 /* Store the account GUID. */
426 r->out.account_guid = samdb_result_guid(res->msgs[0], "objectGUID");
428 if (r->in.acct_type == ACB_SVRTRUST) {
429 status = libnet_JoinSite(ctx, remote_ldb, r);
431 talloc_free(tmp_ctx);
433 return status;
437 * do a domain join using DCERPC/SAMR calls
438 * - connect to the LSA pipe, to try and find out information about the domain
439 * - create a secondary connection to SAMR pipe
440 * - do a samr_Connect to get a policy handle
441 * - do a samr_LookupDomain to get the domain sid
442 * - do a samr_OpenDomain to get a domain handle
443 * - do a samr_CreateAccount to try and get a new account
445 * If that fails, do:
446 * - do a samr_LookupNames to get the users rid
447 * - do a samr_OpenUser to get a user handle
448 * - potentially delete and recreate the user
449 * - assert the account is of the right type with samrQueryUserInfo
451 * - call libnet_SetPassword_samr_handle to set the password,
452 * and pass a samr_UserInfo21 struct to set full_name and the account flags
454 * - do some ADS specific things when we join as Domain Controller,
455 * look at libnet_joinADSDomain() for the details
457 NTSTATUS libnet_JoinDomain(struct libnet_context *ctx, TALLOC_CTX *mem_ctx, struct libnet_JoinDomain *r)
459 TALLOC_CTX *tmp_ctx;
461 NTSTATUS status, cu_status;
463 struct libnet_RpcConnect *connect_with_info;
464 struct dcerpc_pipe *samr_pipe;
466 struct samr_Connect sc;
467 struct policy_handle p_handle;
468 struct samr_OpenDomain od;
469 struct policy_handle d_handle;
470 struct samr_LookupNames ln;
471 struct samr_Ids rids, types;
472 struct samr_OpenUser ou;
473 struct samr_CreateUser2 cu;
474 struct policy_handle *u_handle = NULL;
475 struct samr_QueryUserInfo qui;
476 union samr_UserInfo *uinfo;
477 struct samr_UserInfo21 u_info21;
478 union libnet_SetPassword r2;
479 struct samr_GetUserPwInfo pwp;
480 struct samr_PwInfo info;
481 struct lsa_String samr_account_name;
483 uint32_t acct_flags, old_acct_flags;
484 uint32_t rid, access_granted;
485 int policy_min_pw_len = 0;
487 struct dom_sid *account_sid = NULL;
488 const char *password_str = NULL;
490 r->out.error_string = NULL;
491 ZERO_STRUCT(r2);
493 tmp_ctx = talloc_named(mem_ctx, 0, "libnet_Join temp context");
494 if (!tmp_ctx) {
495 r->out.error_string = NULL;
496 return NT_STATUS_NO_MEMORY;
499 u_handle = talloc(tmp_ctx, struct policy_handle);
500 if (!u_handle) {
501 r->out.error_string = NULL;
502 talloc_free(tmp_ctx);
503 return NT_STATUS_NO_MEMORY;
506 connect_with_info = talloc_zero(tmp_ctx, struct libnet_RpcConnect);
507 if (!connect_with_info) {
508 r->out.error_string = NULL;
509 talloc_free(tmp_ctx);
510 return NT_STATUS_NO_MEMORY;
513 /* prepare connect to the SAMR pipe of PDC */
514 if (r->in.level == LIBNET_JOINDOMAIN_AUTOMATIC) {
515 connect_with_info->in.binding = NULL;
516 connect_with_info->in.name = r->in.domain_name;
517 } else {
518 connect_with_info->in.binding = r->in.binding;
519 connect_with_info->in.name = NULL;
522 /* This level makes a connection to the LSA pipe on the way,
523 * to get some useful bits of information about the domain */
524 connect_with_info->level = LIBNET_RPC_CONNECT_DC_INFO;
525 connect_with_info->in.dcerpc_iface = &ndr_table_samr;
528 establish the SAMR connection
530 status = libnet_RpcConnect(ctx, tmp_ctx, connect_with_info);
531 if (!NT_STATUS_IS_OK(status)) {
532 if (r->in.binding) {
533 r->out.error_string = talloc_asprintf(mem_ctx,
534 "Connection to SAMR pipe of DC %s failed: %s",
535 r->in.binding, connect_with_info->out.error_string);
536 } else {
537 r->out.error_string = talloc_asprintf(mem_ctx,
538 "Connection to SAMR pipe of PDC for %s failed: %s",
539 r->in.domain_name, connect_with_info->out.error_string);
541 talloc_free(tmp_ctx);
542 return status;
545 samr_pipe = connect_with_info->out.dcerpc_pipe;
547 /* prepare samr_Connect */
548 ZERO_STRUCT(p_handle);
549 sc.in.system_name = NULL;
550 sc.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
551 sc.out.connect_handle = &p_handle;
553 /* 2. do a samr_Connect to get a policy handle */
554 status = dcerpc_samr_Connect_r(samr_pipe->binding_handle, tmp_ctx, &sc);
555 if (NT_STATUS_IS_OK(status) && !NT_STATUS_IS_OK(sc.out.result)) {
556 status = sc.out.result;
558 if (!NT_STATUS_IS_OK(status)) {
559 r->out.error_string = talloc_asprintf(mem_ctx,
560 "samr_Connect failed: %s",
561 nt_errstr(status));
562 talloc_free(tmp_ctx);
563 return status;
566 /* If this is a connection on ncacn_ip_tcp to Win2k3 SP1, we don't get back this useful info */
567 if (!connect_with_info->out.domain_name) {
568 if (r->in.level == LIBNET_JOINDOMAIN_AUTOMATIC) {
569 connect_with_info->out.domain_name = talloc_strdup(tmp_ctx, r->in.domain_name);
570 } else {
571 /* Bugger, we just lost our way to automatically find the domain name */
572 connect_with_info->out.domain_name = talloc_strdup(tmp_ctx, lpcfg_workgroup(ctx->lp_ctx));
573 connect_with_info->out.realm = talloc_strdup(tmp_ctx, lpcfg_realm(ctx->lp_ctx));
577 /* Perhaps we didn't get a SID above, because we are against ncacn_ip_tcp */
578 if (!connect_with_info->out.domain_sid) {
579 struct lsa_String name;
580 struct samr_LookupDomain l;
581 struct dom_sid2 *sid = NULL;
582 name.string = connect_with_info->out.domain_name;
583 l.in.connect_handle = &p_handle;
584 l.in.domain_name = &name;
585 l.out.sid = &sid;
587 status = dcerpc_samr_LookupDomain_r(samr_pipe->binding_handle, tmp_ctx, &l);
588 if (NT_STATUS_IS_OK(status) && !NT_STATUS_IS_OK(l.out.result)) {
589 status = l.out.result;
591 if (!NT_STATUS_IS_OK(status)) {
592 r->out.error_string = talloc_asprintf(mem_ctx,
593 "SAMR LookupDomain failed: %s",
594 nt_errstr(status));
595 talloc_free(tmp_ctx);
596 return status;
598 connect_with_info->out.domain_sid = *l.out.sid;
601 /* prepare samr_OpenDomain */
602 ZERO_STRUCT(d_handle);
603 od.in.connect_handle = &p_handle;
604 od.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
605 od.in.sid = connect_with_info->out.domain_sid;
606 od.out.domain_handle = &d_handle;
608 /* do a samr_OpenDomain to get a domain handle */
609 status = dcerpc_samr_OpenDomain_r(samr_pipe->binding_handle, tmp_ctx, &od);
610 if (NT_STATUS_IS_OK(status) && !NT_STATUS_IS_OK(od.out.result)) {
611 status = od.out.result;
613 if (!NT_STATUS_IS_OK(status)) {
614 struct dom_sid_buf buf;
615 r->out.error_string = talloc_asprintf(
616 mem_ctx,
617 "samr_OpenDomain for [%s] failed: %s",
618 dom_sid_str_buf(connect_with_info->out.domain_sid,
619 &buf),
620 nt_errstr(status));
621 talloc_free(tmp_ctx);
622 return status;
625 /* prepare samr_CreateUser2 */
626 ZERO_STRUCTP(u_handle);
627 cu.in.domain_handle = &d_handle;
628 cu.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
629 samr_account_name.string = r->in.account_name;
630 cu.in.account_name = &samr_account_name;
631 cu.in.acct_flags = r->in.acct_type;
632 cu.out.user_handle = u_handle;
633 cu.out.rid = &rid;
634 cu.out.access_granted = &access_granted;
636 /* do a samr_CreateUser2 to get an account handle, or an error */
637 cu_status = dcerpc_samr_CreateUser2_r(samr_pipe->binding_handle, tmp_ctx, &cu);
638 if (NT_STATUS_IS_OK(cu_status) && !NT_STATUS_IS_OK(cu.out.result)) {
639 cu_status = cu.out.result;
641 status = cu_status;
642 if (NT_STATUS_EQUAL(status, NT_STATUS_USER_EXISTS)) {
643 /* prepare samr_LookupNames */
644 ln.in.domain_handle = &d_handle;
645 ln.in.num_names = 1;
646 ln.in.names = talloc_array(tmp_ctx, struct lsa_String, 1);
647 ln.out.rids = &rids;
648 ln.out.types = &types;
649 if (!ln.in.names) {
650 r->out.error_string = NULL;
651 talloc_free(tmp_ctx);
652 return NT_STATUS_NO_MEMORY;
654 ln.in.names[0].string = r->in.account_name;
656 /* 5. do a samr_LookupNames to get the users rid */
657 status = dcerpc_samr_LookupNames_r(samr_pipe->binding_handle, tmp_ctx, &ln);
658 if (NT_STATUS_IS_OK(status) && !NT_STATUS_IS_OK(ln.out.result)) {
659 status = ln.out.result;
661 if (!NT_STATUS_IS_OK(status)) {
662 r->out.error_string = talloc_asprintf(mem_ctx,
663 "samr_LookupNames for [%s] failed: %s",
664 r->in.account_name,
665 nt_errstr(status));
666 talloc_free(tmp_ctx);
667 return status;
670 /* check if we got one RID for the user */
671 if (ln.out.rids->count != 1) {
672 r->out.error_string = talloc_asprintf(mem_ctx,
673 "samr_LookupNames for [%s] returns %d RIDs",
674 r->in.account_name, ln.out.rids->count);
675 talloc_free(tmp_ctx);
676 return NT_STATUS_INVALID_NETWORK_RESPONSE;
679 if (ln.out.types->count != 1) {
680 r->out.error_string = talloc_asprintf(mem_ctx,
681 "samr_LookupNames for [%s] returns %d RID TYPEs",
682 r->in.account_name, ln.out.types->count);
683 talloc_free(tmp_ctx);
684 return NT_STATUS_INVALID_NETWORK_RESPONSE;
687 /* prepare samr_OpenUser */
688 ZERO_STRUCTP(u_handle);
689 ou.in.domain_handle = &d_handle;
690 ou.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
691 ou.in.rid = ln.out.rids->ids[0];
692 rid = ou.in.rid;
693 ou.out.user_handle = u_handle;
695 /* 6. do a samr_OpenUser to get a user handle */
696 status = dcerpc_samr_OpenUser_r(samr_pipe->binding_handle, tmp_ctx, &ou);
697 if (NT_STATUS_IS_OK(status) && !NT_STATUS_IS_OK(ou.out.result)) {
698 status = ou.out.result;
700 if (!NT_STATUS_IS_OK(status)) {
701 r->out.error_string = talloc_asprintf(mem_ctx,
702 "samr_OpenUser for [%s] failed: %s",
703 r->in.account_name,
704 nt_errstr(status));
705 talloc_free(tmp_ctx);
706 return status;
709 if (r->in.recreate_account) {
710 struct samr_DeleteUser d;
711 d.in.user_handle = u_handle;
712 d.out.user_handle = u_handle;
713 status = dcerpc_samr_DeleteUser_r(samr_pipe->binding_handle, mem_ctx, &d);
714 if (NT_STATUS_IS_OK(status) && !NT_STATUS_IS_OK(d.out.result)) {
715 status = d.out.result;
717 if (!NT_STATUS_IS_OK(status)) {
718 r->out.error_string = talloc_asprintf(mem_ctx,
719 "samr_DeleteUser (for recreate) of [%s] failed: %s",
720 r->in.account_name,
721 nt_errstr(status));
722 talloc_free(tmp_ctx);
723 return status;
726 /* We want to recreate, so delete and another samr_CreateUser2 */
728 /* &cu filled in above */
729 status = dcerpc_samr_CreateUser2_r(samr_pipe->binding_handle, tmp_ctx, &cu);
730 if (NT_STATUS_IS_OK(status) && !NT_STATUS_IS_OK(cu.out.result)) {
731 status = cu.out.result;
733 if (!NT_STATUS_IS_OK(status)) {
734 r->out.error_string = talloc_asprintf(mem_ctx,
735 "samr_CreateUser2 (recreate) for [%s] failed: %s",
736 r->in.account_name, nt_errstr(status));
737 talloc_free(tmp_ctx);
738 return status;
741 } else if (!NT_STATUS_IS_OK(status)) {
742 r->out.error_string = talloc_asprintf(mem_ctx,
743 "samr_CreateUser2 for [%s] failed: %s",
744 r->in.account_name, nt_errstr(status));
745 talloc_free(tmp_ctx);
746 return status;
749 /* prepare samr_QueryUserInfo (get flags) */
750 qui.in.user_handle = u_handle;
751 qui.in.level = 16;
752 qui.out.info = &uinfo;
754 status = dcerpc_samr_QueryUserInfo_r(samr_pipe->binding_handle, tmp_ctx, &qui);
755 if (NT_STATUS_IS_OK(status) && !NT_STATUS_IS_OK(qui.out.result)) {
756 status = qui.out.result;
758 if (!NT_STATUS_IS_OK(status)) {
759 r->out.error_string = talloc_asprintf(mem_ctx,
760 "samr_QueryUserInfo for [%s] failed: %s",
761 r->in.account_name,
762 nt_errstr(status));
763 talloc_free(tmp_ctx);
764 return status;
767 if (!uinfo) {
768 status = NT_STATUS_INVALID_PARAMETER;
769 r->out.error_string
770 = talloc_asprintf(mem_ctx,
771 "samr_QueryUserInfo failed to return qui.out.info for [%s]: %s",
772 r->in.account_name, nt_errstr(status));
773 talloc_free(tmp_ctx);
774 return status;
777 old_acct_flags = (uinfo->info16.acct_flags & (ACB_WSTRUST | ACB_SVRTRUST | ACB_DOMTRUST));
778 /* Possibly bail if the account is of the wrong type */
779 if (old_acct_flags
780 != r->in.acct_type) {
781 const char *old_account_type, *new_account_type;
782 switch (old_acct_flags) {
783 case ACB_WSTRUST:
784 old_account_type = "domain member (member)";
785 break;
786 case ACB_SVRTRUST:
787 old_account_type = "domain controller (bdc)";
788 break;
789 case ACB_DOMTRUST:
790 old_account_type = "trusted domain";
791 break;
792 default:
793 return NT_STATUS_INVALID_PARAMETER;
795 switch (r->in.acct_type) {
796 case ACB_WSTRUST:
797 new_account_type = "domain member (member)";
798 break;
799 case ACB_SVRTRUST:
800 new_account_type = "domain controller (bdc)";
801 break;
802 case ACB_DOMTRUST:
803 new_account_type = "trusted domain";
804 break;
805 default:
806 return NT_STATUS_INVALID_PARAMETER;
809 if (!NT_STATUS_EQUAL(cu_status, NT_STATUS_USER_EXISTS)) {
810 /* We created a new user, but they didn't come out the right type?!? */
811 r->out.error_string
812 = talloc_asprintf(mem_ctx,
813 "We asked to create a new machine account (%s) of type %s, "
814 "but we got an account of type %s. This is unexpected. "
815 "Perhaps delete the account and try again.",
816 r->in.account_name, new_account_type, old_account_type);
817 talloc_free(tmp_ctx);
818 return NT_STATUS_INVALID_PARAMETER;
819 } else {
820 /* The account is of the wrong type, so bail */
822 /* TODO: We should allow a --force option to override, and redo this from the top setting r.in.recreate_account */
823 r->out.error_string
824 = talloc_asprintf(mem_ctx,
825 "The machine account (%s) already exists in the domain %s, "
826 "but is a %s. You asked to join as a %s. Please delete "
827 "the account and try again.",
828 r->in.account_name, connect_with_info->out.domain_name, old_account_type, new_account_type);
829 talloc_free(tmp_ctx);
830 return NT_STATUS_USER_EXISTS;
832 } else {
833 acct_flags = uinfo->info16.acct_flags;
836 acct_flags = (acct_flags & ~(ACB_DISABLED|ACB_PWNOTREQ));
838 /* Find out what password policy this user has */
839 pwp.in.user_handle = u_handle;
840 pwp.out.info = &info;
842 status = dcerpc_samr_GetUserPwInfo_r(samr_pipe->binding_handle, tmp_ctx, &pwp);
843 if (NT_STATUS_IS_OK(status) && !NT_STATUS_IS_OK(pwp.out.result)) {
844 status = pwp.out.result;
846 if (NT_STATUS_IS_OK(status)) {
847 policy_min_pw_len = pwp.out.info->min_password_length;
850 if (r->in.account_pass != NULL) {
851 password_str = talloc_strdup(tmp_ctx, r->in.account_pass);
852 } else {
853 /* Grab a password of that minimum length */
854 password_str = generate_random_password(tmp_ctx,
855 MAX(8, policy_min_pw_len), 255);
857 if (!password_str) {
858 r->out.error_string = NULL;
859 talloc_free(tmp_ctx);
860 return NT_STATUS_NO_MEMORY;
863 /* set full_name and reset flags */
864 ZERO_STRUCT(u_info21);
865 u_info21.full_name.string = r->in.account_name;
866 u_info21.acct_flags = acct_flags;
867 u_info21.fields_present = SAMR_FIELD_FULL_NAME | SAMR_FIELD_ACCT_FLAGS;
869 r2.samr_handle.level = LIBNET_SET_PASSWORD_SAMR_HANDLE;
870 r2.samr_handle.in.account_name = r->in.account_name;
871 r2.samr_handle.in.newpassword = password_str;
872 r2.samr_handle.in.user_handle = u_handle;
873 r2.samr_handle.in.dcerpc_pipe = samr_pipe;
874 r2.samr_handle.in.info21 = &u_info21;
876 status = libnet_SetPassword(ctx, tmp_ctx, &r2);
877 if (!NT_STATUS_IS_OK(status)) {
878 r->out.error_string = talloc_steal(mem_ctx, r2.samr_handle.out.error_string);
879 talloc_free(tmp_ctx);
880 return status;
883 account_sid = dom_sid_add_rid(mem_ctx, connect_with_info->out.domain_sid, rid);
884 if (!account_sid) {
885 r->out.error_string = NULL;
886 talloc_free(tmp_ctx);
887 return NT_STATUS_NO_MEMORY;
890 /* Finish out by pushing various bits of status data out for the caller to use */
891 r->out.join_password = password_str;
892 talloc_steal(mem_ctx, r->out.join_password);
894 r->out.domain_sid = connect_with_info->out.domain_sid;
895 talloc_steal(mem_ctx, r->out.domain_sid);
897 r->out.account_sid = account_sid;
898 talloc_steal(mem_ctx, r->out.account_sid);
900 r->out.domain_name = connect_with_info->out.domain_name;
901 talloc_steal(mem_ctx, r->out.domain_name);
902 r->out.realm = connect_with_info->out.realm;
903 talloc_steal(mem_ctx, r->out.realm);
904 r->out.samr_pipe = samr_pipe;
905 talloc_reparent(tmp_ctx, mem_ctx, samr_pipe);
906 r->out.samr_binding =
907 dcerpc_binding_handle_get_binding(samr_pipe->binding_handle);
908 r->out.user_handle = u_handle;
909 talloc_steal(mem_ctx, u_handle);
910 r->out.error_string = r2.samr_handle.out.error_string;
911 talloc_steal(mem_ctx, r2.samr_handle.out.error_string);
912 r->out.kvno = 0;
913 r->out.server_dn_str = NULL;
914 talloc_free(tmp_ctx);
916 /* Now, if it was AD, then we want to start looking changing a
917 * few more things. Otherwise, we are done. */
918 if (r->out.realm) {
919 status = libnet_JoinADSDomain(ctx, r);
920 return status;
923 return status;
926 NTSTATUS libnet_Join_member(struct libnet_context *ctx,
927 TALLOC_CTX *mem_ctx,
928 struct libnet_Join_member *r)
930 NTSTATUS status;
931 TALLOC_CTX *tmp_mem;
932 struct libnet_JoinDomain *r2;
933 struct provision_store_self_join_settings *set_secrets;
934 uint32_t acct_type = 0;
935 const char *account_name;
936 const char *netbios_name;
937 const char *error_string = NULL;
939 r->out.error_string = NULL;
941 tmp_mem = talloc_new(mem_ctx);
942 if (!tmp_mem) {
943 return NT_STATUS_NO_MEMORY;
946 r2 = talloc_zero(tmp_mem, struct libnet_JoinDomain);
947 if (!r2) {
948 status = NT_STATUS_NO_MEMORY;
949 goto out;
952 acct_type = ACB_WSTRUST;
954 if (r->in.netbios_name != NULL) {
955 netbios_name = r->in.netbios_name;
956 } else {
957 netbios_name = talloc_strdup(tmp_mem, lpcfg_netbios_name(ctx->lp_ctx));
958 if (!netbios_name) {
959 status = NT_STATUS_NO_MEMORY;
960 goto out;
964 account_name = talloc_asprintf(tmp_mem, "%s$", netbios_name);
965 if (!account_name) {
966 status = NT_STATUS_NO_MEMORY;
967 goto out;
971 * join the domain
973 r2->in.domain_name = r->in.domain_name;
974 r2->in.account_name = account_name;
975 r2->in.netbios_name = netbios_name;
976 r2->in.level = LIBNET_JOINDOMAIN_AUTOMATIC;
977 r2->in.acct_type = acct_type;
978 r2->in.recreate_account = false;
979 r2->in.account_pass = r->in.account_pass;
980 status = libnet_JoinDomain(ctx, r2, r2);
981 if (!NT_STATUS_IS_OK(status)) {
982 r->out.error_string = talloc_steal(mem_ctx, r2->out.error_string);
983 goto out;
986 set_secrets = talloc_zero(tmp_mem,
987 struct provision_store_self_join_settings);
988 if (!set_secrets) {
989 status = NT_STATUS_NO_MEMORY;
990 goto out;
993 set_secrets->domain_name = r2->out.domain_name;
994 set_secrets->realm = r2->out.realm;
995 set_secrets->netbios_name = netbios_name;
996 set_secrets->secure_channel_type = SEC_CHAN_WKSTA;
997 set_secrets->machine_password = r2->out.join_password;
998 set_secrets->key_version_number = r2->out.kvno;
999 set_secrets->domain_sid = r2->out.domain_sid;
1001 status = provision_store_self_join(ctx, ctx->lp_ctx, ctx->event_ctx, set_secrets, &error_string);
1002 if (!NT_STATUS_IS_OK(status)) {
1003 if (error_string) {
1004 r->out.error_string = talloc_steal(mem_ctx, error_string);
1005 } else {
1006 r->out.error_string
1007 = talloc_asprintf(mem_ctx,
1008 "provision_store_self_join failed with %s",
1009 nt_errstr(status));
1011 goto out;
1014 /* move all out parameter to the callers TALLOC_CTX */
1015 r->out.join_password = talloc_move(mem_ctx, &r2->out.join_password);
1016 r->out.domain_sid = talloc_move(mem_ctx, &r2->out.domain_sid);
1017 r->out.domain_name = talloc_move(mem_ctx, &r2->out.domain_name);
1018 status = NT_STATUS_OK;
1019 out:
1020 talloc_free(tmp_mem);
1021 return status;