r13924: Split more prototypes out of include/proto.h + initial work on header
[kai/samba.git] / source4 / libnet / libnet_join.c
1 /* 
2    Unix SMB/CIFS implementation.
3    
4    Copyright (C) Stefan Metzmacher      2004
5    Copyright (C) Andrew Bartlett <abartlet@samba.org> 2005
6    Copyright (C) Brad Henry 2005
7  
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 2 of the License, or
11    (at your option) any later version.
12    
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.
17    
18    You should have received a copy of the GNU General Public License
19    along with this program; if not, write to the Free Software
20    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 */
22
23 #include "includes.h"
24 #include "libnet/libnet.h"
25 #include "librpc/gen_ndr/ndr_drsuapi.h"
26 #include "lib/ldb/include/ldb.h"
27 #include "lib/ldb/include/ldb_errors.h"
28 #include "passdb/secrets.h"
29 #include "dsdb/samdb/samdb.h"
30 #include "db_wrap.h"
31 #include "libcli/security/proto.h"
32
33 /*
34  * complete a domain join, when joining to a AD domain:
35  * 1.) connect and bind to the DRSUAPI pipe
36  * 2.) do a DsCrackNames() to find the machine account dn
37  * 3.) connect to LDAP
38  * 4.) do an ldap search to find the "msDS-KeyVersionNumber" of the machine account
39  * 5.) set the servicePrincipalName's of the machine account via LDAP, (maybe we should use DsWriteAccountSpn()...)
40  * 6.) do a DsCrackNames() to find the domain dn
41  * 7.) find out Site specific stuff, look at libnet_JoinSite() for details
42  */
43 static NTSTATUS libnet_JoinADSDomain(struct libnet_context *ctx, struct libnet_JoinDomain *r)
44 {
45         NTSTATUS status;
46
47         TALLOC_CTX *tmp_ctx;
48
49         const char *realm = r->out.realm;
50
51         struct dcerpc_binding *samr_binding = r->out.samr_binding;
52
53         struct dcerpc_pipe *drsuapi_pipe;
54         struct dcerpc_binding *drsuapi_binding;
55         struct drsuapi_DsBind r_drsuapi_bind;
56         struct drsuapi_DsCrackNames r_crack_names;
57         struct drsuapi_DsNameString names[1];
58         struct policy_handle drsuapi_bind_handle;
59         struct GUID drsuapi_bind_guid;
60
61         struct ldb_context *remote_ldb;
62         const struct ldb_dn *account_dn;
63         const char *account_dn_str;
64         const char *remote_ldb_url;
65         struct ldb_result *res;
66         struct ldb_message *msg;
67
68         int ret, rtn;
69
70         unsigned int kvno;
71         
72         const char * const attrs[] = {
73                 "msDS-KeyVersionNumber",
74                 "servicePrincipalName",
75                 "dNSHostName",
76                 NULL,
77         };
78
79         r->out.error_string = NULL;
80         
81         /* We need to convert between a samAccountName and domain to a
82          * DN in the directory.  The correct way to do this is with
83          * DRSUAPI CrackNames */
84
85         /* Fiddle with the bindings, so get to DRSUAPI on
86          * NCACN_IP_TCP, sealed */
87         tmp_ctx = talloc_named(r, 0, "libnet_JoinADSDomain temp context");  
88         if (!tmp_ctx) {
89                 r->out.error_string = NULL;
90                 return NT_STATUS_NO_MEMORY;
91         }
92                                                    
93         drsuapi_binding = talloc(tmp_ctx, struct dcerpc_binding);
94         if (!drsuapi_binding) {
95                 r->out.error_string = NULL;
96                 talloc_free(tmp_ctx);
97                 return NT_STATUS_NO_MEMORY;
98         }
99         
100         *drsuapi_binding = *samr_binding;
101
102         /* DRSUAPI is only available on IP_TCP, and locally on NCALRPC */
103         if (drsuapi_binding->transport != NCALRPC) {
104                 drsuapi_binding->transport = NCACN_IP_TCP;
105         }
106         drsuapi_binding->endpoint = NULL;
107         drsuapi_binding->flags |= DCERPC_SEAL;
108
109         status = dcerpc_pipe_connect_b(tmp_ctx, 
110                                        &drsuapi_pipe,
111                                        drsuapi_binding,
112                                        &dcerpc_table_drsuapi,
113                                        ctx->cred, 
114                                        ctx->event_ctx);
115         if (!NT_STATUS_IS_OK(status)) {
116                 r->out.error_string = talloc_asprintf(r,
117                                         "Connection to DRSUAPI pipe of PDC of domain '%s' failed: %s",
118                                         r->in.domain_name,
119                                         nt_errstr(status));
120                 talloc_free(tmp_ctx);
121                 return status;
122         }
123
124         /* get a DRSUAPI pipe handle */
125         GUID_from_string(DRSUAPI_DS_BIND_GUID, &drsuapi_bind_guid);
126
127         r_drsuapi_bind.in.bind_guid = &drsuapi_bind_guid;
128         r_drsuapi_bind.in.bind_info = NULL;
129         r_drsuapi_bind.out.bind_handle = &drsuapi_bind_handle;
130
131         status = dcerpc_drsuapi_DsBind(drsuapi_pipe, tmp_ctx, &r_drsuapi_bind);
132         if (!NT_STATUS_IS_OK(status)) {
133                 if (NT_STATUS_EQUAL(status, NT_STATUS_NET_WRITE_FAULT)) {
134                         r->out.error_string
135                                 = talloc_asprintf(r,
136                                                   "dcerpc_drsuapi_DsBind for [%s\\%s] failed - %s\n", 
137                                                   r->in.domain_name, r->in.account_name, 
138                                                   dcerpc_errstr(tmp_ctx, drsuapi_pipe->last_fault_code));
139                         talloc_free(tmp_ctx);
140                         return status;
141                 } else {
142                         r->out.error_string
143                                 = talloc_asprintf(r,
144                                                   "dcerpc_drsuapi_DsBind for [%s\\%s] failed - %s\n", 
145                                                   r->in.domain_name, r->in.account_name, 
146                                                   nt_errstr(status));
147                         talloc_free(tmp_ctx);
148                         return status;
149                 }
150         } else if (!W_ERROR_IS_OK(r_drsuapi_bind.out.result)) {
151                 r->out.error_string
152                                 = talloc_asprintf(r,
153                                                   "DsBind failed - %s\n", 
154                                                   win_errstr(r_drsuapi_bind.out.result));
155                         talloc_free(tmp_ctx);
156                 return NT_STATUS_UNSUCCESSFUL;
157         }
158
159         /* Actually 'crack' the names */
160         ZERO_STRUCT(r_crack_names);
161         r_crack_names.in.bind_handle            = &drsuapi_bind_handle;
162         r_crack_names.in.level                  = 1;
163         r_crack_names.in.req.req1.unknown1      = 0x000004e4;
164         r_crack_names.in.req.req1.unknown2      = 0x00000407;
165         r_crack_names.in.req.req1.count         = 1;
166         r_crack_names.in.req.req1.names         = names;
167         r_crack_names.in.req.req1.format_flags  = DRSUAPI_DS_NAME_FLAG_NO_FLAGS;
168         r_crack_names.in.req.req1.format_offered= DRSUAPI_DS_NAME_FORMAT_SID_OR_SID_HISTORY;
169         r_crack_names.in.req.req1.format_desired= DRSUAPI_DS_NAME_FORMAT_FQDN_1779;
170         names[0].str = dom_sid_string(tmp_ctx, r->out.account_sid);
171         if (!names[0].str) {
172                 r->out.error_string = NULL;
173                 talloc_free(tmp_ctx);
174                 return NT_STATUS_NO_MEMORY;
175         }
176
177         status = dcerpc_drsuapi_DsCrackNames(drsuapi_pipe, tmp_ctx, &r_crack_names);
178         if (!NT_STATUS_IS_OK(status)) {
179                 if (NT_STATUS_EQUAL(status, NT_STATUS_NET_WRITE_FAULT)) {
180                         r->out.error_string
181                                 = talloc_asprintf(r,
182                                                   "dcerpc_drsuapi_DsCrackNames for [%s] failed - %s\n", 
183                                                   names[0].str,
184                                                   dcerpc_errstr(tmp_ctx, drsuapi_pipe->last_fault_code));
185                         talloc_free(tmp_ctx);
186                         return status;
187                 } else {
188                         r->out.error_string
189                                 = talloc_asprintf(r,
190                                                   "dcerpc_drsuapi_DsCrackNames for [%s] failed - %s\n", 
191                                                   names[0].str,
192                                                   nt_errstr(status));
193                         talloc_free(tmp_ctx);
194                         return status;
195                 }
196         } else if (!W_ERROR_IS_OK(r_crack_names.out.result)) {
197                 r->out.error_string
198                                 = talloc_asprintf(r,
199                                                   "DsCrackNames failed - %s\n", win_errstr(r_crack_names.out.result));
200                 talloc_free(tmp_ctx);
201                 return NT_STATUS_UNSUCCESSFUL;
202         } else if (r_crack_names.out.level != 1 
203                    || !r_crack_names.out.ctr.ctr1 
204                    || r_crack_names.out.ctr.ctr1->count != 1 
205                    || !r_crack_names.out.ctr.ctr1->array[0].result_name
206                    || r_crack_names.out.ctr.ctr1->array[0].status != DRSUAPI_DS_NAME_STATUS_OK) {
207                 r->out.error_string = talloc_asprintf(r, "DsCrackNames failed\n");
208                 talloc_free(tmp_ctx);
209                 return NT_STATUS_UNSUCCESSFUL;
210         }
211
212         /* Store the DN of our machine account. */
213         account_dn_str = r_crack_names.out.ctr.ctr1->array[0].result_name;
214
215         account_dn = ldb_dn_explode(tmp_ctx, account_dn_str);
216         if (!account_dn) {
217                 r->out.error_string = talloc_asprintf(r, "Invalid account dn: %s",
218                                                       account_dn_str);
219                 talloc_free(tmp_ctx);
220                 return NT_STATUS_UNSUCCESSFUL;
221         }
222
223         /* Now we know the user's DN, open with LDAP, read and modify a few things */
224
225         remote_ldb_url = talloc_asprintf(tmp_ctx, "ldap://%s", 
226                                          drsuapi_binding->host);
227         if (!remote_ldb_url) {
228                 r->out.error_string = NULL;
229                 talloc_free(tmp_ctx);
230                 return NT_STATUS_NO_MEMORY;
231         }
232
233         remote_ldb = ldb_wrap_connect(tmp_ctx, remote_ldb_url, 
234                                       NULL, ctx->cred, 0, NULL);
235         if (!remote_ldb) {
236                 r->out.error_string = NULL;
237                 talloc_free(tmp_ctx);
238                 return NT_STATUS_UNSUCCESSFUL;
239         }
240
241         /* search for the user's record */
242         ret = ldb_search(remote_ldb, account_dn, LDB_SCOPE_BASE, 
243                              NULL, attrs, &res);
244         if (ret != LDB_SUCCESS || res->count != 1) {
245                 r->out.error_string = talloc_asprintf(r, "ldb_search for %s failed - %s\n",
246                                                       account_dn_str, ldb_errstring(remote_ldb));
247                 talloc_free(tmp_ctx);
248                 return NT_STATUS_UNSUCCESSFUL;
249         }
250
251         /* If we have a kvno recorded in AD, we need it locally as well */
252         kvno = ldb_msg_find_uint(res->msgs[0], "msDS-KeyVersionNumber", 0);
253
254         /* Prepare a new message, for the modify */
255         msg = ldb_msg_new(tmp_ctx);
256         if (!msg) {
257                 r->out.error_string = NULL;
258                 talloc_free(tmp_ctx);
259                 return NT_STATUS_NO_MEMORY;
260         }
261         msg->dn = res->msgs[0]->dn;
262
263         {
264                 int i;
265                 const char *service_principal_name[6];
266                 const char *dns_host_name = strlower_talloc(tmp_ctx, 
267                                                             talloc_asprintf(tmp_ctx, 
268                                                                             "%s.%s", 
269                                                                             r->in.netbios_name, 
270                                                                             realm));
271
272                 if (!dns_host_name) {
273                         r->out.error_string = NULL;
274                         talloc_free(tmp_ctx);
275                         return NT_STATUS_NO_MEMORY;
276                 }
277
278                 service_principal_name[0] = talloc_asprintf(tmp_ctx, "host/%s", dns_host_name);
279                 service_principal_name[1] = talloc_asprintf(tmp_ctx, "host/%s", strlower_talloc(tmp_ctx, r->in.netbios_name));
280                 service_principal_name[2] = talloc_asprintf(tmp_ctx, "host/%s/%s", dns_host_name, realm);
281                 service_principal_name[3] = talloc_asprintf(tmp_ctx, "host/%s/%s", strlower_talloc(tmp_ctx, r->in.netbios_name), realm);
282                 service_principal_name[4] = talloc_asprintf(tmp_ctx, "host/%s/%s", dns_host_name, r->out.domain_name);
283                 service_principal_name[5] = talloc_asprintf(tmp_ctx, "host/%s/%s", strlower_talloc(tmp_ctx, r->in.netbios_name), r->out.domain_name);
284                 
285                 for (i=0; i < ARRAY_SIZE(service_principal_name); i++) {
286                         if (!service_principal_name[i]) {
287                                 r->out.error_string = NULL;
288                                 talloc_free(tmp_ctx);
289                                 return NT_STATUS_NO_MEMORY;
290                         }
291                         rtn = samdb_msg_add_string(remote_ldb, tmp_ctx, msg, "servicePrincipalName", service_principal_name[i]);
292                         if (rtn == -1) {
293                                 r->out.error_string = NULL;
294                                 talloc_free(tmp_ctx);
295                                 return NT_STATUS_NO_MEMORY;
296                         }
297                 }
298
299                 rtn = samdb_msg_add_string(remote_ldb, tmp_ctx, msg, "dNSHostName", dns_host_name);
300                 if (rtn == -1) {
301                         r->out.error_string = NULL;
302                         talloc_free(tmp_ctx);
303                         return NT_STATUS_NO_MEMORY;
304                 }
305
306                 rtn = samdb_replace(remote_ldb, tmp_ctx, msg);
307                 if (rtn != 0) {
308                         r->out.error_string
309                                 = talloc_asprintf(r, 
310                                                   "Failed to replace entries on %s\n", 
311                                                   ldb_dn_linearize(tmp_ctx, msg->dn));
312                         talloc_free(tmp_ctx);
313                         return NT_STATUS_INTERNAL_DB_CORRUPTION;
314                 }
315         }
316                                 
317         /* DsCrackNames to find out the DN of the domain. */
318         r_crack_names.in.req.req1.format_offered = DRSUAPI_DS_NAME_FORMAT_NT4_ACCOUNT;
319         r_crack_names.in.req.req1.format_desired = DRSUAPI_DS_NAME_FORMAT_FQDN_1779;
320         names[0].str = talloc_asprintf(tmp_ctx, "%s\\", r->out.domain_name);
321         if (!names[0].str) {
322                 r->out.error_string = NULL;
323                 talloc_free(tmp_ctx);
324                 return NT_STATUS_NO_MEMORY;
325         }
326
327         status = dcerpc_drsuapi_DsCrackNames(drsuapi_pipe, tmp_ctx, &r_crack_names);
328         if (!NT_STATUS_IS_OK(status)) {
329                 if (NT_STATUS_EQUAL(status, NT_STATUS_NET_WRITE_FAULT)) {
330                         r->out.error_string
331                                 = talloc_asprintf(r,
332                                                   "dcerpc_drsuapi_DsCrackNames for [%s] failed - %s\n", 
333                                                   r->in.domain_name, 
334                                                   dcerpc_errstr(tmp_ctx, drsuapi_pipe->last_fault_code));
335                         talloc_free(tmp_ctx);
336                         return status;
337                 } else {
338                         r->out.error_string
339                                 = talloc_asprintf(r,
340                                                   "dcerpc_drsuapi_DsCrackNames for [%s] failed - %s\n", 
341                                                   r->in.domain_name, 
342                                                   nt_errstr(status));
343                         talloc_free(tmp_ctx);
344                         return status;
345                 }
346         } else if (!W_ERROR_IS_OK(r_crack_names.out.result)) {
347                 r->out.error_string
348                         = talloc_asprintf(r,
349                                           "DsCrackNames failed - %s\n", win_errstr(r_crack_names.out.result));
350                 talloc_free(tmp_ctx);
351                 return NT_STATUS_UNSUCCESSFUL;
352         } else if (r_crack_names.out.level != 1 
353                    || !r_crack_names.out.ctr.ctr1 
354                    || r_crack_names.out.ctr.ctr1->count != 1
355                    || !r_crack_names.out.ctr.ctr1->array[0].result_name           
356                    || r_crack_names.out.ctr.ctr1->array[0].status != DRSUAPI_DS_NAME_STATUS_OK) {
357                 r->out.error_string = talloc_asprintf(r, "DsCrackNames failed\n");
358                 talloc_free(tmp_ctx);
359                 return NT_STATUS_UNSUCCESSFUL;
360         }
361
362         /* Store the account DN. */
363         r->out.account_dn_str = account_dn_str;
364         talloc_steal(r, account_dn_str);
365
366         /* Store the domain DN. */
367         r->out.domain_dn_str = r_crack_names.out.ctr.ctr1->array[0].result_name;
368         talloc_steal(r, r_crack_names.out.ctr.ctr1->array[0].result_name);
369
370         r->out.kvno = kvno;
371
372         if (r->in.acct_type == ACB_SVRTRUST) {
373                 status = libnet_JoinSite(remote_ldb, r);
374         }
375         talloc_free(tmp_ctx);
376
377         return status;
378 }
379
380 /*
381  * do a domain join using DCERPC/SAMR calls
382  * - connect to the LSA pipe, to try and find out information about the domain
383  * - create a secondary connection to SAMR pipe
384  * - do a samr_Connect to get a policy handle
385  * - do a samr_LookupDomain to get the domain sid
386  * - do a samr_OpenDomain to get a domain handle
387  * - do a samr_CreateAccount to try and get a new account 
388  * 
389  * If that fails, do:
390  * - do a samr_LookupNames to get the users rid
391  * - do a samr_OpenUser to get a user handle
392  * - potentially delete and recreate the user
393  * - assert the account is of the right type with samrQueryUserInfo
394  * 
395  * - call libnet_SetPassword_samr_handle to set the password
396  *
397  * - do a samrSetUserInfo to set the account flags
398  * - do some ADS specific things when we join as Domain Controller,
399  *    look at libnet_joinADSDomain() for the details
400  */
401 NTSTATUS libnet_JoinDomain(struct libnet_context *ctx, TALLOC_CTX *mem_ctx, struct libnet_JoinDomain *r)
402 {
403         TALLOC_CTX *tmp_ctx;
404
405         NTSTATUS status, cu_status;
406
407         struct libnet_RpcConnectDCInfo *connect_with_info;
408         struct dcerpc_pipe *samr_pipe;
409
410         struct samr_Connect sc;
411         struct policy_handle p_handle;
412         struct samr_OpenDomain od;
413         struct policy_handle d_handle;
414         struct samr_LookupNames ln;
415         struct samr_OpenUser ou;
416         struct samr_CreateUser2 cu;
417         struct policy_handle *u_handle = NULL;
418         struct samr_QueryUserInfo qui;
419         struct samr_SetUserInfo sui;
420         union samr_UserInfo u_info;
421         union libnet_SetPassword r2;
422         struct samr_GetUserPwInfo pwp;
423         struct lsa_String samr_account_name;
424         
425         uint32_t acct_flags, old_acct_flags;
426         uint32_t rid, access_granted;
427         int policy_min_pw_len = 0;
428
429         struct dom_sid *account_sid = NULL;
430         const char *password_str = NULL;
431         
432         r->out.error_string = NULL;
433         r2.samr_handle.out.error_string = NULL;
434         
435         tmp_ctx = talloc_named(mem_ctx, 0, "libnet_Join temp context");
436         if (!tmp_ctx) {
437                 r->out.error_string = NULL;
438                 return NT_STATUS_NO_MEMORY;
439         }
440         
441         u_handle = talloc(tmp_ctx, struct policy_handle);
442         if (!u_handle) {
443                 r->out.error_string = NULL;
444                 talloc_free(tmp_ctx);
445                 return NT_STATUS_NO_MEMORY;
446         }
447         
448         connect_with_info = talloc(tmp_ctx, struct libnet_RpcConnectDCInfo);
449         if (!connect_with_info) {
450                 r->out.error_string = NULL;
451                 talloc_free(tmp_ctx);
452                 return NT_STATUS_NO_MEMORY;
453         }
454
455         /* prepare connect to the LSA pipe of PDC */
456         if (r->in.level == LIBNET_JOINDOMAIN_AUTOMATIC) {
457                 connect_with_info->level      = LIBNET_RPC_CONNECT_PDC;
458                 connect_with_info->in.name    = r->in.domain_name;
459         } else {
460                 connect_with_info->level      = LIBNET_RPC_CONNECT_BINDING;
461                 connect_with_info->in.binding = r->in.binding;
462         }
463
464         connect_with_info->in.dcerpc_iface    = &dcerpc_table_samr;
465         /*
466           establish a SAMR connection, on the same CIFS transport
467         */
468         
469         status = libnet_RpcConnectDCInfo(ctx, connect_with_info);
470         if (!NT_STATUS_IS_OK(status)) {
471                 if (r->in.binding) {
472                         r->out.error_string = talloc_asprintf(mem_ctx,
473                                                               "Connection to SAMR pipe of DC %s failed: %s",
474                                                               r->in.binding, connect_with_info->out.error_string);
475                 } else {
476                         r->out.error_string = talloc_asprintf(mem_ctx,
477                                                               "Connection to SAMR pipe of PDC for %s failed: %s",
478                                                               r->in.domain_name, connect_with_info->out.error_string);
479                 }
480                 talloc_free(tmp_ctx);
481                 return status;
482         }
483
484         samr_pipe = connect_with_info->out.dcerpc_pipe, 
485
486         status = dcerpc_pipe_auth(samr_pipe,
487                                   connect_with_info->out.dcerpc_pipe->binding, 
488                                   &dcerpc_table_samr, ctx->cred);
489         if (!NT_STATUS_IS_OK(status)) {
490                 r->out.error_string = talloc_asprintf(mem_ctx,
491                                                 "SAMR bind failed: %s",
492                                                 nt_errstr(status));
493                 talloc_free(tmp_ctx);
494                 return status;
495         }
496
497         /* prepare samr_Connect */
498         ZERO_STRUCT(p_handle);
499         sc.in.system_name = NULL;
500         sc.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
501         sc.out.connect_handle = &p_handle;
502
503         /* 2. do a samr_Connect to get a policy handle */
504         status = dcerpc_samr_Connect(samr_pipe, tmp_ctx, &sc);
505         if (!NT_STATUS_IS_OK(status)) {
506                 r->out.error_string = talloc_asprintf(mem_ctx,
507                                                 "samr_Connect failed: %s",
508                                                 nt_errstr(status));
509                 talloc_free(tmp_ctx);
510                 return status;
511         }
512
513         /* If this is a connection on ncacn_ip_tcp to Win2k3 SP1, we don't get back this useful info */
514         if (!connect_with_info->out.domain_name) {
515                 if (r->in.level == LIBNET_JOINDOMAIN_AUTOMATIC) {
516                         connect_with_info->out.domain_name = talloc_strdup(tmp_ctx, r->in.domain_name);
517                 } else {
518                         /* Bugger, we just lost our way to automaticly find the domain name */
519                         connect_with_info->out.domain_name = talloc_strdup(tmp_ctx, lp_workgroup());
520                 }
521         }
522
523         /* Perhaps we didn't get a SID above, because we are against ncacn_ip_tcp */
524         if (!connect_with_info->out.domain_sid) {
525                 struct lsa_String name;
526                 struct samr_LookupDomain l;
527                 name.string = connect_with_info->out.domain_name;
528                 l.in.connect_handle = &p_handle;
529                 l.in.domain_name = &name;
530                 
531                 status = dcerpc_samr_LookupDomain(samr_pipe, tmp_ctx, &l);
532                 if (!NT_STATUS_IS_OK(status)) {
533                         r->out.error_string = talloc_asprintf(mem_ctx,
534                                                               "SAMR LookupDomain failed: %s",
535                                                               nt_errstr(status));
536                         talloc_free(tmp_ctx);
537                         return status;
538                 }
539                 connect_with_info->out.domain_sid = l.out.sid;
540         }
541
542         /* prepare samr_OpenDomain */
543         ZERO_STRUCT(d_handle);
544         od.in.connect_handle = &p_handle;
545         od.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
546         od.in.sid = connect_with_info->out.domain_sid;
547         od.out.domain_handle = &d_handle;
548
549         /* do a samr_OpenDomain to get a domain handle */
550         status = dcerpc_samr_OpenDomain(samr_pipe, tmp_ctx, &od);                       
551         if (!NT_STATUS_IS_OK(status)) {
552                 r->out.error_string = talloc_asprintf(mem_ctx,
553                                                       "samr_OpenDomain for [%s] failed: %s",
554                                                       dom_sid_string(tmp_ctx, connect_with_info->out.domain_sid),
555                                                       nt_errstr(status));
556                 talloc_free(tmp_ctx);
557                 return status;
558         }
559         
560         /* prepare samr_CreateUser2 */
561         ZERO_STRUCTP(u_handle);
562         cu.in.domain_handle  = &d_handle;
563         cu.in.access_mask     = SEC_FLAG_MAXIMUM_ALLOWED;
564         samr_account_name.string = r->in.account_name;
565         cu.in.account_name    = &samr_account_name;
566         cu.in.acct_flags      = r->in.acct_type;
567         cu.out.user_handle    = u_handle;
568         cu.out.rid            = &rid;
569         cu.out.access_granted = &access_granted;
570
571         /* do a samr_CreateUser2 to get an account handle, or an error */
572         cu_status = dcerpc_samr_CreateUser2(samr_pipe, tmp_ctx, &cu);                   
573         status = cu_status;
574         if (NT_STATUS_EQUAL(status, NT_STATUS_USER_EXISTS)) {
575                 /* prepare samr_LookupNames */
576                 ln.in.domain_handle = &d_handle;
577                 ln.in.num_names = 1;
578                 ln.in.names = talloc_array(tmp_ctx, struct lsa_String, 1);
579                 if (!ln.in.names) {
580                         r->out.error_string = NULL;
581                         talloc_free(tmp_ctx);
582                         return NT_STATUS_NO_MEMORY;
583                 }
584                 ln.in.names[0].string = r->in.account_name;
585                 
586                 /* 5. do a samr_LookupNames to get the users rid */
587                 status = dcerpc_samr_LookupNames(samr_pipe, tmp_ctx, &ln);
588                 if (!NT_STATUS_IS_OK(status)) {
589                         r->out.error_string = talloc_asprintf(mem_ctx,
590                                                 "samr_LookupNames for [%s] failed: %s",
591                                                 r->in.account_name,
592                                                 nt_errstr(status));
593                         talloc_free(tmp_ctx);
594                         return status;
595                 }
596                 
597                 /* check if we got one RID for the user */
598                 if (ln.out.rids.count != 1) {
599                         r->out.error_string = talloc_asprintf(mem_ctx,
600                                                               "samr_LookupNames for [%s] returns %d RIDs\n",
601                                                               r->in.account_name, ln.out.rids.count);
602                         talloc_free(tmp_ctx);
603                         return NT_STATUS_INVALID_PARAMETER;
604                 }
605                 
606                 /* prepare samr_OpenUser */
607                 ZERO_STRUCTP(u_handle);
608                 ou.in.domain_handle = &d_handle;
609                 ou.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
610                 ou.in.rid = ln.out.rids.ids[0];
611                 rid = ou.in.rid;
612                 ou.out.user_handle = u_handle;
613                 
614                 /* 6. do a samr_OpenUser to get a user handle */
615                 status = dcerpc_samr_OpenUser(samr_pipe, tmp_ctx, &ou); 
616                 if (!NT_STATUS_IS_OK(status)) {
617                         r->out.error_string = talloc_asprintf(mem_ctx,
618                                                         "samr_OpenUser for [%s] failed: %s",
619                                                         r->in.account_name,
620                                                         nt_errstr(status));
621                         talloc_free(tmp_ctx);
622                         return status;
623                 }
624
625                 if (r->in.recreate_account) {
626                         struct samr_DeleteUser d;
627                         d.in.user_handle = u_handle;
628                         d.out.user_handle = u_handle;
629                         status = dcerpc_samr_DeleteUser(samr_pipe, mem_ctx, &d);
630                         if (!NT_STATUS_IS_OK(status)) {
631                                 r->out.error_string = talloc_asprintf(mem_ctx,
632                                                                       "samr_DeleteUser (for recreate) of [%s] failed: %s",
633                                                                       r->in.account_name,
634                                                                       nt_errstr(status));
635                                 talloc_free(tmp_ctx);
636                                 return status;
637                         }
638
639                         /* We want to recreate, so delete and another samr_CreateUser2 */
640                         
641                         /* &cu filled in above */
642                         status = dcerpc_samr_CreateUser2(samr_pipe, tmp_ctx, &cu);                      
643                         if (!NT_STATUS_IS_OK(status)) {
644                                 r->out.error_string = talloc_asprintf(mem_ctx,
645                                                                       "samr_CreateUser2 (recreate) for [%s] failed: %s\n",
646                                                                       r->in.domain_name, nt_errstr(status));
647                                 talloc_free(tmp_ctx);
648                                 return status;
649                         }
650                 }
651         } else if (!NT_STATUS_IS_OK(status)) {
652                 r->out.error_string = talloc_asprintf(mem_ctx,
653                                                       "samr_CreateUser2 for [%s] failed: %s\n",
654                                                       r->in.domain_name, nt_errstr(status));
655                 talloc_free(tmp_ctx);
656                 return status;
657         }
658
659         /* prepare samr_QueryUserInfo (get flags) */
660         qui.in.user_handle = u_handle;
661         qui.in.level = 16;
662         
663         status = dcerpc_samr_QueryUserInfo(samr_pipe, tmp_ctx, &qui);
664         if (!NT_STATUS_IS_OK(status)) {
665                 r->out.error_string = talloc_asprintf(mem_ctx,
666                                                 "samr_QueryUserInfo for [%s] failed: %s",
667                                                 r->in.account_name,
668                                                 nt_errstr(status));
669                 talloc_free(tmp_ctx);
670                 return status;
671         }
672         
673         if (!qui.out.info) {
674                 status = NT_STATUS_INVALID_PARAMETER;
675                 r->out.error_string
676                         = talloc_asprintf(mem_ctx,
677                                           "samr_QueryUserInfo failed to return qui.out.info for [%s]: %s\n",
678                                           r->in.account_name, nt_errstr(status));
679                 talloc_free(tmp_ctx);
680                 return status;
681         }
682
683         old_acct_flags = (qui.out.info->info16.acct_flags & (ACB_WSTRUST | ACB_SVRTRUST | ACB_DOMTRUST));
684         /* Possibly bail if the account is of the wrong type */
685         if (old_acct_flags
686             != r->in.acct_type) {
687                 const char *old_account_type, *new_account_type;
688                 switch (old_acct_flags) {
689                 case ACB_WSTRUST:
690                         old_account_type = "domain member (member)";
691                         break;
692                 case ACB_SVRTRUST:
693                         old_account_type = "domain controller (bdc)";
694                         break;
695                 case ACB_DOMTRUST:
696                         old_account_type = "trusted domain";
697                         break;
698                 default:
699                         return NT_STATUS_INVALID_PARAMETER;
700                 }
701                 switch (r->in.acct_type) {
702                 case ACB_WSTRUST:
703                         new_account_type = "domain member (member)";
704                         break;
705                 case ACB_SVRTRUST:
706                         new_account_type = "domain controller (bdc)";
707                         break;
708                 case ACB_DOMTRUST:
709                         new_account_type = "trusted domain";
710                         break;
711                 default:
712                         return NT_STATUS_INVALID_PARAMETER;
713                 }
714
715                 if (!NT_STATUS_EQUAL(cu_status, NT_STATUS_USER_EXISTS)) {
716                         /* We created a new user, but they didn't come out the right type?!? */
717                         r->out.error_string
718                                 = talloc_asprintf(mem_ctx,
719                                                   "We asked to create a new machine account (%s) of type %s, "
720                                                   "but we got an account of type %s.  This is unexpected.  "
721                                                   "Perhaps delete the account and try again.\n",
722                                                   r->in.account_name, new_account_type, old_account_type);
723                         talloc_free(tmp_ctx);
724                         return NT_STATUS_INVALID_PARAMETER;
725                 } else {
726                         /* The account is of the wrong type, so bail */
727
728                         /* TODO: We should allow a --force option to override, and redo this from the top setting r.in.recreate_account */
729                         r->out.error_string
730                                 = talloc_asprintf(mem_ctx,
731                                                   "The machine account (%s) already exists in the domain %s, "
732                                                   "but is a %s.  You asked to join as a %s.  Please delete "
733                                                   "the account and try again.\n",
734                                                   r->in.account_name, connect_with_info->out.domain_name, old_account_type, new_account_type);
735                         talloc_free(tmp_ctx);
736                         return NT_STATUS_USER_EXISTS;
737                 }
738         } else {
739                 acct_flags = qui.out.info->info16.acct_flags;
740         }
741         
742         acct_flags = (acct_flags & ~ACB_DISABLED);
743
744         /* Find out what password policy this user has */
745         pwp.in.user_handle = u_handle;
746
747         status = dcerpc_samr_GetUserPwInfo(samr_pipe, tmp_ctx, &pwp);                           
748         if (NT_STATUS_IS_OK(status)) {
749                 policy_min_pw_len = pwp.out.info.min_password_length;
750         }
751         
752         /* Grab a password of that minimum length */
753         
754         password_str = generate_random_str(tmp_ctx, MAX(8, policy_min_pw_len)); 
755
756         r2.samr_handle.level            = LIBNET_SET_PASSWORD_SAMR_HANDLE;
757         r2.samr_handle.in.account_name  = r->in.account_name;
758         r2.samr_handle.in.newpassword   = password_str;
759         r2.samr_handle.in.user_handle   = u_handle;
760         r2.samr_handle.in.dcerpc_pipe   = samr_pipe;
761
762         status = libnet_SetPassword(ctx, tmp_ctx, &r2); 
763         if (!NT_STATUS_IS_OK(status)) {
764                 r->out.error_string = talloc_steal(mem_ctx, r2.samr_handle.out.error_string);
765                 talloc_free(tmp_ctx);
766                 return status;
767         }
768
769         /* reset flags (if required) */
770         if (acct_flags != qui.out.info->info16.acct_flags) {
771                 ZERO_STRUCT(u_info);
772                 u_info.info16.acct_flags = acct_flags;
773
774                 sui.in.user_handle = u_handle;
775                 sui.in.info = &u_info;
776                 sui.in.level = 16;
777                 
778                 dcerpc_samr_SetUserInfo(samr_pipe, tmp_ctx, &sui);
779                 if (!NT_STATUS_IS_OK(status)) {
780                         r->out.error_string = talloc_asprintf(mem_ctx,
781                                                         "samr_SetUserInfo for [%s] failed to remove ACB_DISABLED flag: %s",
782                                                         r->in.account_name,
783                                                         nt_errstr(status));
784                         talloc_free(tmp_ctx);
785                         return status;
786                 }
787         }
788
789         account_sid = dom_sid_add_rid(mem_ctx, connect_with_info->out.domain_sid, rid);
790         if (!account_sid) {
791                 r->out.error_string = NULL;
792                 talloc_free(tmp_ctx);
793                 return NT_STATUS_NO_MEMORY;
794         }
795
796         /* Finish out by pushing various bits of status data out for the caller to use */
797         r->out.join_password = password_str;
798         talloc_steal(mem_ctx, r->out.join_password);
799
800         r->out.domain_sid = connect_with_info->out.domain_sid;
801         talloc_steal(mem_ctx, r->out.domain_sid);
802
803         r->out.account_sid = account_sid;
804         talloc_steal(mem_ctx, r->out.account_sid);
805
806         r->out.domain_name = connect_with_info->out.domain_name;
807         talloc_steal(mem_ctx, r->out.domain_name);
808         r->out.realm = connect_with_info->out.realm;
809         talloc_steal(mem_ctx, r->out.realm);
810         r->out.samr_pipe = samr_pipe;
811         talloc_steal(mem_ctx, samr_pipe);
812         r->out.samr_binding = samr_pipe->binding;
813         talloc_steal(mem_ctx, r->out.samr_binding);
814         r->out.user_handle = u_handle;
815         talloc_steal(mem_ctx, u_handle);
816         r->out.error_string = r2.samr_handle.out.error_string;
817         talloc_steal(mem_ctx, r2.samr_handle.out.error_string);
818         r->out.kvno = 0;
819         r->out.server_dn_str = NULL;
820         talloc_free(tmp_ctx); 
821
822         /* Now, if it was AD, then we want to start looking changing a
823          * few more things.  Otherwise, we are done. */
824         if (r->out.realm) {
825                 status = libnet_JoinADSDomain(ctx, r);
826                 return status;
827         }
828
829         return status;
830 }
831
832 static NTSTATUS libnet_Join_primary_domain(struct libnet_context *ctx, 
833                                            TALLOC_CTX *mem_ctx, 
834                                            struct libnet_Join *r)
835 {
836         NTSTATUS status;
837         TALLOC_CTX *tmp_mem;
838         struct libnet_JoinDomain *r2;
839         int ret, rtn;
840         struct ldb_context *ldb;
841         const struct ldb_dn *base_dn;
842         struct ldb_message **msgs, *msg;
843         const char *sct;
844         const char * const attrs[] = {
845                 "whenChanged",
846                 "secret",
847                 "priorSecret",
848                 "priorChanged",
849                 "krb5Keytab",
850                 "privateKeytab",
851                 NULL
852         };
853         uint32_t acct_type = 0;
854         const char *account_name;
855         const char *netbios_name;
856         char *filter;
857         
858         r->out.error_string = NULL;
859
860         tmp_mem = talloc_new(mem_ctx);
861         if (!tmp_mem) {
862                 return NT_STATUS_NO_MEMORY;
863         }
864
865         r2 = talloc(tmp_mem, struct libnet_JoinDomain);
866         if (!r2) {
867                 r->out.error_string = NULL;
868                 talloc_free(tmp_mem);
869                 return NT_STATUS_NO_MEMORY;
870         }
871         
872         if (r->in.join_type == SEC_CHAN_BDC) {
873                 acct_type = ACB_SVRTRUST;
874         } else if (r->in.join_type == SEC_CHAN_WKSTA) {
875                 acct_type = ACB_WSTRUST;
876         } else {
877                 r->out.error_string = NULL;
878                 talloc_free(tmp_mem);   
879                 return NT_STATUS_INVALID_PARAMETER;
880         }
881
882         if (r->in.netbios_name != NULL) {
883                 netbios_name = r->in.netbios_name;
884         } else {
885                 netbios_name = talloc_reference(tmp_mem, lp_netbios_name());
886                 if (!netbios_name) {
887                         r->out.error_string = NULL;
888                         talloc_free(tmp_mem);
889                         return NT_STATUS_NO_MEMORY;
890                 }
891         }
892
893         account_name = talloc_asprintf(tmp_mem, "%s$", netbios_name);
894         if (!account_name) {
895                 r->out.error_string = NULL;
896                 talloc_free(tmp_mem);
897                 return NT_STATUS_NO_MEMORY;
898         }
899         
900         /*
901          * Local secrets are stored in secrets.ldb 
902          * open it to make sure we can write the info into it after the join
903          */
904         ldb = secrets_db_connect(tmp_mem);
905         if (!ldb) {
906                 r->out.error_string
907                         = talloc_asprintf(mem_ctx, 
908                                           "Could not open secrets database\n");
909                 talloc_free(tmp_mem);
910                 return NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
911         }
912
913         /*
914          * join the domain
915          */
916         ZERO_STRUCTP(r2);
917         r2->in.domain_name      = r->in.domain_name;
918         r2->in.account_name     = account_name;
919         r2->in.netbios_name     = netbios_name;
920         r2->in.level            = LIBNET_JOINDOMAIN_AUTOMATIC;
921         r2->in.acct_type        = acct_type;
922         r2->in.recreate_account = False;
923         status = libnet_JoinDomain(ctx, r2, r2);
924         if (!NT_STATUS_IS_OK(status)) {
925                 r->out.error_string = talloc_steal(mem_ctx, r2->out.error_string);
926                 talloc_free(tmp_mem);
927                 return status;
928         }
929         
930         /*
931          * now prepare the record for secrets.ldb
932          */
933         sct = talloc_asprintf(tmp_mem, "%d", r->in.join_type); 
934         if (!sct) {
935                 r->out.error_string = NULL;
936                 talloc_free(tmp_mem);
937                 return NT_STATUS_NO_MEMORY;
938         }
939         
940         msg = ldb_msg_new(tmp_mem);
941         if (!msg) {
942                 r->out.error_string = NULL;
943                 talloc_free(tmp_mem);
944                 return NT_STATUS_NO_MEMORY;
945         }
946
947         base_dn = ldb_dn_explode(tmp_mem, "cn=Primary Domains");
948         if (!base_dn) {
949                 r->out.error_string = NULL;
950                 talloc_free(tmp_mem);
951                 return NT_STATUS_NO_MEMORY;
952         }
953
954         msg->dn = ldb_dn_build_child(tmp_mem, "flatname", r2->out.domain_name, base_dn);
955         if (!msg->dn) {
956                 r->out.error_string = NULL;
957                 talloc_free(tmp_mem);
958                 return NT_STATUS_NO_MEMORY;
959         }
960         
961         rtn = samdb_msg_add_string(ldb, tmp_mem, msg, "flatname", r2->out.domain_name);
962         if (rtn == -1) {
963                 r->out.error_string = NULL;
964                 talloc_free(tmp_mem);
965                 return NT_STATUS_NO_MEMORY;
966         }
967
968         if (r2->out.realm) {
969                 rtn = samdb_msg_add_string(ldb, tmp_mem, msg, "realm", r2->out.realm);
970                 if (rtn == -1) {
971                         r->out.error_string = NULL;
972                         talloc_free(tmp_mem);
973                         return NT_STATUS_NO_MEMORY;
974                 }
975
976                 rtn = samdb_msg_add_string(ldb, tmp_mem, msg, "objectClass", "primaryDomain");
977                 if (rtn == -1) {
978                         r->out.error_string = NULL;
979                         talloc_free(tmp_mem);
980                         return NT_STATUS_NO_MEMORY;
981                 }
982         }
983
984         rtn = samdb_msg_add_string(ldb, tmp_mem, msg, "objectClass", "primaryDomain");
985         if (rtn == -1) {
986                 r->out.error_string = NULL;
987                 talloc_free(tmp_mem);
988                 return NT_STATUS_NO_MEMORY;
989         }
990
991         rtn = samdb_msg_add_string(ldb, tmp_mem, msg, "secret", r2->out.join_password);
992         if (rtn == -1) {
993                 r->out.error_string = NULL;
994                 talloc_free(tmp_mem);
995                 return NT_STATUS_NO_MEMORY;
996         }
997
998         rtn = samdb_msg_add_string(ldb, tmp_mem, msg, "samAccountName", r2->in.account_name);
999         if (rtn == -1) {
1000                 r->out.error_string = NULL;
1001                 talloc_free(tmp_mem);
1002                 return NT_STATUS_NO_MEMORY;
1003         }
1004
1005         rtn = samdb_msg_add_string(ldb, tmp_mem, msg, "secureChannelType", sct);
1006         if (rtn == -1) {
1007                 r->out.error_string = NULL;
1008                 talloc_free(tmp_mem);
1009                 return NT_STATUS_NO_MEMORY;
1010         }
1011
1012         if (r2->out.kvno) {
1013                 rtn = samdb_msg_add_uint(ldb, tmp_mem, msg, "msDS-KeyVersionNumber",
1014                                          r2->out.kvno);
1015                 if (rtn == -1) {
1016                         r->out.error_string = NULL;
1017                         talloc_free(tmp_mem);
1018                         return NT_STATUS_NO_MEMORY;
1019                 }
1020         }
1021
1022         if (r2->out.domain_sid) {
1023                 rtn = samdb_msg_add_dom_sid(ldb, tmp_mem, msg, "objectSid",
1024                                             r2->out.domain_sid);
1025                 if (rtn == -1) {
1026                         r->out.error_string = NULL;
1027                         talloc_free(tmp_mem);
1028                         return NT_STATUS_NO_MEMORY;
1029                 }
1030         }
1031
1032         /* 
1033          * search for the secret record
1034          * - remove the records we find
1035          * - and fetch the old secret and store it under priorSecret
1036          */
1037         ret = gendb_search(ldb,
1038                            tmp_mem, base_dn,
1039                            &msgs, attrs,
1040                            "(|" SECRETS_PRIMARY_DOMAIN_FILTER "(realm=%s))",
1041                            r2->out.domain_name, r2->out.realm);
1042         if (ret == 0) {
1043                 rtn = samdb_msg_set_string(ldb, tmp_mem, msg, "secretsKeytab", "secrets.keytab");
1044                 if (rtn == -1) {
1045                         r->out.error_string = NULL;
1046                         talloc_free(tmp_mem);
1047                         return NT_STATUS_NO_MEMORY;
1048                 }
1049         } else if (ret == -1) {
1050                 r->out.error_string
1051                         = talloc_asprintf(mem_ctx, 
1052                                           "Search for domain: %s and realm: %s failed: %s", 
1053                                           r2->out.domain_name, r2->out.realm, ldb_errstring(ldb));
1054                 talloc_free(tmp_mem);
1055                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1056         } else {
1057                 const struct ldb_val *private_keytab;
1058                 const struct ldb_val *krb5_keytab;
1059                 const struct ldb_val *prior_secret;
1060                 const struct ldb_val *prior_modified_time;
1061                 int i;
1062
1063                 for (i = 0; i < ret; i++) {
1064                         ldb_delete(ldb, msgs[i]->dn);
1065                 }
1066
1067                 prior_secret = ldb_msg_find_ldb_val(msgs[0], "secret");
1068                 if (prior_secret) {
1069                         rtn = samdb_msg_set_value(ldb, tmp_mem, msg, "priorSecret", prior_secret);
1070                         if (rtn == -1) {
1071                                 r->out.error_string = NULL;
1072                                 talloc_free(tmp_mem);
1073                                 return NT_STATUS_NO_MEMORY;
1074                         }
1075                 }
1076                 rtn = samdb_msg_set_string(ldb, tmp_mem, msg, "secret", r2->out.join_password);
1077                 if (rtn == -1) {
1078                         r->out.error_string = NULL;
1079                         talloc_free(tmp_mem);
1080                         return NT_STATUS_NO_MEMORY;
1081                 }
1082
1083                 prior_modified_time = ldb_msg_find_ldb_val(msgs[0], 
1084                                                            "whenChanged");
1085                 if (prior_modified_time) {
1086                         rtn = samdb_msg_set_value(ldb, tmp_mem, msg, "priorWhenChanged", 
1087                                                   prior_modified_time);
1088                         if (rtn == -1) {
1089                                 r->out.error_string = NULL;
1090                                 talloc_free(tmp_mem);
1091                                 return NT_STATUS_NO_MEMORY;
1092                         }
1093                 }
1094
1095                 rtn = samdb_msg_set_string(ldb, tmp_mem, msg, "samAccountName", r2->in.account_name);
1096                 if (rtn == -1) {
1097                         r->out.error_string = NULL;
1098                         talloc_free(tmp_mem);
1099                         return NT_STATUS_NO_MEMORY;
1100                 }
1101
1102                 rtn = samdb_msg_set_string(ldb, tmp_mem, msg, "secureChannelType", sct);
1103                 if (rtn == -1) {
1104                         r->out.error_string = NULL;
1105                         talloc_free(tmp_mem);
1106                         return NT_STATUS_NO_MEMORY;
1107                 }
1108
1109                 /* We will want to keep the keytab names */
1110                 private_keytab = ldb_msg_find_ldb_val(msgs[0], "privateKeytab");
1111                 if (private_keytab) {
1112                         rtn = samdb_msg_set_value(ldb, tmp_mem, msg, "privateKeytab", private_keytab);
1113                         if (rtn == -1) {
1114                                 r->out.error_string = NULL;
1115                                 talloc_free(tmp_mem);
1116                                 return NT_STATUS_NO_MEMORY;
1117                         }
1118                 }
1119                 krb5_keytab = ldb_msg_find_ldb_val(msgs[0], "krb5Keytab");
1120                 if (krb5_keytab) {
1121                         rtn = samdb_msg_set_value(ldb, tmp_mem, msg, "krb5Keytab", krb5_keytab);
1122                         if (rtn == -1) {
1123                                 r->out.error_string = NULL;
1124                                 talloc_free(tmp_mem);
1125                                 return NT_STATUS_NO_MEMORY;
1126                         }
1127                 }
1128         }
1129
1130         /* create the secret */
1131         ret = samdb_add(ldb, tmp_mem, msg);
1132         if (ret != 0) {
1133                 r->out.error_string = talloc_asprintf(mem_ctx, "Failed to create secret record %s\n", 
1134                                                       ldb_dn_linearize(ldb, msg->dn));
1135                 talloc_free(tmp_mem);
1136                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1137         }
1138
1139         if (r2->out.realm) {
1140                 struct cli_credentials *creds;
1141                 /* Make a credentials structure from it */
1142                 creds = cli_credentials_init(mem_ctx);
1143                 if (!creds) {
1144                         r->out.error_string = NULL;
1145                         talloc_free(tmp_mem);
1146                         return NT_STATUS_NO_MEMORY;
1147                 }
1148                 cli_credentials_set_conf(creds);
1149                 filter = talloc_asprintf(mem_ctx, "dn=%s", ldb_dn_linearize(mem_ctx, msg->dn));
1150                 status = cli_credentials_set_secrets(creds, NULL, filter);
1151                 if (!NT_STATUS_IS_OK(status)) {
1152                         r->out.error_string = talloc_asprintf(mem_ctx, "Failed to read secrets for keytab update for %s\n", 
1153                                                               filter);
1154                         talloc_free(tmp_mem);
1155                         return status;
1156                 } 
1157                 ret = cli_credentials_update_keytab(creds);
1158                 if (ret != 0) {
1159                         r->out.error_string = talloc_asprintf(mem_ctx, "Failed to update keytab for %s\n", 
1160                                                               filter);
1161                         talloc_free(tmp_mem);
1162                         return NT_STATUS_UNSUCCESSFUL;
1163                 }
1164         }
1165
1166         /* move all out parameter to the callers TALLOC_CTX */
1167         r->out.error_string     = NULL;
1168         r->out.join_password    = r2->out.join_password;
1169         talloc_steal(mem_ctx, r2->out.join_password);
1170         r->out.domain_sid       = r2->out.domain_sid;
1171         talloc_steal(mem_ctx, r2->out.domain_sid);
1172         r->out.domain_name      = r2->out.domain_name;
1173         talloc_steal(mem_ctx, r2->out.domain_name);
1174         talloc_free(tmp_mem);
1175         return NT_STATUS_OK;
1176 }
1177
1178 NTSTATUS libnet_Join(struct libnet_context *ctx, TALLOC_CTX *mem_ctx, struct libnet_Join *r)
1179 {
1180         switch (r->in.join_type) {
1181                 case SEC_CHAN_WKSTA:
1182                         return libnet_Join_primary_domain(ctx, mem_ctx, r);
1183                 case SEC_CHAN_BDC:
1184                         return libnet_Join_primary_domain(ctx, mem_ctx, r);
1185                 case SEC_CHAN_DOMAIN:
1186                         break;
1187         }
1188
1189         r->out.error_string = talloc_asprintf(mem_ctx,
1190                                               "Invalid join type specified (%08X) attempting to join domain %s",
1191                                               r->in.join_type, r->in.domain_name);
1192         return NT_STATUS_INVALID_PARAMETER;
1193 }