r9391: Convert all the code to use struct ldb_dn to ohandle ldap like distinguished...
[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 struct ldb_dn *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 = ldb_dn_explode(mem_ctx, r_crack_names.out.ctr.ctr1->array[0].result_name);
565         if (account_dn == NULL) {
566                 r->out.error_string
567                         = talloc_asprintf(mem_ctx, "Invalid account dn: %s",
568                                           r_crack_names.out.ctr.ctr1->array[0].result_name);
569                 return NT_STATUS_UNSUCCESSFUL;
570         }
571
572         /* Now we know the user's DN, open with LDAP, read and modify a few things */
573
574         remote_ldb_url = talloc_asprintf(tmp_ctx, "ldap://%s", 
575                                          drsuapi_binding->host);
576         remote_ldb = ldb_wrap_connect(tmp_ctx, remote_ldb_url, 0, NULL);
577
578         if (!remote_ldb) {
579                 return NT_STATUS_UNSUCCESSFUL;
580         }
581
582         /* search for the user's record */
583         ldb_ret = ldb_search(remote_ldb, account_dn, LDB_SCOPE_BASE, 
584                              NULL, attrs, &msgs);
585
586         if (ldb_ret != 1) {
587                 r->out.error_string
588                         = talloc_asprintf(mem_ctx,
589                                           "ldb_search for %s failed - %s\n",
590                                           ldb_dn_linearize(mem_ctx, account_dn),
591                                           ldb_errstring(remote_ldb));
592                 return NT_STATUS_UNSUCCESSFUL;
593         }
594
595         /* If we have a kvno recorded in AD, we need it locally as well */
596         r->out.kvno = ldb_msg_find_uint(msgs[0], "msDS-KeyVersionNumber", 0);
597
598         /* Prepare a new message, for the modify */
599         msg = ldb_msg_new(tmp_ctx);
600         if (!msg) {
601                 return NT_STATUS_NO_MEMORY;
602         }
603
604         msg->dn = msgs[0]->dn;
605
606         {
607                 char *service_principal_name[2];
608                 char *dns_host_name = strlower_talloc(mem_ctx, 
609                                                       talloc_asprintf(mem_ctx, 
610                                                                       "%s.%s", lp_netbios_name(), realm));
611                 service_principal_name[0] = talloc_asprintf(tmp_ctx, "host/%s", dns_host_name);
612                 service_principal_name[1] = talloc_asprintf(tmp_ctx, "host/%s", strlower_talloc(mem_ctx, lp_netbios_name()));
613
614                 samdb_msg_add_string(remote_ldb, tmp_ctx, msg, "dNSHostName", dns_host_name);
615                 samdb_msg_add_string(remote_ldb, tmp_ctx, msg, "servicePrincipalName", service_principal_name[0]);
616                 samdb_msg_add_string(remote_ldb, tmp_ctx, msg, "servicePrincipalName", service_principal_name[1]);
617                 
618                 ldb_ret = samdb_replace(remote_ldb, tmp_ctx, msg);
619                 if (ldb_ret != 0) {
620                         r->out.error_string
621                                 = talloc_asprintf(mem_ctx, 
622                                                   "Failed to replace entries on %s\n", 
623                                                   ldb_dn_linearize(mem_ctx, msg->dn));
624                         return NT_STATUS_INTERNAL_DB_CORRUPTION;
625                 }
626         }
627
628         /* close connection */
629         talloc_free(tmp_ctx);
630
631         return NT_STATUS_OK;
632 }
633
634 static NTSTATUS libnet_Join_primary_domain(struct libnet_context *ctx, 
635                                            TALLOC_CTX *mem_ctx, 
636                                            struct libnet_Join *r)
637 {
638         NTSTATUS status;
639         int ret;
640
641         struct ldb_context *ldb;
642         struct libnet_JoinDomain r2;
643         const struct ldb_dn *base_dn = ldb_dn_explode(mem_ctx, "cn=Primary Domains");
644         const struct ldb_val *prior_secret;
645         const struct ldb_val *prior_modified_time;
646         struct ldb_message **msgs, *msg;
647         char *sct;
648         const char *attrs[] = {
649                 "whenChanged",
650                 "secret",
651                 "priorSecret"
652                 "priorChanged",
653                 NULL
654         };
655
656         if (r->in.secure_channel_type == SEC_CHAN_BDC) {
657                 r2.in.acct_type = ACB_SVRTRUST;
658         } else if (r->in.secure_channel_type == SEC_CHAN_WKSTA) {
659                 r2.in.acct_type = ACB_WSTRUST;
660         }
661         r2.in.domain_name  = r->in.domain_name;
662
663         r2.in.account_name = talloc_asprintf(mem_ctx, "%s$", lp_netbios_name());
664
665         /* Local secrets are stored in secrets.ldb */
666         ldb = secrets_db_connect(mem_ctx);
667         if (!ldb) {
668                 r->out.error_string
669                         = talloc_asprintf(mem_ctx, 
670                                           "Could not open secrets database\n");
671                 return NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
672         }
673
674         /* join domain */
675         status = libnet_JoinDomain(ctx, mem_ctx, &r2);
676
677         r->out.error_string = r2.out.error_string;
678         if (!NT_STATUS_IS_OK(status)) {
679                 return status;
680         }
681
682         sct = talloc_asprintf(mem_ctx, "%d", r->in.secure_channel_type);
683         msg = ldb_msg_new(mem_ctx);
684
685         /* search for the secret record */
686         ret = gendb_search(ldb,
687                            mem_ctx, base_dn,
688                            &msgs, attrs,
689                            "(|" SECRETS_PRIMARY_DOMAIN_FILTER "(realm=%s))",
690                            r2.out.domain_name, r2.out.realm);
691
692         msg->dn = ldb_dn_build_child(mem_ctx, "flatname", r2.out.domain_name, base_dn);
693         
694         samdb_msg_add_string(ldb, mem_ctx, msg, "flatname", r2.out.domain_name);
695         if (r2.out.realm) {
696                 samdb_msg_add_string(ldb, mem_ctx, msg, "realm", r2.out.realm);
697         }
698         samdb_msg_add_string(ldb, mem_ctx, msg, "objectClass", "primaryDomain");
699         samdb_msg_add_string(ldb, mem_ctx, msg, "secret", r2.out.join_password);
700         
701         samdb_msg_add_string(ldb, mem_ctx, msg, "samAccountName", r2.in.account_name);
702         
703         samdb_msg_add_string(ldb, mem_ctx, msg, "secureChannelType", sct);
704
705         if (r2.out.kvno) {
706                 samdb_msg_add_uint(ldb, mem_ctx, msg, "msDS-KeyVersionNumber",
707                                    r2.out.kvno);
708         }
709
710         if (ret == 0) {
711         } else if (ret == -1) {
712                 r->out.error_string
713                         = talloc_asprintf(mem_ctx, 
714                                           "Search for domain: %s and realm: %s failed: %s", 
715                                           r2.out.domain_name, r2.out.realm, ldb_errstring(ldb));
716                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
717         } else {
718                 int i;
719                 for (i = 0; i < ret; i++) {
720                         ldb_delete(ldb, msgs[i]->dn);
721                 }
722
723                 prior_secret = ldb_msg_find_ldb_val(msgs[0], "secret");
724                 if (prior_secret) {
725                         samdb_msg_set_value(ldb, mem_ctx, msg, "priorSecret", prior_secret);
726                 }
727                 samdb_msg_set_string(ldb, mem_ctx, msg, "secret", r2.out.join_password);
728                 
729                 prior_modified_time = ldb_msg_find_ldb_val(msgs[0], 
730                                                            "whenChanged");
731                 if (prior_modified_time) {
732                         samdb_msg_set_value(ldb, mem_ctx, msg, "priorWhenChanged", 
733                                             prior_modified_time);
734                 }
735                 
736                 samdb_msg_set_string(ldb, mem_ctx, msg, "samAccountName", r2.in.account_name);
737                 samdb_msg_set_string(ldb, mem_ctx, msg, "secureChannelType", sct);
738         }
739
740         /* create the secret */
741         ret = samdb_add(ldb, mem_ctx, msg);
742         if (ret != 0) {
743                 r->out.error_string
744                         = talloc_asprintf(mem_ctx, 
745                                           "Failed to create secret record %s\n", 
746                                           ldb_dn_linearize(ldb, msg->dn));
747                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
748         }
749         return NT_STATUS_OK;
750 }
751
752 NTSTATUS libnet_Join(struct libnet_context *ctx, TALLOC_CTX *mem_ctx, struct libnet_Join *r)
753 {
754         NTSTATUS nt_status;
755         struct libnet_Join r2;
756         r2.in.secure_channel_type = r->in.secure_channel_type;
757         r2.in.domain_name = r->in.domain_name;
758         
759         if ((r->in.secure_channel_type == SEC_CHAN_WKSTA)
760             || (r->in.secure_channel_type == SEC_CHAN_BDC)) {
761                 nt_status = libnet_Join_primary_domain(ctx, mem_ctx, &r2);
762         } else {
763                 r->out.error_string
764                         = talloc_asprintf(mem_ctx, "Invalid secure channel type specified (%08X) attempting to join domain %s",
765                                          r->in.secure_channel_type, r->in.domain_name);
766                 return NT_STATUS_INVALID_PARAMETER;
767         }
768         r->out.error_string = r2.out.error_string;
769         return nt_status;
770 }
771
772