/* Unix SMB/CIFS implementation. module to store/fetch session keys for the schannel server Copyright (C) Andrew Tridgell 2004 Copyright (C) Andrew Bartlett <abartlet@samba.org> 2006-2009 Copyright (C) Guenther Deschner 2009 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 <http://www.gnu.org/licenses/>. */ #include "includes.h" #include "../libcli/auth/libcli_auth.h" #include "../libcli/auth/schannel_state.h" #include "../librpc/gen_ndr/ndr_schannel.h" /******************************************************************** ********************************************************************/ NTSTATUS schannel_store_session_key_tdb(struct tdb_context *tdb, TALLOC_CTX *mem_ctx, struct netlogon_creds_CredentialState *creds) { enum ndr_err_code ndr_err; DATA_BLOB blob; TDB_DATA value; int ret; char *keystr; keystr = talloc_asprintf_strupper_m(mem_ctx, "%s/%s", SECRETS_SCHANNEL_STATE, creds->computer_name); if (!keystr) { return NT_STATUS_NO_MEMORY; } ndr_err = ndr_push_struct_blob(&blob, mem_ctx, NULL, creds, (ndr_push_flags_fn_t)ndr_push_netlogon_creds_CredentialState); if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { talloc_free(keystr); return ndr_map_error2ntstatus(ndr_err); } value.dptr = blob.data; value.dsize = blob.length; ret = tdb_store_bystring(tdb, keystr, value, TDB_REPLACE); if (ret != TDB_SUCCESS) { DEBUG(0,("Unable to add %s to session key db - %s\n", keystr, tdb_errorstr(tdb))); talloc_free(keystr); return NT_STATUS_INTERNAL_DB_CORRUPTION; } DEBUG(3,("schannel_store_session_key_tdb: stored schannel info with key %s\n", keystr)); if (DEBUGLEVEL >= 10) { NDR_PRINT_DEBUG(netlogon_creds_CredentialState, creds); } talloc_free(keystr); return NT_STATUS_OK; } /******************************************************************** ********************************************************************/ NTSTATUS schannel_fetch_session_key_tdb(struct tdb_context *tdb, TALLOC_CTX *mem_ctx, const char *computer_name, struct netlogon_creds_CredentialState **pcreds) { NTSTATUS status; TDB_DATA value; enum ndr_err_code ndr_err; DATA_BLOB blob; struct netlogon_creds_CredentialState *creds = NULL; char *keystr = NULL; *pcreds = NULL; keystr = talloc_asprintf_strupper_m(mem_ctx, "%s/%s", SECRETS_SCHANNEL_STATE, computer_name); if (!keystr) { status = NT_STATUS_NO_MEMORY; goto done; } value = tdb_fetch_bystring(tdb, keystr); if (!value.dptr) { DEBUG(0,("schannel_fetch_session_key_tdb: Failed to find entry with key %s\n", keystr )); status = NT_STATUS_OBJECT_NAME_NOT_FOUND; goto done; } creds = talloc_zero(mem_ctx, struct netlogon_creds_CredentialState); if (!creds) { status = NT_STATUS_NO_MEMORY; goto done; } blob = data_blob_const(value.dptr, value.dsize); ndr_err = ndr_pull_struct_blob(&blob, mem_ctx, NULL, creds, (ndr_pull_flags_fn_t)ndr_pull_netlogon_creds_CredentialState); if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { status = ndr_map_error2ntstatus(ndr_err); goto done; } if (DEBUGLEVEL >= 10) { NDR_PRINT_DEBUG(netlogon_creds_CredentialState, creds); } DEBUG(3,("schannel_fetch_session_key_tdb: restored schannel info key %s\n", keystr)); status = NT_STATUS_OK; done: talloc_free(keystr); if (!NT_STATUS_IS_OK(status)) { talloc_free(creds); return status; } *pcreds = creds; return NT_STATUS_OK; } /******************************************************************** Validate an incoming authenticator against the credentials for the remote machine. The credentials are (re)read and from the schannel database, and written back after the caclulations are performed. The creds_out parameter (if not NULL) returns the credentials, if the caller needs some of that information. ********************************************************************/ NTSTATUS schannel_creds_server_step_check_tdb(struct tdb_context *tdb, TALLOC_CTX *mem_ctx, const char *computer_name, bool schannel_required_for_call, bool schannel_in_use, struct netr_Authenticator *received_authenticator, struct netr_Authenticator *return_authenticator, struct netlogon_creds_CredentialState **creds_out) { struct netlogon_creds_CredentialState *creds; NTSTATUS status; int ret; ret = tdb_transaction_start(tdb); if (ret != 0) { return NT_STATUS_INTERNAL_DB_CORRUPTION; } /* Because this is a shared structure (even across * disconnects) we must update the database every time we * update the structure */ status = schannel_fetch_session_key_tdb(tdb, mem_ctx, computer_name, &creds); /* If we are flaged that schannel is required for a call, and * it is not in use, then make this an error */ /* It would be good to make this mandatory once schannel is * negotiated, but this is not what windows does */ if (schannel_required_for_call && !schannel_in_use) { DEBUG(0,("schannel_creds_server_step_check_tdb: " "client %s not using schannel for netlogon, despite negotiating it\n", creds->computer_name )); tdb_transaction_cancel(tdb); return NT_STATUS_ACCESS_DENIED; } if (NT_STATUS_IS_OK(status)) { status = netlogon_creds_server_step_check(creds, received_authenticator, return_authenticator); } if (NT_STATUS_IS_OK(status)) { status = schannel_store_session_key_tdb(tdb, mem_ctx, creds); } if (NT_STATUS_IS_OK(status)) { tdb_transaction_commit(tdb); if (creds_out) { *creds_out = creds; talloc_steal(mem_ctx, creds); } } else { tdb_transaction_cancel(tdb); } return status; }