/* Unix SMB/CIFS implementation. Copyright (C) Andrew Tridgell 1992-2001 Copyright (C) Andrew Bartlett 2002 Copyright (C) Rafal Szczesniak 2002 Copyright (C) Tim Potter 2001 This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ /* the Samba secrets database stores any generated, private information such as the local SID and machine trust password */ #include "includes.h" #include "passdb.h" #include "../libcli/auth/libcli_auth.h" #include "secrets.h" #include "dbwrap/dbwrap.h" #include "../librpc/ndr/libndr.h" #include "util_tdb.h" #include "libcli/security/security.h" #undef DBGC_CLASS #define DBGC_CLASS DBGC_PASSDB static char *des_salt_key(const char *realm); /** * Form a key for fetching the domain sid * * @param domain domain name * * @return keystring **/ static const char *domain_sid_keystr(const char *domain) { char *keystr; keystr = talloc_asprintf_strupper_m(talloc_tos(), "%s/%s", SECRETS_DOMAIN_SID, domain); SMB_ASSERT(keystr != NULL); return keystr; } static const char *domain_guid_keystr(const char *domain) { char *keystr; keystr = talloc_asprintf_strupper_m(talloc_tos(), "%s/%s", SECRETS_DOMAIN_GUID, domain); SMB_ASSERT(keystr != NULL); return keystr; } static const char *protect_ids_keystr(const char *domain) { char *keystr; keystr = talloc_asprintf_strupper_m(talloc_tos(), "%s/%s", SECRETS_PROTECT_IDS, domain); SMB_ASSERT(keystr != NULL); return keystr; } /* N O T E: never use this outside of passdb modules that store the SID on their own */ bool secrets_mark_domain_protected(const char *domain) { bool ret; ret = secrets_store(protect_ids_keystr(domain), "TRUE", 5); if (!ret) { DEBUG(0, ("Failed to protect the Domain IDs\n")); } return ret; } bool secrets_clear_domain_protection(const char *domain) { bool ret; void *protection = secrets_fetch(protect_ids_keystr(domain), NULL); if (protection) { SAFE_FREE(protection); ret = secrets_delete_entry(protect_ids_keystr(domain)); if (!ret) { DEBUG(0, ("Failed to remove Domain IDs protection\n")); } return ret; } return true; } bool secrets_store_domain_sid(const char *domain, const struct dom_sid *sid) { char *protect_ids; bool ret; protect_ids = secrets_fetch(protect_ids_keystr(domain), NULL); if (protect_ids) { if (strncmp(protect_ids, "TRUE", 4)) { DEBUG(0, ("Refusing to store a Domain SID, " "it has been marked as protected!\n")); SAFE_FREE(protect_ids); return false; } } SAFE_FREE(protect_ids); ret = secrets_store(domain_sid_keystr(domain), sid, sizeof(struct dom_sid )); /* Force a re-query, in the case where we modified our domain */ if (ret) { if (dom_sid_equal(get_global_sam_sid(), sid) == false) { reset_global_sam_sid(); } } return ret; } bool secrets_fetch_domain_sid(const char *domain, struct dom_sid *sid) { struct dom_sid *dyn_sid; size_t size = 0; dyn_sid = (struct dom_sid *)secrets_fetch(domain_sid_keystr(domain), &size); if (dyn_sid == NULL) return False; if (size != sizeof(struct dom_sid)) { SAFE_FREE(dyn_sid); return False; } *sid = *dyn_sid; SAFE_FREE(dyn_sid); return True; } bool secrets_store_domain_guid(const char *domain, const struct GUID *guid) { char *protect_ids; const char *key; protect_ids = secrets_fetch(protect_ids_keystr(domain), NULL); if (protect_ids) { if (strncmp(protect_ids, "TRUE", 4)) { DEBUG(0, ("Refusing to store a Domain SID, " "it has been marked as protected!\n")); SAFE_FREE(protect_ids); return false; } } SAFE_FREE(protect_ids); key = domain_guid_keystr(domain); return secrets_store(key, guid, sizeof(struct GUID)); } bool secrets_fetch_domain_guid(const char *domain, struct GUID *guid) { struct GUID *dyn_guid; const char *key; size_t size = 0; struct GUID new_guid; key = domain_guid_keystr(domain); dyn_guid = (struct GUID *)secrets_fetch(key, &size); if (!dyn_guid) { if (lp_server_role() == ROLE_DOMAIN_PDC) { new_guid = GUID_random(); if (!secrets_store_domain_guid(domain, &new_guid)) return False; dyn_guid = (struct GUID *)secrets_fetch(key, &size); } if (dyn_guid == NULL) { return False; } } if (size != sizeof(struct GUID)) { DEBUG(1,("UUID size %d is wrong!\n", (int)size)); SAFE_FREE(dyn_guid); return False; } *guid = *dyn_guid; SAFE_FREE(dyn_guid); return True; } /** * Form a key for fetching the machine trust account sec channel type * * @param domain domain name * * @return keystring **/ static const char *machine_sec_channel_type_keystr(const char *domain) { char *keystr; keystr = talloc_asprintf_strupper_m(talloc_tos(), "%s/%s", SECRETS_MACHINE_SEC_CHANNEL_TYPE, domain); SMB_ASSERT(keystr != NULL); return keystr; } /** * Form a key for fetching the machine trust account last change time * * @param domain domain name * * @return keystring **/ static const char *machine_last_change_time_keystr(const char *domain) { char *keystr; keystr = talloc_asprintf_strupper_m(talloc_tos(), "%s/%s", SECRETS_MACHINE_LAST_CHANGE_TIME, domain); SMB_ASSERT(keystr != NULL); return keystr; } /** * Form a key for fetching the machine previous trust account password * * @param domain domain name * * @return keystring **/ static const char *machine_prev_password_keystr(const char *domain) { char *keystr; keystr = talloc_asprintf_strupper_m(talloc_tos(), "%s/%s", SECRETS_MACHINE_PASSWORD_PREV, domain); SMB_ASSERT(keystr != NULL); return keystr; } /** * Form a key for fetching the machine trust account password * * @param domain domain name * * @return keystring **/ static const char *machine_password_keystr(const char *domain) { char *keystr; keystr = talloc_asprintf_strupper_m(talloc_tos(), "%s/%s", SECRETS_MACHINE_PASSWORD, domain); SMB_ASSERT(keystr != NULL); return keystr; } /** * Form a key for fetching the machine trust account password * * @param domain domain name * * @return stored password's key **/ static const char *trust_keystr(const char *domain) { char *keystr; keystr = talloc_asprintf_strupper_m(talloc_tos(), "%s/%s", SECRETS_MACHINE_ACCT_PASS, domain); SMB_ASSERT(keystr != NULL); return keystr; } /************************************************************************ Routine to get the default secure channel type for trust accounts ************************************************************************/ enum netr_SchannelType get_default_sec_channel(void) { if (lp_server_role() == ROLE_DOMAIN_BDC || lp_server_role() == ROLE_DOMAIN_PDC || lp_server_role() == ROLE_ACTIVE_DIRECTORY_DC) { return SEC_CHAN_BDC; } else { return SEC_CHAN_WKSTA; } } /************************************************************************ Routine to get the trust account password for a domain. This only tries to get the legacy hashed version of the password. The user of this function must have locked the trust password file using the above secrets_lock_trust_account_password(). ************************************************************************/ bool secrets_fetch_trust_account_password_legacy(const char *domain, uint8_t ret_pwd[16], time_t *pass_last_set_time, enum netr_SchannelType *channel) { struct machine_acct_pass *pass; size_t size = 0; if (!(pass = (struct machine_acct_pass *)secrets_fetch( trust_keystr(domain), &size))) { DEBUG(5, ("secrets_fetch failed!\n")); return False; } if (size != sizeof(*pass)) { DEBUG(0, ("secrets were of incorrect size!\n")); SAFE_FREE(pass); return False; } if (pass_last_set_time) { *pass_last_set_time = pass->mod_time; } memcpy(ret_pwd, pass->hash, 16); if (channel) { *channel = get_default_sec_channel(); } SAFE_FREE(pass); return True; } /************************************************************************ Routine to get the trust account password for a domain. The user of this function must have locked the trust password file using the above secrets_lock_trust_account_password(). ************************************************************************/ bool secrets_fetch_trust_account_password(const char *domain, uint8_t ret_pwd[16], time_t *pass_last_set_time, enum netr_SchannelType *channel) { char *plaintext; plaintext = secrets_fetch_machine_password(domain, pass_last_set_time, channel); if (plaintext) { DEBUG(4,("Using cleartext machine password\n")); E_md4hash(plaintext, ret_pwd); SAFE_FREE(plaintext); return True; } return secrets_fetch_trust_account_password_legacy(domain, ret_pwd, pass_last_set_time, channel); } /************************************************************************ Routine to delete all information related to the domain joined machine. ************************************************************************/ bool secrets_delete_machine_password_ex(const char *domain, const char *realm) { const char *tmpkey = NULL; bool ok; if (realm != NULL) { tmpkey = des_salt_key(domain); ok = secrets_delete(tmpkey); if (!ok) { return false; } } tmpkey = domain_guid_keystr(domain); ok = secrets_delete(tmpkey); if (!ok) { return false; } tmpkey = machine_prev_password_keystr(domain); ok = secrets_delete(tmpkey); if (!ok) { return false; } tmpkey = machine_password_keystr(domain); ok = secrets_delete_entry(tmpkey); if (!ok) { return false; } tmpkey = machine_sec_channel_type_keystr(domain); ok = secrets_delete_entry(tmpkey); if (!ok) { return false; } tmpkey = machine_last_change_time_keystr(domain); ok = secrets_delete_entry(tmpkey); if (!ok) { return false; } tmpkey = domain_sid_keystr(domain); ok = secrets_delete_entry(tmpkey); if (!ok) { return false; } return true; } /************************************************************************ Routine to delete the domain sid ************************************************************************/ bool secrets_delete_domain_sid(const char *domain) { return secrets_delete_entry(domain_sid_keystr(domain)); } /************************************************************************ Routine to store the previous machine password (by storing the current password as the old) ************************************************************************/ static bool secrets_store_prev_machine_password(const char *domain) { char *oldpass; bool ret; oldpass = (char *)secrets_fetch(machine_password_keystr(domain), NULL); if (oldpass == NULL) { return true; } ret = secrets_store(machine_prev_password_keystr(domain), oldpass, strlen(oldpass)+1); SAFE_FREE(oldpass); return ret; } /************************************************************************ Routine to set the plaintext machine account password for a realm the password is assumed to be a null terminated ascii string. Before storing ************************************************************************/ bool secrets_store_machine_password(const char *pass, const char *domain, enum netr_SchannelType sec_channel) { bool ret; uint32_t last_change_time; uint32_t sec_channel_type; if (!secrets_store_prev_machine_password(domain)) { return false; } ret = secrets_store(machine_password_keystr(domain), pass, strlen(pass)+1); if (!ret) return ret; SIVAL(&last_change_time, 0, time(NULL)); ret = secrets_store(machine_last_change_time_keystr(domain), &last_change_time, sizeof(last_change_time)); SIVAL(&sec_channel_type, 0, sec_channel); ret = secrets_store(machine_sec_channel_type_keystr(domain), &sec_channel_type, sizeof(sec_channel_type)); return ret; } /************************************************************************ Set the machine trust account password, the old pw and last change time, domain SID and salting principals based on values passed in (added to supprt the secrets_tdb_sync module on secrets.ldb) ************************************************************************/ bool secrets_store_machine_pw_sync(const char *pass, const char *oldpass, const char *domain, const char *realm, const char *salting_principal, uint32_t supported_enc_types, const struct dom_sid *domain_sid, uint32_t last_change_time, uint32_t secure_channel_type, bool delete_join) { bool ret; uint8_t last_change_time_store[4]; TALLOC_CTX *frame = talloc_stackframe(); uint8_t sec_channel_bytes[4]; if (delete_join) { secrets_delete_machine_password_ex(domain, realm); TALLOC_FREE(frame); return true; } ret = secrets_store(machine_password_keystr(domain), pass, strlen(pass)+1); if (!ret) { TALLOC_FREE(frame); return ret; } if (oldpass) { ret = secrets_store(machine_prev_password_keystr(domain), oldpass, strlen(oldpass)+1); } else { ret = secrets_delete(machine_prev_password_keystr(domain)); } if (!ret) { TALLOC_FREE(frame); return ret; } if (secure_channel_type == 0) { /* We delete this and instead have the read code fall back to * a default based on server role, as our caller can't specify * this with any more certainty */ ret = secrets_delete(machine_sec_channel_type_keystr(domain)); if (!ret) { TALLOC_FREE(frame); return ret; } } else { SIVAL(&sec_channel_bytes, 0, secure_channel_type); ret = secrets_store(machine_sec_channel_type_keystr(domain), &sec_channel_bytes, sizeof(sec_channel_bytes)); if (!ret) { TALLOC_FREE(frame); return ret; } } SIVAL(&last_change_time_store, 0, last_change_time); ret = secrets_store(machine_last_change_time_keystr(domain), &last_change_time_store, sizeof(last_change_time)); if (!ret) { TALLOC_FREE(frame); return ret; } ret = secrets_store_domain_sid(domain, domain_sid); if (!ret) { TALLOC_FREE(frame); return ret; } if (realm != NULL) { char *key = des_salt_key(realm); if (salting_principal != NULL) { ret = secrets_store(key, salting_principal, strlen(salting_principal)+1); } else { ret = secrets_delete(key); } } TALLOC_FREE(frame); return ret; } /************************************************************************ Return the standard DES salt key ************************************************************************/ char* kerberos_standard_des_salt( void ) { fstring salt; fstr_sprintf( salt, "host/%s.%s@", lp_netbios_name(), lp_realm() ); (void)strlower_m( salt ); fstrcat( salt, lp_realm() ); return SMB_STRDUP( salt ); } /************************************************************************ ************************************************************************/ static char *des_salt_key(const char *realm) { char *keystr; keystr = talloc_asprintf_strupper_m(talloc_tos(), "%s/DES/%s", SECRETS_SALTING_PRINCIPAL, realm); SMB_ASSERT(keystr != NULL); return keystr; } /************************************************************************ ************************************************************************/ bool kerberos_secrets_store_des_salt( const char* salt ) { char* key; bool ret; key = des_salt_key(lp_realm()); if (key == NULL) { DEBUG(0,("kerberos_secrets_store_des_salt: failed to generate key!\n")); return False; } if ( !salt ) { DEBUG(8,("kerberos_secrets_store_des_salt: deleting salt\n")); secrets_delete_entry( key ); return True; } DEBUG(3,("kerberos_secrets_store_des_salt: Storing salt \"%s\"\n", salt)); ret = secrets_store( key, salt, strlen(salt)+1 ); TALLOC_FREE(key); return ret; } /************************************************************************ ************************************************************************/ static char* kerberos_secrets_fetch_des_salt( void ) { char *salt, *key; key = des_salt_key(lp_realm()); if (key == NULL) { DEBUG(0,("kerberos_secrets_fetch_des_salt: failed to generate key!\n")); return NULL; } salt = (char*)secrets_fetch( key, NULL ); TALLOC_FREE(key); return salt; } /************************************************************************ Routine to get the salting principal for this service. Caller must free if return is not null. ************************************************************************/ char *kerberos_secrets_fetch_salt_princ(void) { char *salt_princ_s; /* lookup new key first */ salt_princ_s = kerberos_secrets_fetch_des_salt(); if (salt_princ_s == NULL) { /* fall back to host/machine.realm@REALM */ salt_princ_s = kerberos_standard_des_salt(); } return salt_princ_s; } /************************************************************************ Routine to fetch the previous plaintext machine account password for a realm the password is assumed to be a null terminated ascii string. ************************************************************************/ char *secrets_fetch_prev_machine_password(const char *domain) { return (char *)secrets_fetch(machine_prev_password_keystr(domain), NULL); } /************************************************************************ Routine to fetch the last change time of the machine account password for a realm ************************************************************************/ time_t secrets_fetch_pass_last_set_time(const char *domain) { uint32_t *last_set_time; time_t pass_last_set_time; last_set_time = secrets_fetch(machine_last_change_time_keystr(domain), NULL); if (last_set_time) { pass_last_set_time = IVAL(last_set_time,0); SAFE_FREE(last_set_time); } else { pass_last_set_time = 0; } return pass_last_set_time; } /************************************************************************ Routine to fetch the plaintext machine account password for a realm the password is assumed to be a null terminated ascii string. ************************************************************************/ char *secrets_fetch_machine_password(const char *domain, time_t *pass_last_set_time, enum netr_SchannelType *channel) { char *ret; ret = (char *)secrets_fetch(machine_password_keystr(domain), NULL); if (pass_last_set_time) { *pass_last_set_time = secrets_fetch_pass_last_set_time(domain); } if (channel) { size_t size; uint32_t *channel_type; channel_type = (unsigned int *)secrets_fetch(machine_sec_channel_type_keystr(domain), &size); if (channel_type) { *channel = IVAL(channel_type,0); SAFE_FREE(channel_type); } else { *channel = get_default_sec_channel(); } } return ret; }