void scavenger_schedule_disconnected(struct files_struct *fsp) { NTSTATUS status; struct server_id self = messaging_server_id(fsp->conn->sconn->msg_ctx); struct timeval disconnect_time, until; uint64_t timeout_usec; struct scavenger_message msg; DATA_BLOB msg_blob; struct server_id_buf tmp; if (fsp->op == NULL) { return; } nttime_to_timeval(&disconnect_time, fsp->op->global->disconnect_time); timeout_usec = 1000 * fsp->op->global->durable_timeout_msec; until = timeval_add(&disconnect_time, timeout_usec / 1000000, timeout_usec % 1000000); ZERO_STRUCT(msg); msg.file_id = fsp->file_id; msg.open_persistent_id = fsp->op->global->open_persistent_id; msg.until = timeval_to_nttime(&until); DEBUG(10, ("smbd: %s mark file %s as disconnected at %s with timeout " "at %s in %fs\n", server_id_str_buf(self, &tmp), file_id_string_tos(&fsp->file_id), timeval_string(talloc_tos(), &disconnect_time, true), timeval_string(talloc_tos(), &until, true), fsp->op->global->durable_timeout_msec/1000.0)); SMB_ASSERT(server_id_is_disconnected(&fsp->op->global->server_id)); SMB_ASSERT(!server_id_equal(&self, &smbd_scavenger_state->parent_id)); SMB_ASSERT(!smbd_scavenger_state->am_scavenger); msg_blob = data_blob_const(&msg, sizeof(msg)); DEBUG(10, ("send message to scavenger\n")); status = messaging_send(smbd_scavenger_state->msg, smbd_scavenger_state->parent_id, MSG_SMB_SCAVENGER, &msg_blob); if (!NT_STATUS_IS_OK(status)) { struct server_id_buf tmp1, tmp2; DEBUG(2, ("Failed to send message to parent smbd %s " "from %s: %s\n", server_id_str_buf(smbd_scavenger_state->parent_id, &tmp1), server_id_str_buf(self, &tmp2), nt_errstr(status))); } }
/** * Check whether a cookie-stored struct info is the same * as a given SMB_STRUCT_STAT, as coming with the fsp. */ static bool vfs_default_durable_reconnect_check_stat( struct vfs_default_durable_stat *cookie_st, SMB_STRUCT_STAT *fsp_st, const char *name) { int ret; if (cookie_st->st_ex_dev != fsp_st->st_ex_dev) { DEBUG(1, ("vfs_default_durable_reconnect (%s): " "stat_ex.%s differs: " "cookie:%llu != stat:%llu, " "denying durable reconnect\n", name, "st_ex_dev", (unsigned long long)cookie_st->st_ex_dev, (unsigned long long)fsp_st->st_ex_dev)); return false; } if (cookie_st->st_ex_ino != fsp_st->st_ex_ino) { DEBUG(1, ("vfs_default_durable_reconnect (%s): " "stat_ex.%s differs: " "cookie:%llu != stat:%llu, " "denying durable reconnect\n", name, "st_ex_ino", (unsigned long long)cookie_st->st_ex_ino, (unsigned long long)fsp_st->st_ex_ino)); return false; } if (cookie_st->st_ex_mode != fsp_st->st_ex_mode) { DEBUG(1, ("vfs_default_durable_reconnect (%s): " "stat_ex.%s differs: " "cookie:%llu != stat:%llu, " "denying durable reconnect\n", name, "st_ex_mode", (unsigned long long)cookie_st->st_ex_mode, (unsigned long long)fsp_st->st_ex_mode)); return false; } if (cookie_st->st_ex_nlink != fsp_st->st_ex_nlink) { DEBUG(1, ("vfs_default_durable_reconnect (%s): " "stat_ex.%s differs: " "cookie:%llu != stat:%llu, " "denying durable reconnect\n", name, "st_ex_nlink", (unsigned long long)cookie_st->st_ex_nlink, (unsigned long long)fsp_st->st_ex_nlink)); return false; } if (cookie_st->st_ex_uid != fsp_st->st_ex_uid) { DEBUG(1, ("vfs_default_durable_reconnect (%s): " "stat_ex.%s differs: " "cookie:%llu != stat:%llu, " "denying durable reconnect\n", name, "st_ex_uid", (unsigned long long)cookie_st->st_ex_uid, (unsigned long long)fsp_st->st_ex_uid)); return false; } if (cookie_st->st_ex_gid != fsp_st->st_ex_gid) { DEBUG(1, ("vfs_default_durable_reconnect (%s): " "stat_ex.%s differs: " "cookie:%llu != stat:%llu, " "denying durable reconnect\n", name, "st_ex_gid", (unsigned long long)cookie_st->st_ex_gid, (unsigned long long)fsp_st->st_ex_gid)); return false; } if (cookie_st->st_ex_rdev != fsp_st->st_ex_rdev) { DEBUG(1, ("vfs_default_durable_reconnect (%s): " "stat_ex.%s differs: " "cookie:%llu != stat:%llu, " "denying durable reconnect\n", name, "st_ex_rdev", (unsigned long long)cookie_st->st_ex_rdev, (unsigned long long)fsp_st->st_ex_rdev)); return false; } if (cookie_st->st_ex_size != fsp_st->st_ex_size) { DEBUG(1, ("vfs_default_durable_reconnect (%s): " "stat_ex.%s differs: " "cookie:%llu != stat:%llu, " "denying durable reconnect\n", name, "st_ex_size", (unsigned long long)cookie_st->st_ex_size, (unsigned long long)fsp_st->st_ex_size)); return false; } ret = timespec_compare(&cookie_st->st_ex_atime, &fsp_st->st_ex_atime); if (ret != 0) { struct timeval tc, ts; tc = convert_timespec_to_timeval(cookie_st->st_ex_atime); ts = convert_timespec_to_timeval(fsp_st->st_ex_atime); DEBUG(1, ("vfs_default_durable_reconnect (%s): " "stat_ex.%s differs: " "cookie:'%s' != stat:'%s', " "denying durable reconnect\n", name, "st_ex_atime", timeval_string(talloc_tos(), &tc, true), timeval_string(talloc_tos(), &ts, true))); return false; } ret = timespec_compare(&cookie_st->st_ex_mtime, &fsp_st->st_ex_mtime); if (ret != 0) { struct timeval tc, ts; tc = convert_timespec_to_timeval(cookie_st->st_ex_mtime); ts = convert_timespec_to_timeval(fsp_st->st_ex_mtime); DEBUG(1, ("vfs_default_durable_reconnect (%s): " "stat_ex.%s differs: " "cookie:'%s' != stat:'%s', " "denying durable reconnect\n", name, "st_ex_mtime", timeval_string(talloc_tos(), &tc, true), timeval_string(talloc_tos(), &ts, true))); return false; } ret = timespec_compare(&cookie_st->st_ex_ctime, &fsp_st->st_ex_ctime); if (ret != 0) { struct timeval tc, ts; tc = convert_timespec_to_timeval(cookie_st->st_ex_ctime); ts = convert_timespec_to_timeval(fsp_st->st_ex_ctime); DEBUG(1, ("vfs_default_durable_reconnect (%s): " "stat_ex.%s differs: " "cookie:'%s' != stat:'%s', " "denying durable reconnect\n", name, "st_ex_ctime", timeval_string(talloc_tos(), &tc, true), timeval_string(talloc_tos(), &ts, true))); return false; } ret = timespec_compare(&cookie_st->st_ex_btime, &fsp_st->st_ex_btime); if (ret != 0) { struct timeval tc, ts; tc = convert_timespec_to_timeval(cookie_st->st_ex_btime); ts = convert_timespec_to_timeval(fsp_st->st_ex_btime); DEBUG(1, ("vfs_default_durable_reconnect (%s): " "stat_ex.%s differs: " "cookie:'%s' != stat:'%s', " "denying durable reconnect\n", name, "st_ex_btime", timeval_string(talloc_tos(), &tc, true), timeval_string(talloc_tos(), &ts, true))); return false; } if (cookie_st->st_ex_calculated_birthtime != fsp_st->st_ex_calculated_birthtime) { DEBUG(1, ("vfs_default_durable_reconnect (%s): " "stat_ex.%s differs: " "cookie:%llu != stat:%llu, " "denying durable reconnect\n", name, "st_ex_calculated_birthtime", (unsigned long long)cookie_st->st_ex_calculated_birthtime, (unsigned long long)fsp_st->st_ex_calculated_birthtime)); return false; } if (cookie_st->st_ex_blksize != fsp_st->st_ex_blksize) { DEBUG(1, ("vfs_default_durable_reconnect (%s): " "stat_ex.%s differs: " "cookie:%llu != stat:%llu, " "denying durable reconnect\n", name, "st_ex_blksize", (unsigned long long)cookie_st->st_ex_blksize, (unsigned long long)fsp_st->st_ex_blksize)); return false; } if (cookie_st->st_ex_blocks != fsp_st->st_ex_blocks) { DEBUG(1, ("vfs_default_durable_reconnect (%s): " "stat_ex.%s differs: " "cookie:%llu != stat:%llu, " "denying durable reconnect\n", name, "st_ex_blocks", (unsigned long long)cookie_st->st_ex_blocks, (unsigned long long)fsp_st->st_ex_blocks)); return false; } if (cookie_st->st_ex_flags != fsp_st->st_ex_flags) { DEBUG(1, ("vfs_default_durable_reconnect (%s): " "stat_ex.%s differs: " "cookie:%llu != stat:%llu, " "denying durable reconnect\n", name, "st_ex_flags", (unsigned long long)cookie_st->st_ex_flags, (unsigned long long)fsp_st->st_ex_flags)); return false; } if (cookie_st->st_ex_mask != fsp_st->st_ex_mask) { DEBUG(1, ("vfs_default_durable_reconnect (%s): " "stat_ex.%s differs: " "cookie:%llu != stat:%llu, " "denying durable reconnect\n", name, "st_ex_mask", (unsigned long long)cookie_st->st_ex_mask, (unsigned long long)fsp_st->st_ex_mask)); return false; } return true; }
NTSTATUS trust_pw_change(struct netlogon_creds_cli_context *context, struct messaging_context *msg_ctx, struct dcerpc_binding_handle *b, const char *domain, bool force) { TALLOC_CTX *frame = talloc_stackframe(); struct trust_pw_change_state *state; struct cli_credentials *creds = NULL; const struct samr_Password *current_nt_hash = NULL; const struct samr_Password *previous_nt_hash = NULL; enum netr_SchannelType sec_channel_type = SEC_CHAN_NULL; time_t pass_last_set_time; uint32_t old_version = 0; struct pdb_trusted_domain *td = NULL; struct timeval g_timeout = { 0, }; int timeout = 0; struct timeval tv = { 0, }; size_t new_len = DEFAULT_TRUST_ACCOUNT_PASSWORD_LENGTH; uint8_t new_password_buffer[256 * 2] = { 0, }; char *new_trust_passwd = NULL; size_t len = 0; uint32_t new_version = 0; uint32_t *new_trust_version = NULL; NTSTATUS status; bool ok; state = talloc_zero(frame, struct trust_pw_change_state); if (state == NULL) { TALLOC_FREE(frame); return NT_STATUS_NO_MEMORY; } state->g_ctx = g_lock_ctx_init(state, msg_ctx); if (state->g_ctx == NULL) { TALLOC_FREE(frame); return NT_STATUS_NO_MEMORY; } state->g_lock_key = talloc_asprintf(state, "trust_password_change_%s", domain); if (state->g_lock_key == NULL) { TALLOC_FREE(frame); return NT_STATUS_NO_MEMORY; } g_timeout = timeval_current_ofs(10, 0); status = g_lock_lock(state->g_ctx, state->g_lock_key, G_LOCK_WRITE, g_timeout); if (!NT_STATUS_IS_OK(status)) { DEBUG(1, ("could not get g_lock on [%s]!\n", state->g_lock_key)); TALLOC_FREE(frame); return status; } talloc_set_destructor(state, trust_pw_change_state_destructor); status = pdb_get_trust_credentials(domain, NULL, frame, &creds); if (!NT_STATUS_IS_OK(status)) { DEBUG(0, ("could not fetch domain creds for domain %s - %s!\n", domain, nt_errstr(status))); TALLOC_FREE(frame); return NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE; } current_nt_hash = cli_credentials_get_nt_hash(creds, frame); if (current_nt_hash == NULL) { DEBUG(0, ("cli_credentials_get_nt_hash failed for domain %s!\n", domain)); TALLOC_FREE(frame); return NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE; } old_version = cli_credentials_get_kvno(creds); pass_last_set_time = cli_credentials_get_password_last_changed_time(creds); sec_channel_type = cli_credentials_get_secure_channel_type(creds); new_version = old_version + 1; switch (sec_channel_type) { case SEC_CHAN_WKSTA: case SEC_CHAN_BDC: break; case SEC_CHAN_DNS_DOMAIN: /* * new_len * 2 = 498 bytes is the largest possible length * NL_PASSWORD_VERSION consumes the rest of the possible 512 bytes * and a confounder with at least 2 bytes is required. * * Windows uses new_len = 120 => 240 bytes. */ new_len = 120; /* fall through */ case SEC_CHAN_DOMAIN: status = pdb_get_trusted_domain(frame, domain, &td); if (!NT_STATUS_IS_OK(status)) { DEBUG(0, ("pdb_get_trusted_domain() failed for domain %s - %s!\n", domain, nt_errstr(status))); TALLOC_FREE(frame); return status; } new_trust_version = &new_version; break; default: TALLOC_FREE(frame); return NT_STATUS_NOT_SUPPORTED; } timeout = lp_machine_password_timeout(); if (timeout == 0) { if (!force) { DEBUG(10,("machine password never expires\n")); TALLOC_FREE(frame); return NT_STATUS_OK; } } tv.tv_sec = pass_last_set_time; DEBUG(10, ("password last changed %s\n", timeval_string(talloc_tos(), &tv, false))); tv.tv_sec += timeout; DEBUGADD(10, ("password valid until %s\n", timeval_string(talloc_tos(), &tv, false))); if (!force && !timeval_expired(&tv)) { TALLOC_FREE(frame); return NT_STATUS_OK; } /* * Create a random machine account password * We create a random buffer and convert that to utf8. * This is similar to what windows is doing. */ generate_secret_buffer(new_password_buffer, new_len * 2); ok = convert_string_talloc(frame, CH_UTF16MUNGED, CH_UTF8, new_password_buffer, new_len * 2, (void *)&new_trust_passwd, &len); ZERO_STRUCT(new_password_buffer); if (!ok) { DEBUG(0, ("convert_string_talloc failed\n")); TALLOC_FREE(frame); return NT_STATUS_NO_MEMORY; } /* * We could use cli_credentials_get_old_nt_hash(creds, frame) to * set previous_nt_hash. * * But we want to check if the dc has our current password and only do * a change if that's the case. So we keep previous_nt_hash = NULL. * * TODO: * If the previous password is the only password in common with the dc, * we better skip the password change, or use something like * ServerTrustPasswordsGet() or netr_ServerGetTrustInfo() to fix our * local secrets before doing the change. */ status = netlogon_creds_cli_auth(context, b, *current_nt_hash, previous_nt_hash); if (!NT_STATUS_IS_OK(status)) { DEBUG(0, ("netlogon_creds_cli_auth for domain %s - %s!\n", domain, nt_errstr(status))); TALLOC_FREE(frame); return status; } /* * Return the result of trying to write the new password * back into the trust account file. */ switch (sec_channel_type) { case SEC_CHAN_WKSTA: case SEC_CHAN_BDC: ok = secrets_store_machine_password(new_trust_passwd, domain, sec_channel_type); if (!ok) { DEBUG(0, ("secrets_store_machine_password failed for domain %s!\n", domain)); TALLOC_FREE(frame); return NT_STATUS_INTERNAL_DB_CORRUPTION; } break; case SEC_CHAN_DNS_DOMAIN: case SEC_CHAN_DOMAIN: /* * we need to get the sid first for the * pdb_set_trusteddom_pw call */ ok = pdb_set_trusteddom_pw(domain, new_trust_passwd, &td->security_identifier); if (!ok) { DEBUG(0, ("pdb_set_trusteddom_pw() failed for domain %s!\n", domain)); TALLOC_FREE(frame); return NT_STATUS_INTERNAL_DB_CORRUPTION; } break; default: smb_panic("Unsupported secure channel type"); break; } DEBUG(1,("%s : %s(%s): Changed password locally\n", current_timestring(talloc_tos(), false), __func__, domain)); status = netlogon_creds_cli_ServerPasswordSet(context, b, new_trust_passwd, new_trust_version); if (!NT_STATUS_IS_OK(status)) { DEBUG(0,("%s : %s(%s) remote password change set failed - %s\n", current_timestring(talloc_tos(), false), __func__, domain, nt_errstr(status))); TALLOC_FREE(frame); return status; } DEBUG(1,("%s : %s(%s): Changed password remotely.\n", current_timestring(talloc_tos(), false), __func__, domain)); TALLOC_FREE(frame); return NT_STATUS_OK; }