diff options
Diffstat (limited to 'source4/torture/ntp/ntp_signd.c')
-rw-r--r-- | source4/torture/ntp/ntp_signd.c | 306 |
1 files changed, 306 insertions, 0 deletions
diff --git a/source4/torture/ntp/ntp_signd.c b/source4/torture/ntp/ntp_signd.c new file mode 100644 index 0000000..6d482bf --- /dev/null +++ b/source4/torture/ntp/ntp_signd.c @@ -0,0 +1,306 @@ +/* + Unix SMB/CIFS implementation. + + Test NTP authentication support + + Copyright (C) Andrew Bartlet <abartlet@samba.org> 2008 + + 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 "torture/smbtorture.h" +#include <tevent.h> +#include "lib/stream/packet.h" +#include "lib/tsocket/tsocket.h" +#include "libcli/util/tstream.h" +#include "torture/rpc/torture_rpc.h" +#include "libcli/auth/libcli_auth.h" +#include "librpc/gen_ndr/ndr_netlogon_c.h" +#include "librpc/gen_ndr/ndr_ntp_signd.h" +#include "param/param.h" +#include "system/network.h" +#include "torture/ntp/proto.h" + +#include <gnutls/gnutls.h> +#include <gnutls/crypto.h> + +#define TEST_MACHINE_NAME "ntpsigndtest" + +struct signd_client_state { + struct tsocket_address *local_address; + struct tsocket_address *remote_address; + + struct tstream_context *tstream; + struct tevent_queue *send_queue; + + uint8_t request_hdr[4]; + struct iovec request_iov[2]; + + DATA_BLOB reply; + + NTSTATUS status; +}; + +/* + * A torture test to show that the unix domain socket protocol is + * operating correctly, and the signatures are as expected + */ +static bool test_ntp_signd(struct torture_context *tctx, + struct dcerpc_pipe *p, + struct cli_credentials *credentials) +{ + struct netlogon_creds_CredentialState *creds; + TALLOC_CTX *mem_ctx = talloc_new(tctx); + + struct netr_ServerReqChallenge r; + struct netr_ServerAuthenticate3 a; + struct netr_Credential credentials1, credentials2, credentials3; + uint32_t rid; + const char *machine_name; + const struct samr_Password *pwhash = cli_credentials_get_nt_hash(credentials, mem_ctx); + uint32_t negotiate_flags = NETLOGON_NEG_AUTH2_ADS_FLAGS | NETLOGON_NEG_SUPPORTS_AES; + + struct sign_request sign_req; + struct signed_reply signed_reply; + DATA_BLOB sign_req_blob; + + struct signd_client_state *signd_client; + struct tevent_req *req; + char *unix_address; + int sys_errno; + + gnutls_hash_hd_t hash_hnd; + uint8_t sig[16]; + enum ndr_err_code ndr_err; + bool ok; + int rc; + + machine_name = cli_credentials_get_workstation(credentials); + + torture_comment(tctx, "Testing ServerReqChallenge\n"); + + r.in.server_name = NULL; + r.in.computer_name = machine_name; + r.in.credentials = &credentials1; + r.out.return_credentials = &credentials2; + + generate_random_buffer(credentials1.data, sizeof(credentials1.data)); + + torture_assert_ntstatus_ok(tctx, + dcerpc_netr_ServerReqChallenge_r(p->binding_handle, tctx, &r), + "ServerReqChallenge failed"); + torture_assert_ntstatus_ok(tctx, r.out.result, + "ServerReqChallenge failed"); + + a.in.server_name = NULL; + a.in.account_name = talloc_asprintf(tctx, "%s$", machine_name); + a.in.secure_channel_type = SEC_CHAN_WKSTA; + a.in.computer_name = machine_name; + a.in.negotiate_flags = &negotiate_flags; + a.in.credentials = &credentials3; + a.out.return_credentials = &credentials3; + a.out.negotiate_flags = &negotiate_flags; + a.out.rid = &rid; + + creds = netlogon_creds_client_init(tctx, a.in.account_name, + a.in.computer_name, + a.in.secure_channel_type, + &credentials1, &credentials2, + pwhash, &credentials3, + negotiate_flags); + + torture_assert(tctx, creds != NULL, "memory allocation"); + + torture_comment(tctx, "Testing ServerAuthenticate3\n"); + + torture_assert_ntstatus_ok(tctx, + dcerpc_netr_ServerAuthenticate3_r(p->binding_handle, tctx, &a), + "ServerAuthenticate3 failed"); + torture_assert_ntstatus_ok(tctx, a.out.result, + "ServerAuthenticate3 failed"); + torture_assert(tctx, + netlogon_creds_client_check(creds, &credentials3), + "Credential chaining failed"); + + sign_req.op = SIGN_TO_CLIENT; + sign_req.packet_id = 1; + sign_req.key_id = rid; + sign_req.packet_to_sign = data_blob_string_const("I am a tea pot"); + + ndr_err = ndr_push_struct_blob(&sign_req_blob, + mem_ctx, + &sign_req, + (ndr_push_flags_fn_t)ndr_push_sign_request); + torture_assert(tctx, + NDR_ERR_CODE_IS_SUCCESS(ndr_err), + "Failed to push sign_req"); + + signd_client = talloc(mem_ctx, struct signd_client_state); + + /* Create socket addresses */ + torture_comment(tctx, "Creating the socket addresses\n"); + rc = tsocket_address_unix_from_path(signd_client, "", + &signd_client->local_address); + torture_assert(tctx, rc == 0, + "Failed to create local address from unix path."); + + unix_address = talloc_asprintf(signd_client, + "%s/socket", + lpcfg_ntp_signd_socket_directory(tctx->lp_ctx)); + rc = tsocket_address_unix_from_path(mem_ctx, + unix_address, + &signd_client->remote_address); + torture_assert(tctx, rc == 0, + "Failed to create remote address from unix path."); + + /* Connect to the unix socket */ + torture_comment(tctx, "Connecting to the unix socket\n"); + req = tstream_unix_connect_send(signd_client, + tctx->ev, + signd_client->local_address, + signd_client->remote_address); + torture_assert(tctx, req != NULL, + "Failed to create a tstream unix connect request."); + + ok = tevent_req_poll(req, tctx->ev); + torture_assert(tctx, ok == true, + "Failed to poll for tstream_unix_connect_send."); + + rc = tstream_unix_connect_recv(req, + &sys_errno, + signd_client, + &signd_client->tstream); + TALLOC_FREE(req); + torture_assert(tctx, rc == 0, "Failed to connect to signd!"); + + /* Allocate the send queue */ + signd_client->send_queue = tevent_queue_create(signd_client, + "signd_client_queue"); + torture_assert(tctx, signd_client->send_queue != NULL, + "Failed to create send queue!"); + + /* + * Create the request buffer. + * First add the length of the request buffer + */ + RSIVAL(signd_client->request_hdr, 0, sign_req_blob.length); + signd_client->request_iov[0].iov_base = (char *) signd_client->request_hdr; + signd_client->request_iov[0].iov_len = 4; + + signd_client->request_iov[1].iov_base = (char *) sign_req_blob.data; + signd_client->request_iov[1].iov_len = sign_req_blob.length; + + /* Fire the request buffer */ + torture_comment(tctx, "Sending the request\n"); + req = tstream_writev_queue_send(signd_client, + tctx->ev, + signd_client->tstream, + signd_client->send_queue, + signd_client->request_iov, 2); + torture_assert(tctx, req != NULL, + "Failed to send the signd request."); + + ok = tevent_req_poll(req, tctx->ev); + torture_assert(tctx, ok == true, + "Failed to poll for tstream_writev_queue_send."); + + rc = tstream_writev_queue_recv(req, &sys_errno); + TALLOC_FREE(req); + torture_assert(tctx, rc > 0, "Failed to send data"); + + /* Wait for a reply */ + torture_comment(tctx, "Waiting for the reply\n"); + req = tstream_read_pdu_blob_send(signd_client, + tctx->ev, + signd_client->tstream, + 4, /*initial_read_size */ + packet_full_request_u32, + NULL); + torture_assert(tctx, req != NULL, + "Failed to setup a read for pdu_blob."); + + ok = tevent_req_poll(req, tctx->ev); + torture_assert(tctx, ok == true, + "Failed to poll for tstream_read_pdu_blob_send."); + + signd_client->status = tstream_read_pdu_blob_recv(req, + signd_client, + &signd_client->reply); + torture_assert_ntstatus_ok(tctx, signd_client->status, + "Error reading signd_client reply packet"); + + /* Skip length header */ + signd_client->reply.data += 4; + signd_client->reply.length -= 4; + + /* Check if the reply buffer is valid */ + torture_comment(tctx, "Validating the reply buffer\n"); + ndr_err = ndr_pull_struct_blob_all(&signd_client->reply, + mem_ctx, + &signed_reply, + (ndr_pull_flags_fn_t)ndr_pull_signed_reply); + torture_assert(tctx, NDR_ERR_CODE_IS_SUCCESS(ndr_err), + ndr_map_error2string(ndr_err)); + + torture_assert_u64_equal(tctx, signed_reply.version, + NTP_SIGND_PROTOCOL_VERSION_0, + "Invalid Version"); + torture_assert_u64_equal(tctx, signed_reply.packet_id, + sign_req.packet_id, "Invalid Packet ID"); + torture_assert_u64_equal(tctx, signed_reply.op, + SIGNING_SUCCESS, + "Should have replied with signing success"); + torture_assert_u64_equal(tctx, signed_reply.signed_packet.length, + sign_req.packet_to_sign.length + 20, + "Invalid reply length from signd"); + torture_assert_u64_equal(tctx, rid, + IVAL(signed_reply.signed_packet.data, + sign_req.packet_to_sign.length), + "Incorrect RID in reply"); + + /* Check computed signature */ + gnutls_hash_init(&hash_hnd, GNUTLS_DIG_MD5); + gnutls_hash(hash_hnd, pwhash->hash, sizeof(pwhash->hash)); + gnutls_hash(hash_hnd, + sign_req.packet_to_sign.data, + sign_req.packet_to_sign.length); + gnutls_hash_deinit(hash_hnd, sig); + + torture_assert_mem_equal(tctx, + &signed_reply.signed_packet.data[sign_req.packet_to_sign.length + 4], + sig, 16, "Signature on reply was incorrect!"); + + talloc_free(mem_ctx); + + return true; +} + +NTSTATUS torture_ntp_init(TALLOC_CTX *ctx) +{ + struct torture_suite *suite = torture_suite_create(ctx, "ntp"); + struct torture_rpc_tcase *tcase; + + tcase = torture_suite_add_machine_workstation_rpc_iface_tcase(suite, + "signd", &ndr_table_netlogon, TEST_MACHINE_NAME); + + torture_rpc_tcase_add_test_creds(tcase, "ntp_signd", test_ntp_signd); + + suite->description = talloc_strdup(suite, "NTP tests"); + + torture_register_suite(ctx, suite); + + return NT_STATUS_OK; +} + |