b72577fa846a565d1a0f47bfd71cfabe2ee08714
[kamenim/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  
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 2 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, write to the Free Software
19    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 */
21
22 #include "includes.h"
23 #include "libnet/libnet.h"
24 #include "librpc/gen_ndr/ndr_samr.h"
25 #include "librpc/gen_ndr/ndr_lsa.h"
26 #include "librpc/gen_ndr/ndr_drsuapi.h"
27 #include "lib/ldb/include/ldb.h"
28 #include "include/secrets.h"
29
30 /*
31  * do a domain join using DCERPC/SAMR calls
32  * 1. connect to the SAMR pipe of users domain PDC (maybe a standalone server or workstation)
33  *    is it correct to contact the the pdc of the domain of the user who's password should be set?
34  * 2. do a samr_Connect to get a policy handle
35  * 3. do a samr_LookupDomain to get the domain sid
36  * 4. do a samr_OpenDomain to get a domain handle
37  * 5. do a samr_CreateAccount to try and get a new account 
38  * 
39  * If that fails, do:
40  * 5.1. do a samr_LookupNames to get the users rid
41  * 5.2. do a samr_OpenUser to get a user handle
42  * 
43  * 6. call libnet_SetPassword_samr_handle to set the password
44  *
45  * 7. do a samrSetUserInfo to set the account flags
46  */
47 NTSTATUS libnet_JoinDomain(struct libnet_context *ctx, TALLOC_CTX *mem_ctx, struct libnet_JoinDomain *r)
48 {
49         TALLOC_CTX *tmp_ctx;
50
51         NTSTATUS status;
52         struct libnet_RpcConnect c;
53         struct lsa_ObjectAttribute attr;
54         struct lsa_QosInfo qos;
55         struct lsa_OpenPolicy2 lsa_open_policy;
56         struct policy_handle lsa_p_handle;
57         struct lsa_QueryInfoPolicy2 lsa_query_info2;
58         struct lsa_QueryInfoPolicy lsa_query_info;
59
60         struct dcerpc_binding *samr_binding;
61         struct dcerpc_pipe *samr_pipe;
62         struct samr_Connect sc;
63         struct policy_handle p_handle;
64         struct samr_OpenDomain od;
65         struct policy_handle d_handle;
66         struct samr_LookupNames ln;
67         struct samr_OpenUser ou;
68         struct samr_CreateUser2 cu;
69         struct policy_handle u_handle;
70         struct samr_QueryUserInfo qui;
71         struct samr_SetUserInfo sui;
72         union samr_UserInfo u_info;
73         union libnet_SetPassword r2;
74         struct samr_GetUserPwInfo pwp;
75         struct lsa_String samr_account_name;
76
77         struct dcerpc_pipe *drsuapi_pipe;
78         struct dcerpc_binding *drsuapi_binding;
79         struct drsuapi_DsBind r_drsuapi_bind;
80         struct drsuapi_DsCrackNames r_crack_names;
81         struct drsuapi_DsNameString names[1];
82         struct policy_handle drsuapi_bind_handle;
83         struct GUID drsuapi_bind_guid;
84
85         struct ldb_context *remote_ldb;
86
87         uint32_t acct_flags;
88         uint32_t rid, access_granted;
89         int policy_min_pw_len = 0;
90
91         struct dom_sid *domain_sid;
92         const char *domain_name;
93         const char *realm = NULL; /* Also flag for remote being AD */
94         const char *account_dn;
95
96         char *remote_ldb_url;
97         struct ldb_message **msgs, *msg;
98         int ldb_ret;
99
100         const char *attrs[] = {
101                 "msDS-KeyVersionNumber",
102                 "servicePrincipalName",
103                 "dNSHostName",
104                 NULL,
105         };
106
107         tmp_ctx = talloc_named(mem_ctx, 0, "libnet_Join temp context");
108         if (!tmp_ctx) {
109                 r->out.error_string = NULL;
110                 return NT_STATUS_NO_MEMORY;
111         }
112
113
114         /* prepare connect to the LSA pipe of PDC */
115         c.level                     = LIBNET_RPC_CONNECT_PDC;
116         c.in.domain_name            = r->in.domain_name;
117         c.in.dcerpc_iface_name      = DCERPC_LSARPC_NAME;
118         c.in.dcerpc_iface_uuid      = DCERPC_LSARPC_UUID;
119         c.in.dcerpc_iface_version   = DCERPC_LSARPC_VERSION;
120
121         /* connect to the LSA pipe of the PDC */
122         status = libnet_RpcConnect(ctx, tmp_ctx, &c);
123         if (!NT_STATUS_IS_OK(status)) {
124                 r->out.error_string = talloc_asprintf(mem_ctx,
125                                                 "Connection to LSA pipe of PDC of domain '%s' failed: %s",
126                                                 r->in.domain_name, nt_errstr(status));
127                 talloc_free(tmp_ctx);
128                 return status;
129         }
130
131         
132         /* Get an LSA policy handle */
133
134         ZERO_STRUCT(lsa_p_handle);
135         qos.len = 0;
136         qos.impersonation_level = 2;
137         qos.context_mode = 1;
138         qos.effective_only = 0;
139
140         attr.len = 0;
141         attr.root_dir = NULL;
142         attr.object_name = NULL;
143         attr.attributes = 0;
144         attr.sec_desc = NULL;
145         attr.sec_qos = &qos;
146
147         lsa_open_policy.in.attr = &attr;
148         lsa_open_policy.in.system_name = talloc_asprintf(tmp_ctx, "\\%s", lp_netbios_name());
149         lsa_open_policy.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
150         lsa_open_policy.out.handle = &lsa_p_handle;
151
152         status = dcerpc_lsa_OpenPolicy2(c.out.dcerpc_pipe, tmp_ctx, &lsa_open_policy);
153         if (!NT_STATUS_IS_OK(status)) {
154                 r->out.error_string = talloc_asprintf(mem_ctx,
155                                                 "lsa_OpenPolicy2 failed: %s",
156                                                 nt_errstr(status));
157                 talloc_free(tmp_ctx);
158                 return status;
159         }
160
161         /* Look to see if this is ADS (a fault indicates NT4 or Samba 3.0) */
162
163         lsa_query_info2.in.handle = &lsa_p_handle;
164         lsa_query_info2.in.level = LSA_POLICY_INFO_DNS;
165
166         status = dcerpc_lsa_QueryInfoPolicy2(c.out.dcerpc_pipe, tmp_ctx, 
167                                              &lsa_query_info2);
168         
169         if (!NT_STATUS_EQUAL(status, NT_STATUS_NET_WRITE_FAULT)) {
170                 if (!NT_STATUS_IS_OK(status)) {
171                         r->out.error_string = talloc_asprintf(mem_ctx,
172                                                               "lsa_QueryInfoPolicy2 failed: %s",
173                                                               nt_errstr(status));
174                         talloc_free(tmp_ctx);
175                         return status;
176                 }
177                 realm = lsa_query_info2.out.info->dns.dns_domain.string;
178         }
179
180         /* Grab the domain SID (regardless of the result of the previous call */
181
182         lsa_query_info.in.handle = &lsa_p_handle;
183         lsa_query_info.in.level = LSA_POLICY_INFO_DOMAIN;
184
185         status = dcerpc_lsa_QueryInfoPolicy(c.out.dcerpc_pipe, tmp_ctx, 
186                                              &lsa_query_info);
187         
188         if (!NT_STATUS_IS_OK(status)) {
189                 r->out.error_string = talloc_asprintf(mem_ctx,
190                                                       "lsa_QueryInfoPolicy2 failed: %s",
191                                                       nt_errstr(status));
192                 talloc_free(tmp_ctx);
193                 return status;
194         }
195         domain_sid = lsa_query_info.out.info->domain.sid;
196         domain_name = lsa_query_info.out.info->domain.name.string;
197         
198         r->out.domain_sid = talloc_steal(mem_ctx, domain_sid);
199         r->out.domain_name = talloc_steal(mem_ctx, domain_name);
200         r->out.realm = talloc_steal(mem_ctx, realm);
201
202         /*
203           establish a SAMR connection, on the same CIFS transport
204         */
205
206         /* Find the original binding string */
207         status = dcerpc_parse_binding(tmp_ctx, c.out.dcerpc_pipe->conn->binding_string, &samr_binding);
208         if (!NT_STATUS_IS_OK(status)) {
209                 r->out.error_string
210                         = talloc_asprintf(mem_ctx,
211                                           "Failed to parse dcerpc binding '%s'", 
212                                           c.out.dcerpc_pipe->conn->binding_string);
213                 talloc_free(tmp_ctx);
214                 return status;
215         }
216
217         /* Make binding string for samr, not the other pipe */
218         status = dcerpc_epm_map_binding(tmp_ctx, samr_binding, 
219                                         DCERPC_SAMR_UUID, DCERPC_SAMR_VERSION,
220                                         c.out.dcerpc_pipe->conn->event_ctx);
221         if (!NT_STATUS_IS_OK(status)) {
222                 r->out.error_string
223                         = talloc_asprintf(mem_ctx,
224                                           "Failed to map DCERPC/TCP NCACN_NP pipe for '%s' - %s", 
225                                           DCERPC_NETLOGON_UUID, nt_errstr(status));
226                 talloc_free(tmp_ctx);
227                 return status;
228         }
229
230         /* Setup a SAMR connection */
231         status = dcerpc_secondary_connection(c.out.dcerpc_pipe, &samr_pipe, samr_binding);
232         if (!NT_STATUS_IS_OK(status)) {
233                 r->out.error_string = talloc_asprintf(mem_ctx,
234                                                       "SAMR secondary connection failed: %s",
235                                                       nt_errstr(status));
236                 talloc_free(tmp_ctx);
237                 return status;
238         }
239
240         status = dcerpc_pipe_auth(samr_pipe, samr_binding, DCERPC_SAMR_UUID, 
241                                   DCERPC_SAMR_VERSION, ctx->cred);
242         if (!NT_STATUS_IS_OK(status)) {
243                 r->out.error_string = talloc_asprintf(mem_ctx,
244                                                       "SAMR bind failed: %s",
245                                                       nt_errstr(status));
246                 talloc_free(tmp_ctx);
247                 return status;
248         }
249
250         /* prepare samr_Connect */
251         ZERO_STRUCT(p_handle);
252         sc.in.system_name = NULL;
253         sc.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
254         sc.out.connect_handle = &p_handle;
255
256         /* 2. do a samr_Connect to get a policy handle */
257         status = dcerpc_samr_Connect(samr_pipe, tmp_ctx, &sc);
258         if (!NT_STATUS_IS_OK(status)) {
259                 r->out.error_string = talloc_asprintf(mem_ctx,
260                                                 "samr_Connect failed: %s\n",
261                                                 nt_errstr(status));
262                 talloc_free(tmp_ctx);
263                 return status;
264         }
265
266         /* check result of samr_Connect */
267         if (!NT_STATUS_IS_OK(sc.out.result)) {
268                 r->out.error_string = talloc_asprintf(mem_ctx,
269                                                 "samr_Connect failed: %s\n", 
270                                                 nt_errstr(sc.out.result));
271                 status = sc.out.result;
272                 talloc_free(tmp_ctx);
273                 return status;
274         }
275
276         /* prepare samr_OpenDomain */
277         ZERO_STRUCT(d_handle);
278         od.in.connect_handle = &p_handle;
279         od.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
280         od.in.sid = domain_sid;
281         od.out.domain_handle = &d_handle;
282
283         /* 4. do a samr_OpenDomain to get a domain handle */
284         status = dcerpc_samr_OpenDomain(samr_pipe, tmp_ctx, &od);
285         if (!NT_STATUS_IS_OK(status)) {
286                 r->out.error_string = talloc_asprintf(mem_ctx,
287                                                 "samr_OpenDomain for [%s] failed: %s\n",
288                                                 r->in.domain_name, nt_errstr(status));
289                 talloc_free(tmp_ctx);
290                 return status;
291         }
292
293         /* prepare samr_CreateUser2 */
294         ZERO_STRUCT(u_handle);
295         cu.in.domain_handle  = &d_handle;
296         cu.in.access_mask     = SEC_FLAG_MAXIMUM_ALLOWED;
297         samr_account_name.string = r->in.account_name;
298         cu.in.account_name    = &samr_account_name;
299         cu.in.acct_flags      = r->in.acct_type;
300         cu.out.user_handle    = &u_handle;
301         cu.out.rid            = &rid;
302         cu.out.access_granted = &access_granted;
303
304         /* 4. do a samr_CreateUser2 to get an account handle, or an error */
305         status = dcerpc_samr_CreateUser2(samr_pipe, tmp_ctx, &cu);
306         if (!NT_STATUS_IS_OK(status) && !NT_STATUS_EQUAL(status, NT_STATUS_USER_EXISTS)) {
307                         r->out.error_string = talloc_asprintf(mem_ctx,
308                                                                    "samr_CreateUser2 for [%s] failed: %s\n",
309                                                                    r->in.domain_name, nt_errstr(status));
310                         talloc_free(tmp_ctx);
311                         return status;
312
313         } else if (NT_STATUS_EQUAL(status, NT_STATUS_USER_EXISTS)) {
314                 /* prepare samr_LookupNames */
315                 ln.in.domain_handle = &d_handle;
316                 ln.in.num_names = 1;
317                 ln.in.names = talloc_array(tmp_ctx, struct lsa_String, 1);
318                 if (!ln.in.names) {
319                         r->out.error_string = NULL;
320                         talloc_free(tmp_ctx);
321                         return NT_STATUS_NO_MEMORY;
322                 }
323                 ln.in.names[0].string = r->in.account_name;
324                 
325                 /* 5. do a samr_LookupNames to get the users rid */
326                 status = dcerpc_samr_LookupNames(samr_pipe, tmp_ctx, &ln);
327                 if (!NT_STATUS_IS_OK(status)) {
328                         r->out.error_string = talloc_asprintf(mem_ctx,
329                                                               "samr_LookupNames for [%s] failed: %s\n",
330                                                 r->in.account_name, nt_errstr(status));
331                         talloc_free(tmp_ctx);
332                         return status;
333                 }
334                 
335                 
336                 /* check if we got one RID for the user */
337                 if (ln.out.rids.count != 1) {
338                         r->out.error_string = talloc_asprintf(mem_ctx,
339                                                               "samr_LookupNames for [%s] returns %d RIDs\n",
340                                                               r->in.account_name, ln.out.rids.count);
341                         status = NT_STATUS_INVALID_PARAMETER;
342                         talloc_free(tmp_ctx);
343                         return status;  
344                 }
345                 
346                 /* prepare samr_OpenUser */
347                 ZERO_STRUCT(u_handle);
348                 ou.in.domain_handle = &d_handle;
349                 ou.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
350                 ou.in.rid = ln.out.rids.ids[0];
351                 ou.out.user_handle = &u_handle;
352                 
353                 /* 6. do a samr_OpenUser to get a user handle */
354                 status = dcerpc_samr_OpenUser(samr_pipe, tmp_ctx, &ou);
355                 if (!NT_STATUS_IS_OK(status)) {
356                         r->out.error_string = talloc_asprintf(mem_ctx,
357                                                               "samr_OpenUser for [%s] failed: %s\n",
358                                                               r->in.account_name, nt_errstr(status));
359                         talloc_free(tmp_ctx);
360                         return status;
361                 }
362         }
363
364         /* Find out what password policy this user has */
365         pwp.in.user_handle = &u_handle;
366
367         status = dcerpc_samr_GetUserPwInfo(samr_pipe, tmp_ctx, &pwp);
368         if (NT_STATUS_IS_OK(status)) {
369                 policy_min_pw_len = pwp.out.info.min_password_length;
370         }
371         
372         /* Grab a password of that minimum length */
373         r->out.join_password = generate_random_str(mem_ctx, MAX(8, policy_min_pw_len));
374
375         r2.samr_handle.level            = LIBNET_SET_PASSWORD_SAMR_HANDLE;
376         r2.samr_handle.in.account_name  = r->in.account_name;
377         r2.samr_handle.in.newpassword   = r->out.join_password;
378         r2.samr_handle.in.user_handle   = &u_handle;
379         r2.samr_handle.in.dcerpc_pipe   = samr_pipe;
380
381         status = libnet_SetPassword(ctx, tmp_ctx, &r2);
382
383         r->out.error_string = r2.samr_handle.out.error_string;
384
385         if (!NT_STATUS_IS_OK(status)) {
386                         talloc_free(tmp_ctx);
387                 return status;
388         }
389
390         /* prepare samr_QueryUserInfo (get flags) */
391         qui.in.user_handle = &u_handle;
392         qui.in.level = 16;
393         
394         status = dcerpc_samr_QueryUserInfo(samr_pipe, tmp_ctx, &qui);
395         if (!NT_STATUS_IS_OK(status)) {
396                 r->out.error_string
397                         = talloc_asprintf(mem_ctx,
398                                           "samr_QueryUserInfo for [%s] failed: %s\n",
399                                           r->in.account_name, nt_errstr(status));
400                         talloc_free(tmp_ctx);
401                 return status;
402         }
403         if (!qui.out.info) {
404                 status = NT_STATUS_INVALID_PARAMETER;
405                 r->out.error_string
406                         = talloc_asprintf(mem_ctx,
407                                           "samr_QueryUserInfo failed to return qui.out.info for [%s]: %s\n",
408                                           r->in.account_name, nt_errstr(status));
409                         talloc_free(tmp_ctx);
410                 return status;
411         }
412
413         /* Possibly change account type */
414         if ((qui.out.info->info16.acct_flags & (ACB_WSTRUST | ACB_SVRTRUST | ACB_DOMTRUST)) 
415             != r->in.acct_type) {
416                 acct_flags = (qui.out.info->info16.acct_flags & ~(ACB_WSTRUST | ACB_SVRTRUST | ACB_DOMTRUST))
417                               | r->in.acct_type;
418         } else {
419                 acct_flags = qui.out.info->info16.acct_flags;
420         }
421         
422         acct_flags = (acct_flags & ~ACB_DISABLED);
423
424         /* reset flags (if required) */
425         if (acct_flags != qui.out.info->info16.acct_flags) {
426                 ZERO_STRUCT(u_info);
427                 u_info.info16.acct_flags = acct_flags;
428
429                 sui.in.user_handle = &u_handle;
430                 sui.in.info = &u_info;
431                 sui.in.level = 16;
432                 
433                 dcerpc_samr_SetUserInfo(samr_pipe, tmp_ctx, &sui);
434                 if (!NT_STATUS_IS_OK(status)) {
435                         r->out.error_string
436                                 = talloc_asprintf(mem_ctx,
437                                                   "samr_SetUserInfo for [%s] failed to remove ACB_DISABLED flag: %s\n",
438                                                   r->in.account_name, nt_errstr(status));
439                         talloc_free(tmp_ctx);
440                         return status;
441                 }
442         }
443
444         /* Now, if it was AD, then we want to start looking changing a
445          * few more things.  Otherwise, we are done. */
446         if (!realm) {
447                 r->out.realm = NULL;
448                 r->out.kvno = 0;
449                 talloc_free(tmp_ctx);
450                 return NT_STATUS_OK;
451         }
452
453         /* We need to convert between a samAccountName and domain to a
454          * DN in the directory.  The correct way to do this is with
455          * DRSUAPI CrackNames */
456
457
458         /* Fiddle with the bindings, so get to DRSUAPI on
459          * NCACN_IP_TCP, sealed */
460         drsuapi_binding = talloc(tmp_ctx, struct dcerpc_binding);
461         *drsuapi_binding = *samr_binding;
462         drsuapi_binding->transport = NCACN_IP_TCP;
463         drsuapi_binding->endpoint = NULL;
464         drsuapi_binding->flags |= DCERPC_SEAL;
465         
466         status = dcerpc_pipe_connect_b(tmp_ctx, 
467                                        &drsuapi_pipe,
468                                        drsuapi_binding,
469                                        DCERPC_DRSUAPI_UUID,
470                                        DCERPC_DRSUAPI_VERSION, 
471                                        ctx->cred, 
472                                        ctx->event_ctx);
473
474         if (!NT_STATUS_IS_OK(status)) {
475                 r->out.error_string = talloc_asprintf(mem_ctx,
476                                                 "Connection to DRSUAPI pipe of PDC of domain '%s' failed: %s",
477                                                 r->in.domain_name, nt_errstr(status));
478                 talloc_free(tmp_ctx);
479                 return status;
480         }
481         
482         /* get a DRSUAPI pipe handle */
483         GUID_from_string(DRSUAPI_DS_BIND_GUID, &drsuapi_bind_guid);
484
485         r_drsuapi_bind.in.bind_guid = &drsuapi_bind_guid;
486         r_drsuapi_bind.in.bind_info = NULL;
487         r_drsuapi_bind.out.bind_handle = &drsuapi_bind_handle;
488
489         status = dcerpc_drsuapi_DsBind(drsuapi_pipe, tmp_ctx, &r_drsuapi_bind);
490         if (!NT_STATUS_IS_OK(status)) {
491                 if (NT_STATUS_EQUAL(status, NT_STATUS_NET_WRITE_FAULT)) {
492                         r->out.error_string
493                                 = talloc_asprintf(mem_ctx,
494                                                   "dcerpc_drsuapi_DsBind for [%s\\%s] failed - %s\n", 
495                                                   domain_name, r->in.account_name, 
496                                                   dcerpc_errstr(tmp_ctx, drsuapi_pipe->last_fault_code));
497                         talloc_free(tmp_ctx);
498                         return status;
499                 } else {
500                         r->out.error_string
501                                 = talloc_asprintf(mem_ctx,
502                                                   "dcerpc_drsuapi_DsBind for [%s\\%s] failed - %s\n", 
503                                                   domain_name, r->in.account_name, 
504                                                   nt_errstr(status));
505                         talloc_free(tmp_ctx);
506                         return status;
507                 }
508         } else if (!W_ERROR_IS_OK(r_drsuapi_bind.out.result)) {
509                 r->out.error_string
510                                 = talloc_asprintf(mem_ctx,
511                                                   "DsBind failed - %s\n", win_errstr(r_drsuapi_bind.out.result));
512                         talloc_free(tmp_ctx);
513                 return NT_STATUS_UNSUCCESSFUL;
514         }
515
516         /* Actually 'crack' the names */
517         ZERO_STRUCT(r_crack_names);
518         r_crack_names.in.bind_handle            = &drsuapi_bind_handle;
519         r_crack_names.in.level                  = 1;
520         r_crack_names.in.req.req1.unknown1              = 0x000004e4;
521         r_crack_names.in.req.req1.unknown2              = 0x00000407;
522         r_crack_names.in.req.req1.count         = 1;
523         r_crack_names.in.req.req1.names         = names;
524         r_crack_names.in.req.req1.format_flags  = DRSUAPI_DS_NAME_FLAG_NO_FLAGS;
525         r_crack_names.in.req.req1.format_offered        = DRSUAPI_DS_NAME_FORMAT_NT4_ACCOUNT;
526         r_crack_names.in.req.req1.format_desired        = DRSUAPI_DS_NAME_FORMAT_FQDN_1779;
527         names[0].str = talloc_asprintf(tmp_ctx, "%s\\%s", domain_name, r->in.account_name);
528
529         status = dcerpc_drsuapi_DsCrackNames(drsuapi_pipe, tmp_ctx, &r_crack_names);
530         if (!NT_STATUS_IS_OK(status)) {
531                 if (NT_STATUS_EQUAL(status, NT_STATUS_NET_WRITE_FAULT)) {
532                         r->out.error_string
533                                 = talloc_asprintf(mem_ctx,
534                                                   "dcerpc_drsuapi_DsCrackNames for [%s\\%s] failed - %s\n", 
535                                                   domain_name, r->in.account_name, 
536                                                   dcerpc_errstr(tmp_ctx, drsuapi_pipe->last_fault_code));
537                         talloc_free(tmp_ctx);
538                         return status;
539                 } else {
540                         r->out.error_string
541                                 = talloc_asprintf(mem_ctx,
542                                                   "dcerpc_drsuapi_DsCrackNames for [%s\\%s] failed - %s\n", 
543                                                   domain_name, r->in.account_name, 
544                                                   nt_errstr(status));
545                         talloc_free(tmp_ctx);
546                         return status;
547                 }
548         } else if (!W_ERROR_IS_OK(r_crack_names.out.result)) {
549                 r->out.error_string
550                                 = talloc_asprintf(mem_ctx,
551                                                   "DsCrackNames failed - %s\n", win_errstr(r_crack_names.out.result));
552                 talloc_free(tmp_ctx);
553                 return NT_STATUS_UNSUCCESSFUL;
554         } else if (r_crack_names.out.level != 1 
555                    || !r_crack_names.out.ctr.ctr1 
556                    || r_crack_names.out.ctr.ctr1->count != 1 
557                    || r_crack_names.out.ctr.ctr1->array[0].status != DRSUAPI_DS_NAME_STATUS_OK) {
558                 
559                 r->out.error_string = talloc_asprintf(mem_ctx, "DsCrackNames failed\n");
560                 talloc_free(tmp_ctx);
561                 return NT_STATUS_UNSUCCESSFUL;
562         }
563
564         account_dn = r_crack_names.out.ctr.ctr1->array[0].result_name;
565
566
567         /* Now we know the user's DN, open with LDAP, read and modify a few things */
568
569         remote_ldb_url = talloc_asprintf(tmp_ctx, "ldap://%s", 
570                                          drsuapi_binding->host);
571         remote_ldb = ldb_wrap_connect(tmp_ctx, remote_ldb_url, 0, NULL);
572
573         if (!remote_ldb) {
574                 return NT_STATUS_UNSUCCESSFUL;
575         }
576
577         /* search for the user's record */
578         ldb_ret = ldb_search(remote_ldb, account_dn, LDB_SCOPE_BASE, 
579                              NULL, attrs, &msgs);
580
581         if (ldb_ret != 1) {
582                 r->out.error_string
583                         = talloc_asprintf(mem_ctx,
584                                           "ldb_search for %s failed - %s\n", 
585                                           account_dn, 
586                                           ldb_errstring(remote_ldb));
587                 return NT_STATUS_UNSUCCESSFUL;
588         }
589
590         /* If we have a kvno recorded in AD, we need it locally as well */
591         r->out.kvno = ldb_msg_find_uint(msgs[0], "msDS-KeyVersionNumber", 0);
592
593         /* Prepare a new message, for the modify */
594         msg = ldb_msg_new(tmp_ctx);
595         if (!msg) {
596                 return NT_STATUS_NO_MEMORY;
597         }
598
599         msg->dn = msgs[0]->dn;
600
601         {
602                 char *service_principal_name[2];
603                 char *dns_host_name = strlower_talloc(mem_ctx, 
604                                                       talloc_asprintf(mem_ctx, 
605                                                                       "%s.%s", lp_netbios_name(), realm));
606                 service_principal_name[0] = talloc_asprintf(tmp_ctx, "host/%s", dns_host_name);
607                 service_principal_name[1] = talloc_asprintf(tmp_ctx, "host/%s", strlower_talloc(mem_ctx, lp_netbios_name()));
608
609                 samdb_msg_add_string(remote_ldb, tmp_ctx, msg, "dNSHostName", dns_host_name);
610                 samdb_msg_add_string(remote_ldb, tmp_ctx, msg, "servicePrincipalName", service_principal_name[0]);
611                 samdb_msg_add_string(remote_ldb, tmp_ctx, msg, "servicePrincipalName", service_principal_name[1]);
612                 
613                 ldb_ret = samdb_replace(remote_ldb, tmp_ctx, msg);
614                 if (ldb_ret != 0) {
615                         r->out.error_string
616                                 = talloc_asprintf(mem_ctx, 
617                                                   "Failed to replace entries on %s\n", 
618                                                   msg->dn);
619                         return NT_STATUS_INTERNAL_DB_CORRUPTION;
620                 }
621         }
622
623         /* close connection */
624         talloc_free(tmp_ctx);
625
626         return NT_STATUS_OK;
627 }
628
629 static NTSTATUS libnet_Join_primary_domain(struct libnet_context *ctx, 
630                                            TALLOC_CTX *mem_ctx, 
631                                            struct libnet_Join *r)
632 {
633         NTSTATUS status;
634         int ret;
635
636         struct ldb_context *ldb;
637         struct libnet_JoinDomain r2;
638         const char *base_dn = "cn=Primary Domains";
639         const struct ldb_val *prior_secret;
640         const struct ldb_val *prior_modified_time;
641         struct ldb_message **msgs, *msg;
642         char *sct;
643         const char *attrs[] = {
644                 "whenChanged",
645                 "secret",
646                 "priorSecret"
647                 "priorChanged",
648                 NULL
649         };
650
651         if (r->in.secure_channel_type == SEC_CHAN_BDC) {
652                 r2.in.acct_type = ACB_SVRTRUST;
653         } else if (r->in.secure_channel_type == SEC_CHAN_WKSTA) {
654                 r2.in.acct_type = ACB_WSTRUST;
655         }
656         r2.in.domain_name  = r->in.domain_name;
657
658         r2.in.account_name = talloc_asprintf(mem_ctx, "%s$", lp_netbios_name());
659
660         /* Local secrets are stored in secrets.ldb */
661         ldb = secrets_db_connect(mem_ctx);
662         if (!ldb) {
663                 r->out.error_string
664                         = talloc_asprintf(mem_ctx, 
665                                           "Could not open secrets database\n");
666                 return NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
667         }
668
669         /* join domain */
670         status = libnet_JoinDomain(ctx, mem_ctx, &r2);
671
672         r->out.error_string = r2.out.error_string;
673         if (!NT_STATUS_IS_OK(status)) {
674                 return status;
675         }
676
677         sct = talloc_asprintf(mem_ctx, "%d", r->in.secure_channel_type);
678         msg = ldb_msg_new(mem_ctx);
679
680         /* search for the secret record */
681         ret = gendb_search(ldb,
682                            mem_ctx, base_dn, &msgs, attrs,
683                            "(|" SECRETS_PRIMARY_DOMAIN_FILTER "(realm=%s))",
684                            r2.out.domain_name, r2.out.realm);
685
686         msg->dn = talloc_asprintf(mem_ctx, "flatname=%s,%s", 
687                                   r2.out.domain_name,
688                                   base_dn);
689         
690         samdb_msg_add_string(ldb, mem_ctx, msg, "flatname", r2.out.domain_name);
691         if (r2.out.realm) {
692                 samdb_msg_add_string(ldb, mem_ctx, msg, "realm", r2.out.realm);
693         }
694         samdb_msg_add_string(ldb, mem_ctx, msg, "objectClass", "primaryDomain");
695         samdb_msg_add_string(ldb, mem_ctx, msg, "secret", r2.out.join_password);
696         
697         samdb_msg_add_string(ldb, mem_ctx, msg, "samAccountName", r2.in.account_name);
698         
699         samdb_msg_add_string(ldb, mem_ctx, msg, "secureChannelType", sct);
700
701         if (r2.out.kvno) {
702                 samdb_msg_add_uint(ldb, mem_ctx, msg, "msDS-KeyVersionNumber",
703                                    r2.out.kvno);
704         }
705
706         if (ret == 0) {
707         } else if (ret == -1) {
708                 r->out.error_string
709                         = talloc_asprintf(mem_ctx, 
710                                           "Search for domain: %s and realm: %s failed: %s", 
711                                           r2.out.domain_name, r2.out.realm, ldb_errstring(ldb));
712                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
713         } else {
714                 int i;
715                 for (i = 0; i < ret; i++) {
716                         ldb_delete(ldb, msgs[i]->dn);
717                 }
718
719                 prior_secret = ldb_msg_find_ldb_val(msgs[0], "secret");
720                 if (prior_secret) {
721                         samdb_msg_set_value(ldb, mem_ctx, msg, "priorSecret", prior_secret);
722                 }
723                 samdb_msg_set_string(ldb, mem_ctx, msg, "secret", r2.out.join_password);
724                 
725                 prior_modified_time = ldb_msg_find_ldb_val(msgs[0], 
726                                                            "whenChanged");
727                 if (prior_modified_time) {
728                         samdb_msg_set_value(ldb, mem_ctx, msg, "priorWhenChanged", 
729                                             prior_modified_time);
730                 }
731                 
732                 samdb_msg_set_string(ldb, mem_ctx, msg, "samAccountName", r2.in.account_name);
733                 samdb_msg_set_string(ldb, mem_ctx, msg, "secureChannelType", sct);
734         }
735
736         /* create the secret */
737         ret = samdb_add(ldb, mem_ctx, msg);
738         if (ret != 0) {
739                 r->out.error_string
740                         = talloc_asprintf(mem_ctx, 
741                                           "Failed to create secret record %s\n", 
742                                           msg->dn);
743                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
744         }
745         return NT_STATUS_OK;
746 }
747
748 NTSTATUS libnet_Join(struct libnet_context *ctx, TALLOC_CTX *mem_ctx, struct libnet_Join *r)
749 {
750         NTSTATUS nt_status;
751         struct libnet_Join r2;
752         r2.in.secure_channel_type = r->in.secure_channel_type;
753         r2.in.domain_name = r->in.domain_name;
754         
755         if ((r->in.secure_channel_type == SEC_CHAN_WKSTA)
756             || (r->in.secure_channel_type == SEC_CHAN_BDC)) {
757                 nt_status = libnet_Join_primary_domain(ctx, mem_ctx, &r2);
758         } else {
759                 r->out.error_string
760                         = talloc_asprintf(mem_ctx, "Invalid secure channel type specified (%08X) attempting to join domain %s",
761                                          r->in.secure_channel_type, r->in.domain_name);
762                 return NT_STATUS_INVALID_PARAMETER;
763         }
764         r->out.error_string = r2.out.error_string;
765         return nt_status;
766 }
767
768