/src/samba/source3/passdb/secrets.c
Line | Count | Source |
1 | | /* |
2 | | Unix SMB/CIFS implementation. |
3 | | Copyright (C) Andrew Tridgell 1992-2001 |
4 | | Copyright (C) Andrew Bartlett 2002 |
5 | | Copyright (C) Rafal Szczesniak 2002 |
6 | | Copyright (C) Tim Potter 2001 |
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 | | /* the Samba secrets database stores any generated, private information |
23 | | such as the local SID and machine trust password */ |
24 | | |
25 | | #include "includes.h" |
26 | | #include "system/filesys.h" |
27 | | #include "../libcli/auth/libcli_auth.h" |
28 | | #include "librpc/gen_ndr/ndr_secrets.h" |
29 | | #include "secrets.h" |
30 | | #include "dbwrap/dbwrap.h" |
31 | | #include "dbwrap/dbwrap_open.h" |
32 | | #include "../libcli/security/security.h" |
33 | | #include "util_tdb.h" |
34 | | #include "auth/credentials/credentials.h" |
35 | | |
36 | | #undef DBGC_CLASS |
37 | 0 | #define DBGC_CLASS DBGC_PASSDB |
38 | | |
39 | | static struct db_context *db_ctx; |
40 | | |
41 | | /* open up the secrets database with specified private_dir path */ |
42 | | bool secrets_init_path(const char *private_dir) |
43 | 0 | { |
44 | 0 | char *fname = NULL; |
45 | 0 | TALLOC_CTX *frame; |
46 | |
|
47 | 0 | if (db_ctx != NULL) { |
48 | 0 | return True; |
49 | 0 | } |
50 | | |
51 | 0 | if (private_dir == NULL) { |
52 | 0 | return False; |
53 | 0 | } |
54 | | |
55 | 0 | frame = talloc_stackframe(); |
56 | 0 | fname = talloc_asprintf(frame, "%s/secrets.tdb", private_dir); |
57 | 0 | if (fname == NULL) { |
58 | 0 | TALLOC_FREE(frame); |
59 | 0 | return False; |
60 | 0 | } |
61 | | |
62 | 0 | db_ctx = db_open(NULL, fname, 0, |
63 | 0 | TDB_DEFAULT, O_RDWR|O_CREAT, 0600, |
64 | 0 | DBWRAP_LOCK_ORDER_1, DBWRAP_FLAG_NONE); |
65 | |
|
66 | 0 | if (db_ctx == NULL) { |
67 | 0 | DEBUG(0,("Failed to open %s\n", fname)); |
68 | 0 | TALLOC_FREE(frame); |
69 | 0 | return False; |
70 | 0 | } |
71 | | |
72 | 0 | TALLOC_FREE(frame); |
73 | 0 | return True; |
74 | 0 | } |
75 | | |
76 | | /* open up the secrets database */ |
77 | | bool secrets_init(void) |
78 | 0 | { |
79 | 0 | return secrets_init_path(lp_private_dir()); |
80 | 0 | } |
81 | | |
82 | | struct db_context *secrets_db_ctx(void) |
83 | 0 | { |
84 | 0 | if (!secrets_init()) { |
85 | 0 | return NULL; |
86 | 0 | } |
87 | | |
88 | 0 | return db_ctx; |
89 | 0 | } |
90 | | |
91 | | /* |
92 | | * close secrets.tdb |
93 | | */ |
94 | | void secrets_shutdown(void) |
95 | 0 | { |
96 | 0 | TALLOC_FREE(db_ctx); |
97 | 0 | } |
98 | | |
99 | | /* read a entry from the secrets database - the caller must free the result |
100 | | if size is non-null then the size of the entry is put in there |
101 | | */ |
102 | | void *secrets_fetch(const char *key, size_t *size) |
103 | 0 | { |
104 | 0 | TDB_DATA dbuf; |
105 | 0 | void *result; |
106 | 0 | NTSTATUS status; |
107 | |
|
108 | 0 | if (!secrets_init()) { |
109 | 0 | return NULL; |
110 | 0 | } |
111 | | |
112 | 0 | status = dbwrap_fetch(db_ctx, talloc_tos(), string_tdb_data(key), |
113 | 0 | &dbuf); |
114 | 0 | if (!NT_STATUS_IS_OK(status)) { |
115 | 0 | return NULL; |
116 | 0 | } |
117 | | |
118 | 0 | result = smb_memdup(dbuf.dptr, dbuf.dsize); |
119 | 0 | if (result == NULL) { |
120 | 0 | return NULL; |
121 | 0 | } |
122 | | /* |
123 | | * secrets_fetch() is a generic code and may be used for sensitive data, |
124 | | * so clear the local dbuf.dptr memory via BURN_PTR_SIZE(). |
125 | | * The future plan is to convert secrets_fetch() to talloc. |
126 | | * That would improve performance via: |
127 | | * - avoid smb_memdup() above, instead directly return dbuf.dptr |
128 | | * - BURN_PTR_SIZE() will be done not here but in the caller and only |
129 | | * if the caller asks for sensitive data. |
130 | | */ |
131 | 0 | BURN_PTR_SIZE(dbuf.dptr, dbuf.dsize); |
132 | 0 | TALLOC_FREE(dbuf.dptr); |
133 | |
|
134 | 0 | if (size) { |
135 | 0 | *size = dbuf.dsize; |
136 | 0 | } |
137 | |
|
138 | 0 | return result; |
139 | 0 | } |
140 | | |
141 | | /* store a secrets entry |
142 | | */ |
143 | | bool secrets_store(const char *key, const void *data, size_t size) |
144 | 0 | { |
145 | 0 | NTSTATUS status; |
146 | |
|
147 | 0 | if (!secrets_init()) { |
148 | 0 | return false; |
149 | 0 | } |
150 | | |
151 | 0 | status = dbwrap_trans_store(db_ctx, string_tdb_data(key), |
152 | 0 | make_tdb_data((const uint8_t *)data, size), |
153 | 0 | TDB_REPLACE); |
154 | 0 | return NT_STATUS_IS_OK(status); |
155 | 0 | } |
156 | | |
157 | | bool secrets_store_creds(struct cli_credentials *creds) |
158 | 0 | { |
159 | 0 | const char *p = NULL; |
160 | 0 | bool ok; |
161 | |
|
162 | 0 | p = cli_credentials_get_username(creds); |
163 | 0 | if (p == NULL) { |
164 | 0 | return false; |
165 | 0 | } |
166 | | |
167 | 0 | ok = secrets_store(SECRETS_AUTH_USER, p, strlen(p) + 1); |
168 | 0 | if (!ok) { |
169 | 0 | DBG_ERR("Failed storing auth user name\n"); |
170 | 0 | return false; |
171 | 0 | } |
172 | | |
173 | | |
174 | 0 | p = cli_credentials_get_domain(creds); |
175 | 0 | if (p == NULL) { |
176 | 0 | return false; |
177 | 0 | } |
178 | | |
179 | 0 | ok = secrets_store(SECRETS_AUTH_DOMAIN, p, strlen(p) + 1); |
180 | 0 | if (!ok) { |
181 | 0 | DBG_ERR("Failed storing auth domain name\n"); |
182 | 0 | return false; |
183 | 0 | } |
184 | | |
185 | | |
186 | 0 | p = cli_credentials_get_password(creds); |
187 | 0 | if (p == NULL) { |
188 | 0 | return false; |
189 | 0 | } |
190 | | |
191 | 0 | ok = secrets_store(SECRETS_AUTH_PASSWORD, p, strlen(p) + 1); |
192 | 0 | if (!ok) { |
193 | 0 | DBG_ERR("Failed storing auth password\n"); |
194 | 0 | return false; |
195 | 0 | } |
196 | | |
197 | 0 | return true; |
198 | 0 | } |
199 | | |
200 | | |
201 | | /* delete a secets database entry |
202 | | */ |
203 | | bool secrets_delete_entry(const char *key) |
204 | 0 | { |
205 | 0 | NTSTATUS status; |
206 | 0 | if (!secrets_init()) { |
207 | 0 | return false; |
208 | 0 | } |
209 | | |
210 | 0 | status = dbwrap_trans_delete(db_ctx, string_tdb_data(key)); |
211 | |
|
212 | 0 | return NT_STATUS_IS_OK(status); |
213 | 0 | } |
214 | | |
215 | | /* |
216 | | * Deletes the key if it exists. |
217 | | */ |
218 | | bool secrets_delete(const char *key) |
219 | 0 | { |
220 | 0 | bool exists; |
221 | |
|
222 | 0 | if (!secrets_init()) { |
223 | 0 | return false; |
224 | 0 | } |
225 | | |
226 | 0 | exists = dbwrap_exists(db_ctx, string_tdb_data(key)); |
227 | 0 | if (!exists) { |
228 | 0 | return true; |
229 | 0 | } |
230 | | |
231 | 0 | return secrets_delete_entry(key); |
232 | 0 | } |
233 | | |
234 | | /** |
235 | | * Form a key for fetching a trusted domain password |
236 | | * |
237 | | * @param domain trusted domain name |
238 | | * |
239 | | * @return stored password's key |
240 | | **/ |
241 | | static char *trustdom_keystr(const char *domain) |
242 | 0 | { |
243 | 0 | char *keystr; |
244 | |
|
245 | 0 | keystr = talloc_asprintf_strupper_m(talloc_tos(), "%s/%s", |
246 | 0 | SECRETS_DOMTRUST_ACCT_PASS, |
247 | 0 | domain); |
248 | 0 | SMB_ASSERT(keystr != NULL); |
249 | 0 | return keystr; |
250 | 0 | } |
251 | | |
252 | | /************************************************************************ |
253 | | Routine to get account password to trusted domain |
254 | | ************************************************************************/ |
255 | | |
256 | | bool secrets_fetch_trusted_domain_password(const char *domain, char** pwd, |
257 | | struct dom_sid *sid, time_t *pass_last_set_time) |
258 | 0 | { |
259 | 0 | struct TRUSTED_DOM_PASS pass; |
260 | 0 | enum ndr_err_code ndr_err; |
261 | | |
262 | | /* unpacking structures */ |
263 | 0 | DATA_BLOB blob; |
264 | | |
265 | | /* fetching trusted domain password structure */ |
266 | 0 | if (!(blob.data = (uint8_t *)secrets_fetch(trustdom_keystr(domain), |
267 | 0 | &blob.length))) { |
268 | 0 | DEBUG(5, ("secrets_fetch failed!\n")); |
269 | 0 | return False; |
270 | 0 | } |
271 | | |
272 | | /* unpack trusted domain password */ |
273 | 0 | ndr_err = ndr_pull_struct_blob(&blob, talloc_tos(), &pass, |
274 | 0 | (ndr_pull_flags_fn_t)ndr_pull_TRUSTED_DOM_PASS); |
275 | | |
276 | | /* This blob is NOT talloc based! */ |
277 | 0 | BURN_FREE(blob.data, blob.length); |
278 | |
|
279 | 0 | if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { |
280 | 0 | return false; |
281 | 0 | } |
282 | | |
283 | 0 | if (pass.pass != NULL) { |
284 | 0 | talloc_keep_secret(discard_const_p(char, pass.pass)); |
285 | 0 | } |
286 | | |
287 | | /* the trust's password */ |
288 | 0 | if (pwd) { |
289 | 0 | *pwd = SMB_STRDUP(pass.pass); |
290 | 0 | if (!*pwd) { |
291 | 0 | return False; |
292 | 0 | } |
293 | 0 | } |
294 | | |
295 | | /* last change time */ |
296 | 0 | if (pass_last_set_time) *pass_last_set_time = pass.mod_time; |
297 | | |
298 | | /* domain sid */ |
299 | 0 | if (sid != NULL) sid_copy(sid, &pass.domain_sid); |
300 | |
|
301 | 0 | return True; |
302 | 0 | } |
303 | | |
304 | | /** |
305 | | * Routine to store the password for trusted domain |
306 | | * |
307 | | * @param domain remote domain name |
308 | | * @param pwd plain text password of trust relationship |
309 | | * @param sid remote domain sid |
310 | | * |
311 | | * @return true if succeeded |
312 | | **/ |
313 | | |
314 | | bool secrets_store_trusted_domain_password(const char* domain, const char* pwd, |
315 | | const struct dom_sid *sid) |
316 | 0 | { |
317 | 0 | bool ret; |
318 | | |
319 | | /* packing structures */ |
320 | 0 | DATA_BLOB blob; |
321 | 0 | enum ndr_err_code ndr_err; |
322 | 0 | struct TRUSTED_DOM_PASS pass; |
323 | 0 | ZERO_STRUCT(pass); |
324 | |
|
325 | 0 | pass.uni_name = domain; |
326 | 0 | pass.uni_name_len = strlen(domain)+1; |
327 | | |
328 | | /* last change time */ |
329 | 0 | pass.mod_time = time(NULL); |
330 | | |
331 | | /* password of the trust */ |
332 | 0 | pass.pass_len = strlen(pwd); |
333 | 0 | pass.pass = pwd; |
334 | | |
335 | | /* domain sid */ |
336 | 0 | sid_copy(&pass.domain_sid, sid); |
337 | |
|
338 | 0 | ndr_err = ndr_push_struct_blob(&blob, talloc_tos(), &pass, |
339 | 0 | (ndr_push_flags_fn_t)ndr_push_TRUSTED_DOM_PASS); |
340 | 0 | if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { |
341 | 0 | return false; |
342 | 0 | } |
343 | | |
344 | 0 | ret = secrets_store(trustdom_keystr(domain), blob.data, blob.length); |
345 | | |
346 | | /* This blob is talloc based. */ |
347 | 0 | data_blob_clear_free(&blob); |
348 | |
|
349 | 0 | return ret; |
350 | 0 | } |
351 | | |
352 | | /************************************************************************ |
353 | | Routine to delete the password for trusted domain |
354 | | ************************************************************************/ |
355 | | |
356 | | bool trusted_domain_password_delete(const char *domain) |
357 | 0 | { |
358 | 0 | return secrets_delete_entry(trustdom_keystr(domain)); |
359 | 0 | } |
360 | | |
361 | | bool secrets_store_ldap_pw(const char* dn, char* pw) |
362 | 0 | { |
363 | 0 | char *key = NULL; |
364 | 0 | bool ret; |
365 | |
|
366 | 0 | if (asprintf(&key, "%s/%s", SECRETS_LDAP_BIND_PW, dn) < 0) { |
367 | 0 | DEBUG(0, ("secrets_store_ldap_pw: asprintf failed!\n")); |
368 | 0 | return False; |
369 | 0 | } |
370 | | |
371 | 0 | ret = secrets_store(key, pw, strlen(pw)+1); |
372 | |
|
373 | 0 | SAFE_FREE(key); |
374 | 0 | return ret; |
375 | 0 | } |
376 | | |
377 | | /******************************************************************* |
378 | | Find the ldap password. |
379 | | ******************************************************************/ |
380 | | |
381 | | bool fetch_ldap_pw(char **dn, char** pw) |
382 | 0 | { |
383 | 0 | char *key = NULL; |
384 | 0 | size_t size = 0; |
385 | |
|
386 | 0 | *dn = smb_xstrdup(lp_ldap_admin_dn()); |
387 | |
|
388 | 0 | if (asprintf(&key, "%s/%s", SECRETS_LDAP_BIND_PW, *dn) < 0) { |
389 | 0 | SAFE_FREE(*dn); |
390 | 0 | DEBUG(0, ("fetch_ldap_pw: asprintf failed!\n")); |
391 | 0 | return false; |
392 | 0 | } |
393 | | |
394 | 0 | *pw=(char *)secrets_fetch(key, &size); |
395 | 0 | SAFE_FREE(key); |
396 | |
|
397 | 0 | if (*pw == NULL || size == 0 || (*pw)[size-1] != '\0') { |
398 | 0 | DBG_ERR("No valid password for %s\n", *dn); |
399 | 0 | BURN_FREE_STR(*pw); |
400 | 0 | SAFE_FREE(*dn); |
401 | 0 | return false; |
402 | 0 | } |
403 | | |
404 | 0 | return true; |
405 | 0 | } |
406 | | |
407 | | /******************************************************************************* |
408 | | Store a complete AFS keyfile into secrets.tdb. |
409 | | *******************************************************************************/ |
410 | | |
411 | | bool secrets_store_afs_keyfile(const char *cell, const struct afs_keyfile *keyfile) |
412 | 0 | { |
413 | 0 | fstring key; |
414 | |
|
415 | 0 | if ((cell == NULL) || (keyfile == NULL)) |
416 | 0 | return False; |
417 | | |
418 | 0 | if (ntohl(keyfile->nkeys) > SECRETS_AFS_MAXKEYS) |
419 | 0 | return False; |
420 | | |
421 | 0 | slprintf(key, sizeof(key)-1, "%s/%s", SECRETS_AFS_KEYFILE, cell); |
422 | 0 | return secrets_store(key, keyfile, sizeof(struct afs_keyfile)); |
423 | 0 | } |
424 | | |
425 | | /******************************************************************************* |
426 | | Fetch the current (highest) AFS key from secrets.tdb |
427 | | *******************************************************************************/ |
428 | | bool secrets_fetch_afs_key(const char *cell, struct afs_key *result) |
429 | 0 | { |
430 | 0 | fstring key; |
431 | 0 | struct afs_keyfile *keyfile; |
432 | 0 | size_t size = 0; |
433 | 0 | uint32_t i; |
434 | |
|
435 | 0 | slprintf(key, sizeof(key)-1, "%s/%s", SECRETS_AFS_KEYFILE, cell); |
436 | |
|
437 | 0 | keyfile = (struct afs_keyfile *)secrets_fetch(key, &size); |
438 | |
|
439 | 0 | if (keyfile == NULL) |
440 | 0 | return False; |
441 | | |
442 | 0 | if (size != sizeof(struct afs_keyfile)) { |
443 | 0 | BURN_FREE(keyfile, sizeof(*keyfile)); |
444 | 0 | return False; |
445 | 0 | } |
446 | | |
447 | 0 | i = ntohl(keyfile->nkeys); |
448 | |
|
449 | 0 | if (i > SECRETS_AFS_MAXKEYS) { |
450 | 0 | BURN_FREE(keyfile, sizeof(*keyfile)); |
451 | 0 | return False; |
452 | 0 | } |
453 | | |
454 | 0 | *result = keyfile->entry[i-1]; |
455 | |
|
456 | 0 | result->kvno = ntohl(result->kvno); |
457 | |
|
458 | 0 | BURN_FREE(keyfile, sizeof(*keyfile)); |
459 | |
|
460 | 0 | return True; |
461 | 0 | } |
462 | | |
463 | | /****************************************************************************** |
464 | | When kerberos is not available, choose between anonymous or |
465 | | authenticated connections. |
466 | | |
467 | | We need to use an authenticated connection if DCs have the |
468 | | RestrictAnonymous registry entry set > 0, or the "Additional |
469 | | restrictions for anonymous connections" set in the win2k Local |
470 | | Security Policy. |
471 | | |
472 | | Caller to free() result in domain, username, password |
473 | | *******************************************************************************/ |
474 | | void secrets_fetch_ipc_userpass(char **username, char **domain, char **password) |
475 | 0 | { |
476 | 0 | *username = (char *)secrets_fetch(SECRETS_AUTH_USER, NULL); |
477 | 0 | *domain = (char *)secrets_fetch(SECRETS_AUTH_DOMAIN, NULL); |
478 | 0 | *password = (char *)secrets_fetch(SECRETS_AUTH_PASSWORD, NULL); |
479 | |
|
480 | 0 | if (*username && **username) { |
481 | |
|
482 | 0 | if (!*domain || !**domain) { |
483 | 0 | SAFE_FREE(*domain); |
484 | 0 | *domain = smb_xstrdup(lp_workgroup()); |
485 | 0 | } |
486 | |
|
487 | 0 | if (!*password || !**password) { |
488 | 0 | BURN_FREE_STR(*password); |
489 | 0 | *password = smb_xstrdup(""); |
490 | 0 | } |
491 | |
|
492 | 0 | DEBUG(3, ("IPC$ connections done by user %s\\%s\n", |
493 | 0 | *domain, *username)); |
494 | |
|
495 | 0 | } else { |
496 | 0 | DEBUG(3, ("IPC$ connections done anonymously\n")); |
497 | 0 | SAFE_FREE(*username); |
498 | 0 | SAFE_FREE(*domain); |
499 | 0 | BURN_FREE_STR(*password); |
500 | 0 | *username = smb_xstrdup(""); |
501 | 0 | *domain = smb_xstrdup(""); |
502 | 0 | *password = smb_xstrdup(""); |
503 | 0 | } |
504 | 0 | } |
505 | | |
506 | | bool secrets_store_generic(const char *owner, const char *key, const char *secret) |
507 | 0 | { |
508 | 0 | char *tdbkey = NULL; |
509 | 0 | bool ret; |
510 | |
|
511 | 0 | if (asprintf(&tdbkey, "SECRETS/GENERIC/%s/%s", owner, key) < 0) { |
512 | 0 | DEBUG(0, ("asprintf failed!\n")); |
513 | 0 | return False; |
514 | 0 | } |
515 | | |
516 | 0 | ret = secrets_store(tdbkey, secret, strlen(secret)+1); |
517 | |
|
518 | 0 | SAFE_FREE(tdbkey); |
519 | 0 | return ret; |
520 | 0 | } |
521 | | |
522 | | /******************************************************************* |
523 | | Find the ldap password. |
524 | | ******************************************************************/ |
525 | | |
526 | | char *secrets_fetch_generic(const char *owner, const char *key) |
527 | 0 | { |
528 | 0 | char *secret = NULL; |
529 | 0 | char *tdbkey = NULL; |
530 | |
|
531 | 0 | if (( ! owner) || ( ! key)) { |
532 | 0 | DEBUG(1, ("Invalid Parameters\n")); |
533 | 0 | return NULL; |
534 | 0 | } |
535 | | |
536 | 0 | if (asprintf(&tdbkey, "SECRETS/GENERIC/%s/%s", owner, key) < 0) { |
537 | 0 | DEBUG(0, ("Out of memory!\n")); |
538 | 0 | return NULL; |
539 | 0 | } |
540 | | |
541 | 0 | secret = (char *)secrets_fetch(tdbkey, NULL); |
542 | 0 | SAFE_FREE(tdbkey); |
543 | |
|
544 | 0 | return secret; |
545 | 0 | } |
546 | | |