Line data Source code
1 : /*
2 : Unix SMB/CIFS implementation.
3 :
4 : User credentials handling (as regards on-disk files)
5 :
6 : Copyright (C) Jelmer Vernooij 2005
7 : Copyright (C) Tim Potter 2001
8 : Copyright (C) Andrew Bartlett <abartlet@samba.org> 2005
9 :
10 : This program is free software; you can redistribute it and/or modify
11 : it under the terms of the GNU General Public License as published by
12 : the Free Software Foundation; either version 3 of the License, or
13 : (at your option) any later version.
14 :
15 : This program is distributed in the hope that it will be useful,
16 : but WITHOUT ANY WARRANTY; without even the implied warranty of
17 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 : GNU General Public License for more details.
19 :
20 : You should have received a copy of the GNU General Public License
21 : along with this program. If not, see <http://www.gnu.org/licenses/>.
22 : */
23 :
24 : #include "includes.h"
25 : #include "lib/events/events.h"
26 : #include <ldb.h>
27 : #include "librpc/gen_ndr/samr.h" /* for struct samrPassword */
28 : #include "param/secrets.h"
29 : #include "system/filesys.h"
30 : #include "auth/credentials/credentials.h"
31 : #include "auth/credentials/credentials_internal.h"
32 : #include "auth/credentials/credentials_krb5.h"
33 : #include "auth/kerberos/kerberos_util.h"
34 : #include "param/param.h"
35 : #include "lib/events/events.h"
36 : #include "dsdb/samdb/samdb.h"
37 : #include "source3/include/secrets.h"
38 : #include "dbwrap/dbwrap.h"
39 : #include "dbwrap/dbwrap_open.h"
40 : #include "lib/util/util_tdb.h"
41 : #include "libds/common/roles.h"
42 :
43 : #undef DBGC_CLASS
44 : #define DBGC_CLASS DBGC_AUTH
45 :
46 : /**
47 : * Fill in credentials for the machine trust account, from the secrets database.
48 : *
49 : * @param cred Credentials structure to fill in
50 : * @retval NTSTATUS error detailing any failure
51 : */
52 68418 : static NTSTATUS cli_credentials_set_secrets_lct(struct cli_credentials *cred,
53 : struct loadparm_context *lp_ctx,
54 : struct ldb_context *ldb,
55 : const char *base,
56 : const char *filter,
57 : time_t secrets_tdb_last_change_time,
58 : const char *secrets_tdb_password,
59 : char **error_string)
60 : {
61 2657 : TALLOC_CTX *mem_ctx;
62 :
63 2657 : int ldb_ret;
64 2657 : struct ldb_message *msg;
65 :
66 2657 : const char *machine_account;
67 2657 : const char *password;
68 2657 : const char *domain;
69 2657 : const char *realm;
70 2657 : enum netr_SchannelType sct;
71 2657 : const char *salt_principal;
72 2657 : char *keytab;
73 2657 : const struct ldb_val *whenChanged;
74 2657 : time_t lct;
75 :
76 : /* ok, we are going to get it now, don't recurse back here */
77 68418 : cred->machine_account_pending = false;
78 :
79 : /* some other parts of the system will key off this */
80 68418 : cred->machine_account = true;
81 :
82 68418 : mem_ctx = talloc_named(cred, 0, "cli_credentials_set_secrets from ldb");
83 :
84 68418 : if (!ldb) {
85 : /* Local secrets are stored in secrets.ldb */
86 68418 : ldb = secrets_db_connect(mem_ctx, lp_ctx);
87 68418 : if (!ldb) {
88 989 : *error_string = talloc_strdup(cred, "Could not open secrets.ldb");
89 989 : talloc_free(mem_ctx);
90 989 : return NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
91 : }
92 : }
93 :
94 67429 : ldb_ret = dsdb_search_one(ldb, mem_ctx, &msg,
95 : ldb_dn_new(mem_ctx, ldb, base),
96 : LDB_SCOPE_SUBTREE,
97 : NULL, 0, "%s", filter);
98 :
99 67429 : if (ldb_ret != LDB_SUCCESS) {
100 18 : *error_string = talloc_asprintf(cred, "Could not find entry to match filter: '%s' base: '%s': %s: %s",
101 : filter, base ? base : "",
102 : ldb_strerror(ldb_ret), ldb_errstring(ldb));
103 18 : talloc_free(mem_ctx);
104 18 : return NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
105 : }
106 :
107 67411 : password = ldb_msg_find_attr_as_string(msg, "secret", NULL);
108 :
109 67411 : whenChanged = ldb_msg_find_ldb_val(msg, "whenChanged");
110 67411 : if (!whenChanged || ldb_val_to_time(whenChanged, &lct) != LDB_SUCCESS) {
111 : /* This attribute is mandatory */
112 0 : talloc_free(mem_ctx);
113 0 : return NT_STATUS_NOT_FOUND;
114 : }
115 :
116 : /* Don't set secrets.ldb info if the secrets.tdb entry was more recent */
117 67411 : if (lct < secrets_tdb_last_change_time) {
118 0 : talloc_free(mem_ctx);
119 0 : return NT_STATUS_NOT_FOUND;
120 : }
121 :
122 67411 : if ((lct == secrets_tdb_last_change_time) &&
123 67406 : (secrets_tdb_password != NULL) &&
124 67406 : (password != NULL) &&
125 67406 : (strcmp(password, secrets_tdb_password) != 0)) {
126 0 : talloc_free(mem_ctx);
127 0 : return NT_STATUS_NOT_FOUND;
128 : }
129 :
130 67411 : cli_credentials_set_password_last_changed_time(cred, lct);
131 :
132 67411 : machine_account = ldb_msg_find_attr_as_string(msg, "samAccountName", NULL);
133 :
134 67411 : if (!machine_account) {
135 0 : machine_account = ldb_msg_find_attr_as_string(msg, "servicePrincipalName", NULL);
136 :
137 0 : if (!machine_account) {
138 0 : const char *ldap_bind_dn = ldb_msg_find_attr_as_string(msg, "ldapBindDn", NULL);
139 0 : if (!ldap_bind_dn) {
140 0 : *error_string = talloc_asprintf(cred,
141 : "Could not find 'samAccountName', "
142 : "'servicePrincipalName' or "
143 : "'ldapBindDn' in secrets record: %s",
144 0 : ldb_dn_get_linearized(msg->dn));
145 0 : talloc_free(mem_ctx);
146 0 : return NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
147 : } else {
148 : /* store bind dn in credentials */
149 0 : cli_credentials_set_bind_dn(cred, ldap_bind_dn);
150 : }
151 : }
152 : }
153 :
154 67411 : salt_principal = ldb_msg_find_attr_as_string(msg, "saltPrincipal", NULL);
155 67411 : cli_credentials_set_salt_principal(cred, salt_principal);
156 :
157 67411 : sct = ldb_msg_find_attr_as_int(msg, "secureChannelType", 0);
158 67411 : if (sct) {
159 67409 : cli_credentials_set_secure_channel_type(cred, sct);
160 : }
161 :
162 67411 : if (!password) {
163 0 : const struct ldb_val *nt_password_hash = ldb_msg_find_ldb_val(msg, "unicodePwd");
164 0 : struct samr_Password hash;
165 0 : ZERO_STRUCT(hash);
166 0 : if (nt_password_hash) {
167 0 : memcpy(hash.hash, nt_password_hash->data,
168 0 : MIN(nt_password_hash->length, sizeof(hash.hash)));
169 :
170 0 : cli_credentials_set_nt_hash(cred, &hash, CRED_SPECIFIED);
171 : } else {
172 0 : cli_credentials_set_password(cred, NULL, CRED_SPECIFIED);
173 : }
174 : } else {
175 67411 : cli_credentials_set_password(cred, password, CRED_SPECIFIED);
176 : }
177 :
178 67411 : domain = ldb_msg_find_attr_as_string(msg, "flatname", NULL);
179 67411 : if (domain) {
180 67409 : cli_credentials_set_domain(cred, domain, CRED_SPECIFIED);
181 : }
182 :
183 67411 : realm = ldb_msg_find_attr_as_string(msg, "realm", NULL);
184 67411 : if (realm) {
185 67411 : cli_credentials_set_realm(cred, realm, CRED_SPECIFIED);
186 : }
187 :
188 67411 : if (machine_account) {
189 67411 : cli_credentials_set_username(cred, machine_account, CRED_SPECIFIED);
190 : }
191 :
192 67411 : cli_credentials_set_kvno(cred, ldb_msg_find_attr_as_int(msg, "msDS-KeyVersionNumber", 0));
193 :
194 : /* If there was an external keytab specified by reference in
195 : * the LDB, then use this. Otherwise we will make one up
196 : * (chewing CPU time) from the password */
197 67411 : keytab = keytab_name_from_msg(cred, ldb, msg);
198 67411 : if (keytab) {
199 67411 : cli_credentials_set_keytab_name(cred, lp_ctx, keytab, CRED_SPECIFIED);
200 67411 : talloc_free(keytab);
201 : }
202 67411 : talloc_free(mem_ctx);
203 :
204 67411 : return NT_STATUS_OK;
205 : }
206 :
207 :
208 : /**
209 : * Fill in credentials for the machine trust account, from the secrets database.
210 : *
211 : * @param cred Credentials structure to fill in
212 : * @retval NTSTATUS error detailing any failure
213 : */
214 0 : _PUBLIC_ NTSTATUS cli_credentials_set_secrets(struct cli_credentials *cred,
215 : struct loadparm_context *lp_ctx,
216 : struct ldb_context *ldb,
217 : const char *base,
218 : const char *filter,
219 : char **error_string)
220 : {
221 0 : NTSTATUS status = cli_credentials_set_secrets_lct(cred, lp_ctx, ldb, base, filter, 0, NULL, error_string);
222 0 : if (!NT_STATUS_IS_OK(status)) {
223 : /* set anonymous as the fallback, if the machine account won't work */
224 0 : cli_credentials_set_anonymous(cred);
225 : }
226 0 : return status;
227 : }
228 :
229 : /**
230 : * Fill in credentials for the machine trust account, from the secrets database.
231 : *
232 : * @param cred Credentials structure to fill in
233 : * @retval NTSTATUS error detailing any failure
234 : */
235 68163 : _PUBLIC_ NTSTATUS cli_credentials_set_machine_account(struct cli_credentials *cred,
236 : struct loadparm_context *lp_ctx)
237 : {
238 2657 : struct db_context *db_ctx;
239 2657 : char *secrets_tdb_path;
240 2657 : int hash_size, tdb_flags;
241 :
242 68163 : secrets_tdb_path = lpcfg_private_db_path(cred, lp_ctx, "secrets");
243 68163 : if (secrets_tdb_path == NULL) {
244 0 : return NT_STATUS_NO_MEMORY;
245 : }
246 :
247 68163 : hash_size = lpcfg_tdb_hash_size(lp_ctx, secrets_tdb_path);
248 68163 : tdb_flags = lpcfg_tdb_flags(lp_ctx, TDB_DEFAULT);
249 :
250 68163 : db_ctx = dbwrap_local_open(
251 : cred,
252 : secrets_tdb_path,
253 : hash_size,
254 : tdb_flags,
255 : O_RDWR,
256 : 0600,
257 : DBWRAP_LOCK_ORDER_1,
258 : DBWRAP_FLAG_NONE);
259 68163 : TALLOC_FREE(secrets_tdb_path);
260 :
261 : /*
262 : * We do not check for errors here, we might not have a
263 : * secrets.tdb at all, and so we just need to check the
264 : * secrets.ldb
265 : */
266 68163 : return cli_credentials_set_machine_account_db_ctx(cred, lp_ctx, db_ctx);
267 : }
268 :
269 : /**
270 : * Fill in credentials for the machine trust account, from the
271 : * secrets.ldb or passed in handle to secrets.tdb (perhaps in CTDB).
272 : *
273 : * This version is used in parts of the code that can link in the
274 : * CTDB dbwrap backend, by passing down the already open handle.
275 : *
276 : * @param cred Credentials structure to fill in
277 : * @param db_ctx dbwrap context for secrets.tdb
278 : * @retval NTSTATUS error detailing any failure
279 : */
280 68416 : _PUBLIC_ NTSTATUS cli_credentials_set_machine_account_db_ctx(struct cli_credentials *cred,
281 : struct loadparm_context *lp_ctx,
282 : struct db_context *db_ctx)
283 : {
284 2657 : NTSTATUS status;
285 2657 : char *filter;
286 68416 : char *error_string = NULL;
287 2657 : const char *domain;
288 2657 : bool secrets_tdb_password_more_recent;
289 68416 : time_t secrets_tdb_lct = 0;
290 68416 : char *secrets_tdb_password = NULL;
291 68416 : char *secrets_tdb_old_password = NULL;
292 68416 : uint32_t secrets_tdb_secure_channel_type = SEC_CHAN_NULL;
293 68416 : int server_role = lpcfg_server_role(lp_ctx);
294 68416 : int security = lpcfg_security(lp_ctx);
295 2657 : char *keystr;
296 68416 : char *keystr_upper = NULL;
297 68416 : TALLOC_CTX *tmp_ctx = talloc_named(cred, 0, "cli_credentials_set_secrets from ldb");
298 68416 : if (!tmp_ctx) {
299 0 : return NT_STATUS_NO_MEMORY;
300 : }
301 :
302 : /* Bleh, nasty recursion issues: We are setting a machine
303 : * account here, so we don't want the 'pending' flag around
304 : * any more */
305 68416 : cred->machine_account_pending = false;
306 :
307 : /* We have to do this, as the fallback in
308 : * cli_credentials_set_secrets is to run as anonymous, so the domain is wiped */
309 68416 : domain = cli_credentials_get_domain(cred);
310 :
311 68416 : if (db_ctx) {
312 2655 : TDB_DATA dbuf;
313 67691 : keystr = talloc_asprintf(tmp_ctx, "%s/%s",
314 : SECRETS_MACHINE_LAST_CHANGE_TIME,
315 : domain);
316 67691 : keystr_upper = strupper_talloc(tmp_ctx, keystr);
317 67691 : status = dbwrap_fetch(db_ctx, tmp_ctx, string_tdb_data(keystr_upper),
318 : &dbuf);
319 67691 : if (NT_STATUS_IS_OK(status) && dbuf.dsize == 4) {
320 67663 : secrets_tdb_lct = IVAL(dbuf.dptr,0);
321 : }
322 :
323 67691 : keystr = talloc_asprintf(tmp_ctx, "%s/%s",
324 : SECRETS_MACHINE_PASSWORD,
325 : domain);
326 67691 : keystr_upper = strupper_talloc(tmp_ctx, keystr);
327 67691 : status = dbwrap_fetch(db_ctx, tmp_ctx, string_tdb_data(keystr_upper),
328 : &dbuf);
329 67691 : if (NT_STATUS_IS_OK(status)) {
330 67663 : secrets_tdb_password = (char *)dbuf.dptr;
331 : }
332 :
333 67691 : keystr = talloc_asprintf(tmp_ctx, "%s/%s",
334 : SECRETS_MACHINE_PASSWORD_PREV,
335 : domain);
336 67691 : keystr_upper = strupper_talloc(tmp_ctx, keystr);
337 67691 : status = dbwrap_fetch(db_ctx, tmp_ctx, string_tdb_data(keystr_upper),
338 : &dbuf);
339 67691 : if (NT_STATUS_IS_OK(status)) {
340 2118 : secrets_tdb_old_password = (char *)dbuf.dptr;
341 : }
342 :
343 67691 : keystr = talloc_asprintf(tmp_ctx, "%s/%s",
344 : SECRETS_MACHINE_SEC_CHANNEL_TYPE,
345 : domain);
346 67691 : keystr_upper = strupper_talloc(tmp_ctx, keystr);
347 67691 : status = dbwrap_fetch(db_ctx, tmp_ctx, string_tdb_data(keystr_upper),
348 : &dbuf);
349 67691 : if (NT_STATUS_IS_OK(status) && dbuf.dsize == 4) {
350 67663 : secrets_tdb_secure_channel_type = IVAL(dbuf.dptr,0);
351 : }
352 : }
353 :
354 68416 : filter = talloc_asprintf(cred, SECRETS_PRIMARY_DOMAIN_FILTER,
355 : domain);
356 68416 : status = cli_credentials_set_secrets_lct(cred, lp_ctx, NULL,
357 : SECRETS_PRIMARY_DOMAIN_DN,
358 : filter, secrets_tdb_lct, secrets_tdb_password, &error_string);
359 68416 : if (secrets_tdb_password == NULL) {
360 751 : secrets_tdb_password_more_recent = false;
361 67663 : } else if (NT_STATUS_EQUAL(NT_STATUS_CANT_ACCESS_DOMAIN_INFO, status)
362 67406 : || NT_STATUS_EQUAL(NT_STATUS_NOT_FOUND, status)) {
363 257 : secrets_tdb_password_more_recent = true;
364 67406 : } else if (secrets_tdb_lct > cli_credentials_get_password_last_changed_time(cred)) {
365 0 : secrets_tdb_password_more_recent = true;
366 67406 : } else if (secrets_tdb_lct == cli_credentials_get_password_last_changed_time(cred)) {
367 67406 : secrets_tdb_password_more_recent = strcmp(secrets_tdb_password, cli_credentials_get_password(cred)) != 0;
368 : } else {
369 0 : secrets_tdb_password_more_recent = false;
370 : }
371 :
372 68414 : if (secrets_tdb_password_more_recent) {
373 257 : enum credentials_use_kerberos use_kerberos =
374 : CRED_USE_KERBEROS_DISABLED;
375 257 : char *machine_account = talloc_asprintf(tmp_ctx, "%s$", lpcfg_netbios_name(lp_ctx));
376 257 : cli_credentials_set_password(cred, secrets_tdb_password, CRED_SPECIFIED);
377 257 : cli_credentials_set_old_password(cred, secrets_tdb_old_password, CRED_SPECIFIED);
378 257 : cli_credentials_set_domain(cred, domain, CRED_SPECIFIED);
379 257 : if (strequal(domain, lpcfg_workgroup(lp_ctx))) {
380 257 : cli_credentials_set_realm(cred, lpcfg_realm(lp_ctx), CRED_SPECIFIED);
381 :
382 257 : switch (server_role) {
383 237 : case ROLE_DOMAIN_MEMBER:
384 237 : if (security != SEC_ADS) {
385 37 : break;
386 : }
387 :
388 0 : FALL_THROUGH;
389 : case ROLE_ACTIVE_DIRECTORY_DC:
390 : case ROLE_IPA_DC:
391 200 : use_kerberos = CRED_USE_KERBEROS_DESIRED;
392 200 : break;
393 : }
394 : }
395 257 : cli_credentials_set_kerberos_state(cred,
396 : use_kerberos,
397 : CRED_SPECIFIED);
398 257 : cli_credentials_set_username(cred, machine_account, CRED_SPECIFIED);
399 257 : cli_credentials_set_password_last_changed_time(cred, secrets_tdb_lct);
400 257 : cli_credentials_set_secure_channel_type(cred, secrets_tdb_secure_channel_type);
401 257 : status = NT_STATUS_OK;
402 68159 : } else if (!NT_STATUS_IS_OK(status)) {
403 750 : if (db_ctx) {
404 0 : error_string
405 56 : = talloc_asprintf(cred,
406 : "Failed to fetch machine account password for %s from both "
407 : "secrets.ldb (%s) and from %s",
408 : domain,
409 28 : error_string == NULL ? "error" : error_string,
410 : dbwrap_name(db_ctx));
411 : } else {
412 2 : char *secrets_tdb_path;
413 :
414 722 : secrets_tdb_path = lpcfg_private_db_path(tmp_ctx,
415 : lp_ctx,
416 : "secrets");
417 722 : if (secrets_tdb_path == NULL) {
418 0 : return NT_STATUS_NO_MEMORY;
419 : }
420 :
421 722 : error_string = talloc_asprintf(cred,
422 : "Failed to fetch machine account password from "
423 : "secrets.ldb: %s and failed to open %s",
424 722 : error_string == NULL ? "error" : error_string,
425 : secrets_tdb_path);
426 : }
427 752 : DEBUG(1, ("Could not find machine account in secrets database: %s: %s\n",
428 : error_string == NULL ? "error" : error_string,
429 : nt_errstr(status)));
430 : /* set anonymous as the fallback, if the machine account won't work */
431 750 : cli_credentials_set_anonymous(cred);
432 : }
433 :
434 68416 : TALLOC_FREE(tmp_ctx);
435 68416 : return status;
436 : }
437 :
438 : /**
439 : * Fill in credentials for a particular principal, from the secrets database.
440 : *
441 : * @param cred Credentials structure to fill in
442 : * @retval NTSTATUS error detailing any failure
443 : */
444 2 : _PUBLIC_ NTSTATUS cli_credentials_set_stored_principal(struct cli_credentials *cred,
445 : struct loadparm_context *lp_ctx,
446 : const char *serviceprincipal)
447 : {
448 0 : NTSTATUS status;
449 0 : char *filter;
450 2 : char *error_string = NULL;
451 : /* Bleh, nasty recursion issues: We are setting a machine
452 : * account here, so we don't want the 'pending' flag around
453 : * any more */
454 2 : cred->machine_account_pending = false;
455 2 : filter = talloc_asprintf(cred, SECRETS_PRINCIPAL_SEARCH,
456 : cli_credentials_get_realm(cred),
457 : cli_credentials_get_domain(cred),
458 : serviceprincipal);
459 2 : status = cli_credentials_set_secrets_lct(cred, lp_ctx, NULL,
460 : SECRETS_PRINCIPALS_DN, filter,
461 : 0, NULL, &error_string);
462 2 : if (!NT_STATUS_IS_OK(status)) {
463 0 : DEBUG(1, ("Could not find %s principal in secrets database: %s: %s\n",
464 : serviceprincipal, nt_errstr(status),
465 : error_string ? error_string : "<no error>"));
466 : }
467 2 : return status;
468 : }
469 :
470 : /**
471 : * Ask that when required, the credentials system will be filled with
472 : * machine trust account, from the secrets database.
473 : *
474 : * @param cred Credentials structure to fill in
475 : * @note This function is used to call the above function after, rather
476 : * than during, popt processing.
477 : *
478 : */
479 5444 : _PUBLIC_ void cli_credentials_set_machine_account_pending(struct cli_credentials *cred,
480 : struct loadparm_context *lp_ctx)
481 : {
482 5444 : cred->machine_account_pending = true;
483 5444 : cred->machine_account_pending_lp_ctx = lp_ctx;
484 5444 : }
485 :
486 :
|