1 /* SPDX-License-Identifier: BSD-2-Clause */
2 /*
3 * Copyright (c) 2017, Intel Corporation
4 * All rights reserved.
5 */
6 #include <glib.h>
7
8 #include <errno.h>
9 #include <fcntl.h>
10 #include <inttypes.h>
11 #include <stdbool.h>
12 #include <stdio.h>
13 #include <stdlib.h>
14 #include <string.h>
15 #include <sys/select.h>
16 #include <unistd.h>
17
18 #include <setjmp.h>
19 #include <cmocka.h>
20
21 #include "connection-manager.h"
22 #include "sink-interface.h"
23 #include "source-interface.h"
24 #include "command-attrs.h"
25 #include "command-source.h"
26 #include "tabrmd-defaults.h"
27 #include "tpm2-command.h"
28 #include "util.h"
29
30 typedef struct source_test_data {
31 ConnectionManager *manager;
32 CommandAttrs *command_attrs;
33 CommandSource *source;
34 Connection *connection;
35 gboolean match;
36 } source_test_data_t;
37
38
39 /* mock function to return TPM command attributes TPMA_CC */
40 TPMA_CC
__wrap_command_attrs_from_cc(CommandAttrs * attrs,TPM2_CC command_code)41 __wrap_command_attrs_from_cc (CommandAttrs *attrs,
42 TPM2_CC command_code)
43 {
44 UNUSED_PARAM(attrs);
45 UNUSED_PARAM(command_code);
46 return (TPMA_CC)mock_type (UINT32);
47 }
48 Connection*
__wrap_connection_manager_lookup_istream(ConnectionManager * manager,GInputStream * istream)49 __wrap_connection_manager_lookup_istream (ConnectionManager *manager,
50 GInputStream *istream)
51 {
52 UNUSED_PARAM(manager);
53 UNUSED_PARAM(istream);
54 g_debug ("%s", __func__);
55 return CONNECTION (mock_ptr_type (GObject*));
56 }
57 gint
__wrap_connection_manager_remove(ConnectionManager * manager,Connection * connection)58 __wrap_connection_manager_remove (ConnectionManager *manager,
59 Connection *connection)
60 {
61 UNUSED_PARAM(manager);
62 UNUSED_PARAM(connection);
63 return mock_type (int);
64 }
65 uint8_t*
__wrap_read_tpm_buffer_alloc(GSocket * socket,size_t * buf_size)66 __wrap_read_tpm_buffer_alloc (GSocket *socket,
67 size_t *buf_size)
68 {
69 uint8_t *buf_src = mock_type (uint8_t*);
70 uint8_t *buf_dst = NULL;
71 size_t size = mock_type (size_t);
72 UNUSED_PARAM(socket);
73
74 g_debug ("%s", __func__);
75 buf_dst = g_malloc0 (size);
76 memcpy (buf_dst, buf_src, size);
77 *buf_size = size;
78
79 return buf_dst;
80 }
81 void
__wrap_sink_enqueue(Sink * sink,GObject * obj)82 __wrap_sink_enqueue (Sink *sink,
83 GObject *obj)
84 {
85 GObject **object;
86 UNUSED_PARAM(sink);
87
88 g_debug ("%s", __func__);
89 object = mock_ptr_type (GObject**);
90
91 *object = G_OBJECT (obj);
92 g_object_ref (*object);
93 }
94 /*
95 * This wrap function allows us to gain access to the data that will be
96 * passed to the source callback registered by the CommandSource object when
97 * a new Connection is added. Without this callback it's not possible to get
98 * access to the source_data_t structure created by the CommandSource insert
99 * function which is required for us to simulate the callback function.
100 */
101 void
__wrap_g_source_set_callback(GSource * source,GSourceFunc func,gpointer data,GDestroyNotify notify)102 __wrap_g_source_set_callback (GSource *source,
103 GSourceFunc func,
104 gpointer data,
105 GDestroyNotify notify)
106 {
107 source_data_t *source_data = (source_data_t*)data;
108 source_data_t **source_data_param = mock_type (source_data_t**);
109 UNUSED_PARAM(source);
110 UNUSED_PARAM(func);
111 UNUSED_PARAM(notify);
112
113 *source_data_param = source_data;
114 }
115 /* command_source_allocate_test begin
116 * Test to allocate and destroy a CommandSource.
117 */
118 static void
command_source_allocate_test(void ** state)119 command_source_allocate_test (void **state)
120 {
121 source_test_data_t *data = (source_test_data_t *)*state;
122
123 data->command_attrs = command_attrs_new ();
124 data->source = command_source_new (data->manager,
125 data->command_attrs);
126 assert_non_null (data->source);
127 }
128
129 static int
command_source_allocate_setup(void ** state)130 command_source_allocate_setup (void **state)
131 {
132 source_test_data_t *data;
133
134 data = calloc (1, sizeof (source_test_data_t));
135 data->manager = connection_manager_new (TABRMD_CONNECTIONS_MAX_DEFAULT);
136
137 *state = data;
138 return 0;
139 }
140 /* command_source_allocate end */
141
142 /* command_source_start_test
143 * This is a basic usage flow test. Can it be started, canceled and joined.
144 * We're testing the underlying pthread usage ... mostly.
145 */
146 void
command_source_start_test(void ** state)147 command_source_start_test (void **state)
148 {
149 source_test_data_t *data;
150 gint ret;
151
152 data = (source_test_data_t*)*state;
153 thread_start(THREAD (data->source));
154 sleep (1);
155 thread_cancel (THREAD (data->source));
156 ret = thread_join (THREAD (data->source));
157 assert_int_equal (ret, 0);
158 }
159
160 static int
command_source_start_setup(void ** state)161 command_source_start_setup (void **state)
162 {
163 source_test_data_t *data;
164
165 data = calloc (1, sizeof (source_test_data_t));
166 data->manager = connection_manager_new (TABRMD_CONNECTIONS_MAX_DEFAULT);
167 if (data->manager == NULL)
168 g_error ("failed to allocate new connection_manager");
169 data->command_attrs = command_attrs_new ();
170 data->source = command_source_new (data->manager,
171 data->command_attrs);
172 if (data->source == NULL)
173 g_error ("failed to allocate new command_source");
174
175 *state = data;
176 return 0;
177 }
178
179 static int
command_source_teardown(void ** state)180 command_source_teardown (void **state)
181 {
182 source_test_data_t *data = (source_test_data_t*)*state;
183
184 g_clear_object (&data->source);
185 g_clear_object (&data->manager);
186 g_clear_object (&data->command_attrs);
187 g_clear_pointer (&data, g_free);
188 return 0;
189 }
190 /* command_source_start_test end */
191
192 /* command_source_connection_insert_test begin
193 * In this test we create a connection source and all that that entails. We then
194 * create a new connection and insert it into the connection manager. We then signal
195 * to the source that there's a new connection in the manager by sending data to
196 * it over the send end of the wakeup pipe "wakeup_send_fd". We then check the
197 * receive_fdset in the source structure to be sure the receive end of the
198 * connection pipe is set. This is how we know that the source is now watching
199 * for data from the new connection.
200 */
201 static int
command_source_connection_setup(void ** state)202 command_source_connection_setup (void **state)
203 {
204 source_test_data_t *data;
205
206 g_debug ("%s", __func__);
207 data = calloc (1, sizeof (source_test_data_t));
208 data->manager = connection_manager_new (TABRMD_CONNECTIONS_MAX_DEFAULT);
209 data->command_attrs = command_attrs_new ();
210 data->source = command_source_new (data->manager,
211 data->command_attrs);
212
213 *state = data;
214 return 0;
215 }
216 static void
command_source_connection_insert_test(void ** state)217 command_source_connection_insert_test (void **state)
218 {
219 struct source_test_data *data = (struct source_test_data*)*state;
220 source_data_t *source_data;
221 CommandSource *source = data->source;
222 GIOStream *iostream;
223 HandleMap *handle_map;
224 Connection *connection;
225 gint ret, client_fd;
226
227 g_debug ("%s", __func__);
228 handle_map = handle_map_new (TPM2_HT_TRANSIENT, MAX_ENTRIES_DEFAULT);
229 iostream = create_connection_iostream (&client_fd);
230 connection = connection_new (iostream, 5, handle_map);
231 g_object_unref (handle_map);
232 g_object_unref (iostream);
233 /* starts the main loop in the CommandSource */
234 ret = thread_start(THREAD (source));
235 will_return (__wrap_g_source_set_callback, &source_data);
236 assert_int_equal (ret, 0);
237 /* normally a callback from the connection manager but we fake it here */
238 sleep (1);
239 command_source_on_new_connection (data->manager, connection, source);
240 /* check internal state of the CommandSource*/
241 assert_int_equal (g_hash_table_size (source->istream_to_source_data_map),
242 1);
243 thread_cancel (THREAD (source));
244 thread_join (THREAD (source));
245 g_object_unref (connection);
246 }
247 /* command_source_session_insert_test end */
248
249 /**
250 * A test: Test the command_source_connection_responder function. We do this
251 * by creating a new Connection object, associating it with a new
252 * Tpm2Command object (that we populate with a command body), and then
253 * calling the command_source_connection_responder.
254 * This function will in turn call the connection_manager_lookup_fd,
255 * tpm2_command_new_from_fd, before finally calling the sink_enqueue function.
256 * We mock these 3 functions to control the flow through the function under
257 * test.
258 * The most tricky bit to this is the way the __wrap_sink_enqueue function
259 * works. Since this thing has no return value we pass it a reference to a
260 * Tpm2Command pointer. It sets this to the Tpm2Command that it receives.
261 * We determine success /failure for this test by verifying that the
262 * sink_enqueue function receives the same Tpm2Command that we passed to
263 * the command under test (command_source_connection_responder).
264 */
265 static void
command_source_on_io_ready_success_test(void ** state)266 command_source_on_io_ready_success_test (void **state)
267 {
268 struct source_test_data *data = (struct source_test_data*)*state;
269 GIOStream *iostream;
270 HandleMap *handle_map;
271 Connection *connection;
272 Tpm2Command *command_out;
273 gint client_fd;
274 guint8 data_in [] = { 0x80, 0x01, 0x0, 0x0, 0x0, 0x17,
275 0x0, 0x0, 0x01, 0x7a, 0x0, 0x0,
276 0x0, 0x06, 0x0, 0x0, 0x01, 0x0,
277 0x0, 0x0, 0x0, 0x7f, 0x0a };
278
279 handle_map = handle_map_new (TPM2_HT_TRANSIENT, MAX_ENTRIES_DEFAULT);
280 iostream = create_connection_iostream (&client_fd);
281 connection = connection_new (iostream, 0, handle_map);
282 g_object_unref (handle_map);
283 g_object_unref (iostream);
284 /* prime wraps */
285 will_return (__wrap_connection_manager_lookup_istream, connection);
286
287 /* setup read of tpm buffer */
288 will_return (__wrap_read_tpm_buffer_alloc, data_in);
289 will_return (__wrap_read_tpm_buffer_alloc, sizeof (data_in));
290 /* setup query for command attributes */
291 will_return (__wrap_command_attrs_from_cc, 0);
292
293 will_return (__wrap_sink_enqueue, &command_out);
294
295 command_source_on_input_ready (NULL, data->source);
296
297 assert_memory_equal (tpm2_command_get_buffer (command_out),
298 data_in,
299 sizeof (data_in));
300 g_object_unref (command_out);
301 }
302 /*
303 * This tests the CommandSource on_io_ready function for situations where
304 * the GSocket associated with a client connection is closed. This causes
305 * the attempt to read data from the socket to return an error indicating
306 * that the socket was closed. In this case the function should return a
307 * value telling GLib to remove the GSource from the main loop. Additionally
308 * the data held internally by the CommandSource must be freed.
309 */
310 static void
command_source_on_io_ready_eof_test(void ** state)311 command_source_on_io_ready_eof_test (void **state)
312 {
313 struct source_test_data *data = (struct source_test_data*)*state;
314 source_data_t *source_data;
315 GIOStream *iostream;
316 HandleMap *handle_map;
317 Connection *connection;
318 ControlMessage *msg;
319 gint client_fd, hash_table_size;
320 gboolean ret;
321
322 handle_map = handle_map_new (TPM2_HT_TRANSIENT, MAX_ENTRIES_DEFAULT);
323 iostream = create_connection_iostream (&client_fd);
324 connection = connection_new (iostream, 0, handle_map);
325 g_object_unref (handle_map);
326 g_object_unref (iostream);
327 /* prime wraps */
328 will_return (__wrap_g_source_set_callback, &source_data);
329 will_return (__wrap_connection_manager_lookup_istream, connection);
330 will_return (__wrap_read_tpm_buffer_alloc, NULL);
331 will_return (__wrap_read_tpm_buffer_alloc, 0);
332 will_return (__wrap_sink_enqueue, &msg);
333 will_return (__wrap_connection_manager_remove, TRUE);
334
335 command_source_on_new_connection (data->manager, connection, data->source);
336 ret = command_source_on_input_ready (g_io_stream_get_input_stream (connection->iostream), source_data);
337 assert_int_equal (ret, G_SOURCE_REMOVE);
338 hash_table_size = g_hash_table_size (data->source->istream_to_source_data_map);
339 assert_int_equal (hash_table_size, 0);
340 g_object_unref (msg);
341 }
342 /* command_source_connection_test end */
343 int
main(void)344 main (void)
345 {
346 const struct CMUnitTest tests[] = {
347 cmocka_unit_test_setup_teardown (command_source_allocate_test,
348 command_source_allocate_setup,
349 command_source_teardown),
350 cmocka_unit_test_setup_teardown (command_source_start_test,
351 command_source_start_setup,
352 command_source_teardown),
353 cmocka_unit_test_setup_teardown (command_source_connection_insert_test,
354 command_source_connection_setup,
355 command_source_teardown),
356 cmocka_unit_test_setup_teardown (command_source_on_io_ready_success_test,
357 command_source_connection_setup,
358 command_source_teardown),
359 cmocka_unit_test_setup_teardown (command_source_on_io_ready_eof_test,
360 command_source_connection_setup,
361 command_source_teardown),
362 };
363 return cmocka_run_group_tests (tests, NULL, NULL);
364 }
365