1 /*
2 * Copyright (c) 2002-2013 Balabit
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
17 *
18 * As an additional exemption you are allowed to compile & link against the
19 * OpenSSL libraries as published by the OpenSSL project. See the file
20 * COPYING for details.
21 *
22 */
23 #include "cfg-lexer-subst.h"
24 #include "apphook.h"
25 #include <criterion/criterion.h>
26 #include <string.h>
27
28 static CfgArgs *
construct_cfg_args_for_args(const gchar * additional_values[])29 construct_cfg_args_for_args(const gchar *additional_values[])
30 {
31 CfgArgs *args = cfg_args_new();
32 gint i;
33
34 cfg_args_set(args, "arg", "arg_value");
35 cfg_args_set(args, "simple_string", "\"simple_string_value\"");
36 cfg_args_set(args, "simple_qstring", "'simple_qstring_value'");
37 cfg_args_set(args, "escaped_string", "\"escaped_string\\\"\\r\\n\"");
38
39 cr_assert(cfg_args_contains(args, "simple-string"), "normalize: when set, must contain: simple-string");
40 cr_assert(cfg_args_contains(args, "simple_string"), "normalize: when set, must contain: simple_string");
41
42 for (i = 0; additional_values && additional_values[i] && additional_values[i + 1]; i += 2)
43 {
44 cfg_args_set(args, additional_values[i], additional_values[i + 1]);
45 }
46 return args;
47 }
48
49 static CfgArgs *
construct_cfg_args_for_defaults(void)50 construct_cfg_args_for_defaults(void)
51 {
52 CfgArgs *args = cfg_args_new();
53 cfg_args_set(args, "arg", "default_for_arg");
54 cfg_args_set(args, "def", "default_for_def");
55 return args;
56 }
57
58 static CfgArgs *
construct_cfg_args_for_globals(void)59 construct_cfg_args_for_globals(void)
60 {
61 CfgArgs *args = cfg_args_new();
62 cfg_args_set(args, "arg", "global_for_arg");
63 cfg_args_set(args, "def", "global_for_def");
64 cfg_args_set(args, "globl", "global_for_globl");
65 return args;
66 }
67
68 static CfgLexerSubst *
construct_object_with_values(const gchar * additional_values[])69 construct_object_with_values(const gchar *additional_values[])
70 {
71 return cfg_lexer_subst_new(construct_cfg_args_for_globals(),
72 construct_cfg_args_for_defaults(),
73 construct_cfg_args_for_args(additional_values));
74 }
75
76 static CfgLexerSubst *
construct_object(void)77 construct_object(void)
78 {
79 return construct_object_with_values(NULL);
80 }
81
82 static gchar *
duplicate_string_without_with_trailing_nonzero_bytes(const gchar * input)83 duplicate_string_without_with_trailing_nonzero_bytes(const gchar *input)
84 {
85 gsize input_len = strlen(input);
86 const gint canary_size = 10;
87 gchar *input_dup = malloc(input_len + canary_size);
88
89 memcpy(input_dup, input, input_len);
90 memset(input_dup + input_len, 'A', canary_size - 1);
91 input_dup[input_len + canary_size - 1] = 0;
92 return input_dup;
93 }
94
95 static void
_assert_invoke_result(CfgLexerSubst * subst,gchar * input_dup,gssize input_len,const gchar * expected_output)96 _assert_invoke_result(CfgLexerSubst *subst, gchar *input_dup, gssize input_len, const gchar *expected_output)
97 {
98 gchar *result;
99 gsize result_len;
100 GError *error = NULL;
101
102 result = cfg_lexer_subst_invoke(subst, input_dup, input_len, &result_len, &error);
103 cr_assert_null(error, "Error value is non-null while no error is expected");
104 cr_assert_not_null(result, "value substitution returned an unexpected failure");
105 cr_assert_str_eq(result, expected_output, "value substitution is broken");
106 cr_assert_eq(result_len, strlen(expected_output), "length returned by invoke_result is invalid");
107 g_free(result);
108 }
109
110 static void
assert_invoke_result_with_input_length_explicit(CfgLexerSubst * subst,const gchar * input,const gchar * expected_output)111 assert_invoke_result_with_input_length_explicit(CfgLexerSubst *subst, const gchar *input, const gchar *expected_output)
112 {
113 gchar *input_dup;
114
115 input_dup = duplicate_string_without_with_trailing_nonzero_bytes(input);
116 _assert_invoke_result(subst, input_dup, strlen(input), expected_output);
117 g_free(input_dup);
118 }
119
120 static void
assert_invoke_result_with_zero_terminated_input(CfgLexerSubst * subst,const gchar * input,const gchar * expected_output)121 assert_invoke_result_with_zero_terminated_input(CfgLexerSubst *subst, const gchar *input, const gchar *expected_output)
122 {
123 gchar *input_dup;
124
125 input_dup = strdup(input);
126 _assert_invoke_result(subst, input_dup, -1, expected_output);
127 g_free(input_dup);
128 }
129
130 static void
assert_invoke_result(CfgLexerSubst * subst,const gchar * input,const gchar * expected_output)131 assert_invoke_result(CfgLexerSubst *subst, const gchar *input, const gchar *expected_output)
132 {
133 assert_invoke_result_with_input_length_explicit(subst, input, expected_output);
134 assert_invoke_result_with_zero_terminated_input(subst, input, expected_output);
135 }
136
137 static void
assert_invoke_failure(CfgLexerSubst * subst,const gchar * input,const gchar * expected_error)138 assert_invoke_failure(CfgLexerSubst *subst, const gchar *input, const gchar *expected_error)
139 {
140 gchar *result;
141 gsize result_len;
142 gchar *input_dup = g_strdup(input);
143 GError *error = NULL;
144
145 result = cfg_lexer_subst_invoke(subst, input_dup, strlen(input), &result_len, &error);
146 cr_assert_null(result, "expected failure for value substitution, but success was returned");
147 cr_assert_not_null(error, "expected a non-NULL error object for failure");
148 cr_assert_str_eq(error->message, expected_error, "error message mismatch");
149 g_free(input_dup);
150 }
151
Test(cfg_lexer_subst,test_double_backtick_replaced_with_a_single_one)152 Test(cfg_lexer_subst, test_double_backtick_replaced_with_a_single_one)
153 {
154 CfgLexerSubst *subst = construct_object();
155 assert_invoke_result(subst, "``", "`");
156 cfg_lexer_subst_free(subst);
157 }
158
Test(cfg_lexer_subst,test_single_backtick_causes_an_error)159 Test(cfg_lexer_subst, test_single_backtick_causes_an_error)
160 {
161 CfgLexerSubst *subst = construct_object();
162 assert_invoke_failure(subst, "foo ` bar", "missing closing backtick (`) character");
163 cfg_lexer_subst_free(subst);
164 }
165
Test(cfg_lexer_subst,test_backtick_after_quoted_character_succeeds)166 Test(cfg_lexer_subst, test_backtick_after_quoted_character_succeeds)
167 {
168 CfgLexerSubst *subst = construct_object();
169 assert_invoke_result(subst, "foo \"string \\n`arg`\" bar", "foo \"string \\narg_value\" bar");
170 cfg_lexer_subst_free(subst);
171 }
172
Test(cfg_lexer_subst,test_backtick_as_a_quoted_character_in_a_string_results_in_failure)173 Test(cfg_lexer_subst, test_backtick_as_a_quoted_character_in_a_string_results_in_failure)
174 {
175 CfgLexerSubst *subst = construct_object();
176
177 assert_invoke_failure(subst, "foo \"string \\`arg`\" bar",
178 "cannot subsitute backticked values right after a string quote character");
179 cfg_lexer_subst_free(subst);
180 }
181
Test(cfg_lexer_subst,test_value_in_normal_text_replaced_with_its_literal_value)182 Test(cfg_lexer_subst, test_value_in_normal_text_replaced_with_its_literal_value)
183 {
184 CfgLexerSubst *subst = construct_object();
185 assert_invoke_result(subst, "foo `arg` bar", "foo arg_value bar");
186 assert_invoke_result(subst, "foo `simple_string` bar", "foo \"simple_string_value\" bar");
187 assert_invoke_result(subst, "foo `simple_qstring` bar", "foo 'simple_qstring_value' bar");
188 assert_invoke_result(subst, "foo `escaped_string` bar", "foo \"escaped_string\\\"\\r\\n\" bar");
189 cfg_lexer_subst_free(subst);
190 }
191
Test(cfg_lexer_subst,test_values_are_resolution_order_args_defaults_globals_env)192 Test(cfg_lexer_subst, test_values_are_resolution_order_args_defaults_globals_env)
193 {
194 CfgLexerSubst *subst = construct_object();
195
196 setenv("env", "env_for_env", TRUE);
197 assert_invoke_result(subst, "foo `arg` bar", "foo arg_value bar");
198 assert_invoke_result(subst, "foo `def` bar", "foo default_for_def bar");
199 assert_invoke_result(subst, "foo `globl` bar", "foo global_for_globl bar");
200 assert_invoke_result(subst, "foo `env` bar", "foo env_for_env bar");
201 cfg_lexer_subst_free(subst);
202 }
203
Test(cfg_lexer_subst,test_values_are_inserted_within_strings)204 Test(cfg_lexer_subst, test_values_are_inserted_within_strings)
205 {
206 CfgLexerSubst *subst = construct_object();
207
208 assert_invoke_result(subst, "foo \"`arg`\" bar", "foo \"arg_value\" bar");
209 assert_invoke_result(subst, "foo '`arg`' bar", "foo 'arg_value' bar");
210 cfg_lexer_subst_free(subst);
211 }
212
Test(cfg_lexer_subst,test_string_literals_are_inserted_into_strings_without_quotes)213 Test(cfg_lexer_subst, test_string_literals_are_inserted_into_strings_without_quotes)
214 {
215 const gchar *additional_values[] =
216 {
217 "simple_string_with_whitespace", " \"string_with_whitespace\" ",
218 NULL, NULL
219 };
220 CfgLexerSubst *subst = construct_object_with_values(additional_values);
221
222 /* double quotes */
223 assert_invoke_result(subst, "foo \"x `simple_string` y\" bar", "foo \"x simple_string_value y\" bar");
224 /* apostrophes */
225 assert_invoke_result(subst, "foo 'x `simple_string` y' bar", "foo 'x simple_string_value y' bar");
226 assert_invoke_result(subst, "foo \"x `simple_string_with_whitespace` y\" bar",
227 "foo \"x string_with_whitespace y\" bar");
228 cfg_lexer_subst_free(subst);
229 }
230
Test(cfg_lexer_subst,test_incorrect_strings_and_multiple_tokens_are_inserted_verbatim)231 Test(cfg_lexer_subst, test_incorrect_strings_and_multiple_tokens_are_inserted_verbatim)
232 {
233 const gchar *additional_values[] =
234 {
235 "half_string", "\"halfstring",
236 "tokens_that_start_with_string", "\"str\", token",
237 "tokens_enclosed_in_strings", "\"str1\", token, \"str2\"",
238 NULL, NULL
239 };
240 CfgLexerSubst *subst = construct_object_with_values(additional_values);
241
242 assert_invoke_result(subst, "foo \"x `simple_string` y\" bar", "foo \"x simple_string_value y\" bar");
243 assert_invoke_result(subst, "foo \"x `half_string` y\" bar", "foo \"x \"halfstring y\" bar");
244 assert_invoke_result(subst, "foo \"x `tokens_that_start_with_string` y\" bar", "foo \"x \"str\", token y\" bar");
245 assert_invoke_result(subst, "foo \"x `tokens_enclosed_in_strings` y\" bar",
246 "foo \"x \"str1\", token, \"str2\" y\" bar");
247 cfg_lexer_subst_free(subst);
248 }
249
Test(cfg_lexer_subst,test_strings_with_special_chars_are_properly_encoded_in_strings)250 Test(cfg_lexer_subst, test_strings_with_special_chars_are_properly_encoded_in_strings)
251 {
252 const gchar *additional_values[] =
253 {
254 "string_with_characters_that_need_quoting", "\"quote: \\\", newline: \\r\\n, backslash: \\\\\"",
255 NULL, NULL
256 };
257 CfgLexerSubst *subst = construct_object_with_values(additional_values);
258
259 assert_invoke_result(subst, "foo \"x `string_with_characters_that_need_quoting` y\" bar",
260 "foo \"x quote: \\\", newline: \\r\\n, backslash: \\\\ y\" bar");
261 cfg_lexer_subst_free(subst);
262 }
263
Test(cfg_lexer_subst,test_strings_with_embedded_apostrophe_cause_an_error_when_encoding_in_qstring)264 Test(cfg_lexer_subst, test_strings_with_embedded_apostrophe_cause_an_error_when_encoding_in_qstring)
265 {
266 const gchar *additional_values[] =
267 {
268 "string_with_apostrophe", "\"'foo'\"",
269 NULL, NULL
270 };
271 CfgLexerSubst *subst = construct_object_with_values(additional_values);
272
273 assert_invoke_result(subst, "foo \"x `string_with_apostrophe` y\" bar", "foo \"x 'foo' y\" bar");
274 assert_invoke_failure(subst, "foo 'x `string_with_apostrophe` y' bar",
275 "cannot represent apostrophes within apostroph-enclosed string");
276 cfg_lexer_subst_free(subst);
277 }
278
Test(cfg_lexer_subst,test_tracking_string_state)279 Test(cfg_lexer_subst, test_tracking_string_state)
280 {
281 const gchar *additional_values[] =
282 {
283 "quoted_escaped_newline", "\"\\n\"",
284 NULL, NULL
285 };
286
287 CfgLexerSubst *subst = construct_object_with_values(additional_values);
288
289 assert_invoke_result(subst, "\"hello\\n\" `quoted_escaped_newline`", "\"hello\\n\" \"\\n\"");
290 assert_invoke_result(subst, "\"hello\\n\\n\" `quoted_escaped_newline`", "\"hello\\n\\n\" \"\\n\"");
291 assert_invoke_result(subst, "\"hello\\n\\n \" `quoted_escaped_newline`", "\"hello\\n\\n \" \"\\n\"");
292 cfg_lexer_subst_free(subst);
293 }
294
295 TestSuite(cfg_lexer_subst, .init = app_startup, .fini = app_shutdown);
296