1 /*
2 * Copyright (c) 2011-2015 Balabit
3 * Copyright (c) 2011-2014 Gergely Nagy <algernon@balabit.hu>
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2.1 of the License, or (at your option) any later version.
9 *
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18 *
19 * As an additional exemption you are allowed to compile & link against the
20 * OpenSSL libraries as published by the OpenSSL project. See the file
21 * COPYING for details.
22 *
23 */
24 #include "value-pairs/cmdline.h"
25 #include "value-pairs/internals.h"
26
27 #include <string.h>
28 #include <stdlib.h>
29 #include <ctype.h>
30
31 /*******************************************************************************
32 * Command line parser
33 *******************************************************************************/
34
35 static void
vp_cmdline_parse_rekey_finish(gpointer data)36 vp_cmdline_parse_rekey_finish (gpointer data)
37 {
38 gpointer *args = (gpointer *) data;
39 ValuePairs *vp = (ValuePairs *) args[1];
40 ValuePairsTransformSet *vpts = (ValuePairsTransformSet *) args[2];
41
42 if (vpts)
43 value_pairs_add_transforms (vp, args[2]);
44 args[2] = NULL;
45 g_free(args[3]);
46 args[3] = NULL;
47 }
48
49 static void
vp_cmdline_start_key(gpointer data,const gchar * key)50 vp_cmdline_start_key(gpointer data, const gchar *key)
51 {
52 gpointer *args = (gpointer *) data;
53
54 vp_cmdline_parse_rekey_finish (data);
55 args[3] = g_strdup(key);
56 }
57
58 static ValuePairsTransformSet *
vp_cmdline_rekey_verify(gchar * key,ValuePairsTransformSet * vpts,gpointer data)59 vp_cmdline_rekey_verify (gchar *key, ValuePairsTransformSet *vpts,
60 gpointer data)
61 {
62 gpointer *args = (gpointer *)data;
63
64 if (!vpts)
65 {
66 if (!key)
67 return NULL;
68 vpts = value_pairs_transform_set_new (key);
69 vp_cmdline_parse_rekey_finish (data);
70 args[2] = vpts;
71 return vpts;
72 }
73 return vpts;
74 }
75
76 /* parse a value-pair specification from a command-line like environment */
77 static gboolean
vp_cmdline_parse_scope(const gchar * option_name,const gchar * value,gpointer data,GError ** error)78 vp_cmdline_parse_scope(const gchar *option_name, const gchar *value,
79 gpointer data, GError **error)
80 {
81 gpointer *args = (gpointer *) data;
82 ValuePairs *vp = (ValuePairs *) args[1];
83 gchar **scopes;
84 gint i;
85
86 vp_cmdline_parse_rekey_finish (data);
87
88 scopes = g_strsplit (value, ",", -1);
89 for (i = 0; scopes[i] != NULL; i++)
90 {
91 if (!value_pairs_add_scope (vp, scopes[i]))
92 {
93 g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE,
94 "Error parsing value-pairs: unknown scope %s", scopes[i]);
95 g_strfreev (scopes);
96 return FALSE;
97 }
98 }
99 g_strfreev (scopes);
100
101 return TRUE;
102 }
103
104 static gboolean
vp_cmdline_parse_exclude(const gchar * option_name,const gchar * value,gpointer data,GError ** error)105 vp_cmdline_parse_exclude(const gchar *option_name, const gchar *value,
106 gpointer data, GError **error)
107 {
108 gpointer *args = (gpointer *) data;
109 ValuePairs *vp = (ValuePairs *) args[1];
110 gchar **excludes;
111 gint i;
112
113 vp_cmdline_parse_rekey_finish (data);
114
115 excludes = g_strsplit(value, ",", -1);
116 for (i = 0; excludes[i] != NULL; i++)
117 value_pairs_add_glob_pattern(vp, excludes[i], FALSE);
118 g_strfreev(excludes);
119
120 return TRUE;
121 }
122
123 static gboolean
vp_cmdline_parse_key(const gchar * option_name,const gchar * value,gpointer data,GError ** error)124 vp_cmdline_parse_key(const gchar *option_name, const gchar *value,
125 gpointer data, GError **error)
126 {
127 gpointer *args = (gpointer *) data;
128 ValuePairs *vp = (ValuePairs *) args[1];
129 gchar **keys;
130 gint i;
131
132 vp_cmdline_start_key(data, value);
133
134 keys = g_strsplit(value, ",", -1);
135 for (i = 0; keys[i] != NULL; i++)
136 value_pairs_add_glob_pattern(vp, keys[i], TRUE);
137 g_strfreev(keys);
138
139 return TRUE;
140 }
141
142 static gboolean
vp_cmdline_parse_rekey(const gchar * option_name,const gchar * value,gpointer data,GError ** error)143 vp_cmdline_parse_rekey(const gchar *option_name, const gchar *value,
144 gpointer data, GError **error)
145 {
146 vp_cmdline_start_key(data, value);
147 return TRUE;
148 }
149
150 static void
value_pairs_parse_type(gchar * spec,gchar ** value,gchar ** type)151 value_pairs_parse_type(gchar *spec, gchar **value, gchar **type)
152 {
153 char *sp, *ep;
154
155 *type = NULL;
156 sp = spec;
157
158 while (g_ascii_isalnum(*sp) || (*sp) == '_')
159 sp++;
160
161 while (*sp == ' ' || *sp == '\t')
162 sp++;
163
164 if (*sp != '(' ||
165 !((g_ascii_toupper(spec[0]) >= 'A' &&
166 g_ascii_toupper(spec[0]) <= 'Z') ||
167 spec[0] == '_'))
168 {
169 *value = spec;
170 return;
171 }
172
173 ep = strrchr(sp, ')');
174 if (ep == NULL || ep[1] != '\0')
175 {
176 *value = spec;
177 return;
178 }
179
180 *value = sp + 1;
181 *type = spec;
182 sp[0] = '\0';
183 ep[0] = '\0';
184 }
185
186 static gboolean
vp_cmdline_parse_pair(const gchar * option_name,const gchar * value,gpointer data,GError ** error)187 vp_cmdline_parse_pair (const gchar *option_name, const gchar *value,
188 gpointer data, GError **error)
189 {
190 gpointer *args = (gpointer *) data;
191 ValuePairs *vp = (ValuePairs *) args[1];
192 GlobalConfig *cfg = (GlobalConfig *) args[0];
193 gchar **kv, *v, *t;
194 gboolean res = FALSE;
195 LogTemplate *template;
196
197 vp_cmdline_parse_rekey_finish (data);
198
199 if (strchr(value, '=') == NULL)
200 {
201 g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE,
202 "Error parsing value-pairs: expected an equal sign in key=value pair");
203 return FALSE;
204 }
205
206 kv = g_strsplit(value, "=", 2);
207 value_pairs_parse_type(kv[1], &v, &t);
208
209 template = log_template_new(cfg, NULL);
210 if (!log_template_compile(template, v, error))
211 goto error;
212 if (!log_template_set_type_hint(template, t, error))
213 goto error;
214
215 value_pairs_add_pair(vp, kv[0], template);
216
217 res = TRUE;
218 error:
219 log_template_unref(template);
220 g_strfreev(kv);
221
222 return res;
223 }
224
225 static gboolean
vp_cmdline_parse_pair_or_key(const gchar * option_name,const gchar * value,gpointer data,GError ** error)226 vp_cmdline_parse_pair_or_key (const gchar *option_name, const gchar *value,
227 gpointer data, GError **error)
228 {
229 if (strchr(value, '=') == NULL)
230 return vp_cmdline_parse_key(option_name, value, data, error);
231 else
232 return vp_cmdline_parse_pair(option_name, value, data, error);
233 }
234
235 static gboolean
vp_cmdline_parse_subkeys(const gchar * option_name,const gchar * value,gpointer data,GError ** error)236 vp_cmdline_parse_subkeys(const gchar *option_name, const gchar *value,
237 gpointer data, GError **error)
238 {
239 gpointer *args = (gpointer *) data;
240 ValuePairs *vp = (ValuePairs *) args[1];
241 ValuePairsTransformSet *vpts = (ValuePairsTransformSet *) args[2];
242
243 if (!value[0])
244 {
245 g_set_error(error, G_OPTION_ERROR, G_OPTION_ERROR_FAILED,
246 "Error parsing value-pairs: --subkeys requires a non-empty argument");
247 return FALSE;
248 }
249
250 GString *prefix = g_string_new(value);
251 g_string_append_c(prefix, '*');
252 value_pairs_add_glob_pattern(vp, prefix->str, TRUE);
253
254 vp_cmdline_start_key(data, prefix->str);
255
256 vpts = vp_cmdline_rekey_verify(prefix->str, vpts, data);
257 if (!vpts)
258 {
259 g_set_error(error, G_OPTION_ERROR, G_OPTION_ERROR_FAILED,
260 "Error parsing value-pairs: --subkeys failed to create key");
261 g_string_free(prefix, TRUE);
262 return FALSE;
263 }
264
265 value_pairs_transform_set_add_func
266 (vpts, value_pairs_new_transform_replace_prefix(value, ""));
267
268 g_string_free(prefix, TRUE);
269 return TRUE;
270 }
271
272 static gboolean
vp_cmdline_parse_rekey_replace_prefix(const gchar * option_name,const gchar * value,gpointer data,GError ** error)273 vp_cmdline_parse_rekey_replace_prefix (const gchar *option_name, const gchar *value,
274 gpointer data, GError **error)
275 {
276 gpointer *args = (gpointer *) data;
277 ValuePairsTransformSet *vpts = (ValuePairsTransformSet *) args[2];
278 gchar *key = (gchar *) args[3];
279 gchar **kv;
280
281 vpts = vp_cmdline_rekey_verify (key, vpts, data);
282 if (!vpts)
283 {
284 g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_FAILED,
285 "Error parsing value-pairs: --replace-prefix used without --key or --rekey");
286 return FALSE;
287 }
288
289 if (!g_strstr_len (value, strlen (value), "="))
290 {
291 g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE,
292 "Error parsing value-pairs: rekey replace-prefix construct should be in the format string=replacement");
293 return FALSE;
294 }
295
296 kv = g_strsplit(value, "=", 2);
297 value_pairs_transform_set_add_func
298 (vpts, value_pairs_new_transform_replace_prefix (kv[0], kv[1]));
299
300 g_free (kv[0]);
301 g_free (kv[1]);
302 g_free (kv);
303
304 return TRUE;
305 }
306
307 static gboolean
vp_cmdline_parse_rekey_add_prefix(const gchar * option_name,const gchar * value,gpointer data,GError ** error)308 vp_cmdline_parse_rekey_add_prefix (const gchar *option_name, const gchar *value,
309 gpointer data, GError **error)
310 {
311 gpointer *args = (gpointer *) data;
312 ValuePairsTransformSet *vpts = (ValuePairsTransformSet *) args[2];
313 gchar *key = (gchar *) args[3];
314
315 vpts = vp_cmdline_rekey_verify (key, vpts, data);
316 if (!vpts)
317 {
318 g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_FAILED,
319 "Error parsing value-pairs: --add-prefix used without --key or --rekey");
320 return FALSE;
321 }
322
323 value_pairs_transform_set_add_func
324 (vpts, value_pairs_new_transform_add_prefix (value));
325 return TRUE;
326 }
327
328 static gboolean
vp_cmdline_parse_rekey_shift(const gchar * option_name,const gchar * value,gpointer data,GError ** error)329 vp_cmdline_parse_rekey_shift (const gchar *option_name, const gchar *value,
330 gpointer data, GError **error)
331 {
332 gpointer *args = (gpointer *) data;
333 ValuePairsTransformSet *vpts = (ValuePairsTransformSet *) args[2];
334 gchar *key = (gchar *) args[3];
335 gchar *end = NULL;
336 gint number_to_shift = strtol(value, &end, 0);
337
338 if (number_to_shift <= 0 || *end != 0)
339 {
340 g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_FAILED,
341 "Error parsing value-pairs, argument to --shift is not numeric or not a positive number");
342 return FALSE;
343 }
344
345 vpts = vp_cmdline_rekey_verify (key, vpts, data);
346 if (!vpts)
347 {
348 g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_FAILED,
349 "Error parsing value-pairs: --shift used without --key or --rekey");
350 return FALSE;
351 }
352
353 value_pairs_transform_set_add_func(vpts,
354 value_pairs_new_transform_shift (number_to_shift));
355 return TRUE;
356 }
357
358 static gboolean
vp_cmdline_parse_rekey_shift_levels(const gchar * option_name,const gchar * value,gpointer data,GError ** error)359 vp_cmdline_parse_rekey_shift_levels (const gchar *option_name, const gchar *value,
360 gpointer data, GError **error)
361 {
362 gpointer *args = (gpointer *) data;
363 ValuePairsTransformSet *vpts = (ValuePairsTransformSet *) args[2];
364 gchar *key = (gchar *) args[3];
365 gchar *end = NULL;
366 gint number_to_shift = strtol(value, &end, 0);
367
368 if (number_to_shift <= 0 || *end != 0)
369 {
370 g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_FAILED,
371 "Error parsing value-pairs, argument to --shift-levels is not numeric or not a positive number");
372 return FALSE;
373 }
374
375 vpts = vp_cmdline_rekey_verify (key, vpts, data);
376 if (!vpts)
377 {
378 g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_FAILED,
379 "Error parsing value-pairs: --shift-levels used without --key or --rekey");
380 return FALSE;
381 }
382
383 value_pairs_transform_set_add_func(vpts,
384 value_pairs_new_transform_shift_levels (number_to_shift));
385 return TRUE;
386 }
387
388 ValuePairs *
value_pairs_new_from_cmdline(GlobalConfig * cfg,gint * argc,gchar *** argv,gboolean ignore_unknown_options,GError ** error)389 value_pairs_new_from_cmdline (GlobalConfig *cfg,
390 gint *argc, gchar ***argv,
391 gboolean ignore_unknown_options,
392 GError **error)
393 {
394 ValuePairs *vp;
395 GOptionContext *ctx;
396
397 vp = value_pairs_new();
398
399 GOptionEntry vp_options[] =
400 {
401 {
402 "scope", 's', 0, G_OPTION_ARG_CALLBACK, vp_cmdline_parse_scope,
403 NULL, NULL
404 },
405 {
406 "exclude", 'x', 0, G_OPTION_ARG_CALLBACK, vp_cmdline_parse_exclude,
407 NULL, NULL
408 },
409 {
410 "key", 'k', 0, G_OPTION_ARG_CALLBACK, vp_cmdline_parse_key,
411 NULL, NULL
412 },
413 {
414 "rekey", 'r', 0, G_OPTION_ARG_CALLBACK, vp_cmdline_parse_rekey,
415 NULL, NULL
416 },
417 {
418 "pair", 'p', 0, G_OPTION_ARG_CALLBACK, vp_cmdline_parse_pair,
419 NULL, NULL
420 },
421 {
422 "shift", 'S', 0, G_OPTION_ARG_CALLBACK, vp_cmdline_parse_rekey_shift,
423 NULL, NULL
424 },
425 {
426 "shift-levels", 0, 0, G_OPTION_ARG_CALLBACK, vp_cmdline_parse_rekey_shift_levels,
427 NULL, NULL
428 },
429 {
430 "add-prefix", 'A', 0, G_OPTION_ARG_CALLBACK, vp_cmdline_parse_rekey_add_prefix,
431 NULL, NULL
432 },
433 {
434 "replace-prefix", 'R', 0, G_OPTION_ARG_CALLBACK, vp_cmdline_parse_rekey_replace_prefix,
435 NULL, NULL
436 },
437 {
438 "replace", 0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_CALLBACK,
439 vp_cmdline_parse_rekey_replace_prefix, NULL, NULL
440 },
441 {
442 "subkeys", 0, 0, G_OPTION_ARG_CALLBACK, vp_cmdline_parse_subkeys,
443 NULL, NULL
444 },
445 {
446 "omit-empty-values", 0, 0, G_OPTION_ARG_NONE, &vp->omit_empty_values,
447 NULL, NULL
448 },
449 {
450 G_OPTION_REMAINING, 0, 0, G_OPTION_ARG_CALLBACK, vp_cmdline_parse_pair_or_key,
451 NULL, NULL
452 },
453 { NULL }
454 };
455 GOptionGroup *og;
456 gpointer user_data_args[4];
457 gboolean success;
458
459 user_data_args[0] = cfg;
460 user_data_args[1] = vp;
461 user_data_args[2] = NULL;
462 user_data_args[3] = NULL;
463
464 ctx = g_option_context_new("value-pairs");
465 og = g_option_group_new(NULL, NULL, NULL, user_data_args, NULL);
466 g_option_group_add_entries(og, vp_options);
467 g_option_context_set_main_group(ctx, og);
468 g_option_context_set_ignore_unknown_options(ctx, ignore_unknown_options);
469
470 success = g_option_context_parse(ctx, argc, argv, error);
471 vp_cmdline_parse_rekey_finish(user_data_args);
472 g_option_context_free(ctx);
473
474 if (!success)
475 {
476 value_pairs_unref(vp);
477 vp = NULL;
478 }
479
480 return vp;
481 }
482