~zulcss/samba/server-dailies-3.4

« back to all changes in this revision

Viewing changes to source4/libnet/libnet_join.c

  • Committer: Chuck Short
  • Date: 2010-09-28 20:38:39 UTC
  • Revision ID: zulcss@ubuntu.com-20100928203839-pgjulytsi9ue63x1
Initial version

Show diffs side-by-side

added added

removed removed

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