1 /*
2 Copyright 2021 Northern.tech AS
3
4 This file is part of CFEngine 3 - written and maintained by Northern.tech AS.
5
6 This program is free software; you can redistribute it and/or modify it
7 under the terms of the GNU General Public License as published by the
8 Free Software Foundation; version 3.
9
10 This program 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
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
18
19 To the extent this program is licensed as part of the Enterprise
20 versions of CFEngine, the applicable Commercial Open Source License
21 (COSL) may apply to this file if you as a licensee so wish it. See
22 included file COSL.txt.
23 */
24
25
26 #include <conversion.h>
27
28 #include <promises.h>
29 #include <files_names.h>
30 #include <dbm_api.h>
31 #include <mod_access.h>
32 #include <item_lib.h>
33 #include <logging.h>
34 #include <rlist.h>
35 #include <string_lib.h>
36
37
MapAddress(const char * unspec_address)38 const char *MapAddress(const char *unspec_address)
39 { /* Is the address a mapped ipv4 over ipv6 address */
40
41 if (strncmp(unspec_address, "::ffff:", 7) == 0)
42 {
43 return unspec_address + 7;
44 }
45 else
46 {
47 return unspec_address;
48 }
49 }
50
FindTypeInArray(const char * const haystack[],const char * needle,int default_value,int null_value)51 int FindTypeInArray(const char *const haystack[], const char *needle, int default_value, int null_value)
52 {
53 if (needle == NULL)
54 {
55 return null_value;
56 }
57
58 for (int i = 0; haystack[i] != NULL; ++i)
59 {
60 if (strcmp(needle, haystack[i]) == 0)
61 {
62 return i;
63 }
64 }
65
66 return default_value;
67 }
68
MeasurePolicyFromString(const char * s)69 MeasurePolicy MeasurePolicyFromString(const char *s)
70 {
71 static const char *const MEASURE_POLICY_TYPES[] =
72 { "average", "sum", "first", "last", NULL };
73
74 return FindTypeInArray(MEASURE_POLICY_TYPES, s, MEASURE_POLICY_AVERAGE, MEASURE_POLICY_NONE);
75 }
76
EnvironmentStateFromString(const char * s)77 EnvironmentState EnvironmentStateFromString(const char *s)
78 {
79 static const char *const ENV_STATE_TYPES[] =
80 { "create", "delete", "running", "suspended", "down", NULL };
81
82 return FindTypeInArray(ENV_STATE_TYPES, s, ENVIRONMENT_STATE_NONE, ENVIRONMENT_STATE_CREATE);
83 }
84
InsertMatchTypeFromString(const char * s)85 InsertMatchType InsertMatchTypeFromString(const char *s)
86 {
87 static const char *const INSERT_MATCH_TYPES[] =
88 { "ignore_leading", "ignore_trailing", "ignore_embedded",
89 "exact_match", NULL };
90
91 return FindTypeInArray(INSERT_MATCH_TYPES, s, INSERT_MATCH_TYPE_EXACT, INSERT_MATCH_TYPE_EXACT);
92 }
93
SyslogPriorityFromString(const char * s)94 int SyslogPriorityFromString(const char *s)
95 {
96 static const char *const SYSLOG_PRIORITY_TYPES[] =
97 { "emergency", "alert", "critical", "error", "warning", "notice",
98 "info", "debug", NULL };
99
100 return FindTypeInArray(SYSLOG_PRIORITY_TYPES, s, 3, 3);
101 }
102
ShellTypeFromString(const char * string)103 ShellType ShellTypeFromString(const char *string)
104 {
105 // For historical reasons, supports all CF_BOOL values (true/false/yes/no...),
106 // as well as "noshell,useshell,powershell".
107 char *start, *end;
108 char *options = "noshell,useshell,powershell," CF_BOOL;
109 int i;
110 int size;
111
112 if (string == NULL)
113 {
114 return SHELL_TYPE_NONE;
115 }
116
117 start = options;
118 size = strlen(string);
119 for (i = 0;; i++)
120 {
121 end = strchr(start, ',');
122 if (end == NULL)
123 {
124 break;
125 }
126 if (size == end - start && strncmp(string, start, end - start) == 0)
127 {
128 int cfBoolIndex;
129 switch (i)
130 {
131 case 0:
132 return SHELL_TYPE_NONE;
133 case 1:
134 return SHELL_TYPE_USE;
135 case 2:
136 return SHELL_TYPE_POWERSHELL;
137 default:
138 // Even cfBoolIndex is true, odd cfBoolIndex is false (from CF_BOOL).
139 cfBoolIndex = i-3;
140 return (cfBoolIndex & 1) ? SHELL_TYPE_NONE : SHELL_TYPE_USE;
141 }
142 }
143 start = end + 1;
144 }
145 return SHELL_TYPE_NONE;
146 }
147
DatabaseTypeFromString(const char * s)148 DatabaseType DatabaseTypeFromString(const char *s)
149 {
150 static const char *const DB_TYPES[] = { "mysql", "postgres", NULL };
151
152 return FindTypeInArray(DB_TYPES, s, DATABASE_TYPE_NONE, DATABASE_TYPE_NONE);
153 }
154
UserStateFromString(const char * s)155 UserState UserStateFromString(const char *s)
156 {
157 static const char *const U_TYPES[] =
158 { "present", "absent", "locked", NULL };
159
160 return FindTypeInArray(U_TYPES, s, USER_STATE_NONE, USER_STATE_NONE);
161 }
162
PasswordFormatFromString(const char * s)163 PasswordFormat PasswordFormatFromString(const char *s)
164 {
165 static const char *const U_TYPES[] = { "plaintext", "hash", NULL };
166
167 return FindTypeInArray(U_TYPES, s, PASSWORD_FORMAT_NONE, PASSWORD_FORMAT_NONE);
168 }
169
PackageActionFromString(const char * s)170 PackageAction PackageActionFromString(const char *s)
171 {
172 static const char *const PACKAGE_ACTION_TYPES[] =
173 { "add", "delete", "reinstall", "update", "addupdate", "patch",
174 "verify", NULL };
175
176 return FindTypeInArray(PACKAGE_ACTION_TYPES, s, PACKAGE_ACTION_NONE, PACKAGE_ACTION_NONE);
177 }
178
GetNewPackagePolicy(const char * s,const char ** action_types)179 NewPackageAction GetNewPackagePolicy(const char *s, const char **action_types)
180 {
181 return FindTypeInArray(action_types, s, NEW_PACKAGE_ACTION_NONE, NEW_PACKAGE_ACTION_NONE);
182 }
183
PackageVersionComparatorFromString(const char * s)184 PackageVersionComparator PackageVersionComparatorFromString(const char *s)
185 {
186 static const char *const PACKAGE_SELECT_TYPES[] =
187 { "==", "!=", ">", "<", ">=", "<=", NULL };
188
189 return FindTypeInArray(PACKAGE_SELECT_TYPES, s, PACKAGE_VERSION_COMPARATOR_NONE, PACKAGE_VERSION_COMPARATOR_NONE);
190 }
191
PackageActionPolicyFromString(const char * s)192 PackageActionPolicy PackageActionPolicyFromString(const char *s)
193 {
194 static const char *const ACTION_POLICY_TYPES[] =
195 { "individual", "bulk", NULL };
196
197 return FindTypeInArray(ACTION_POLICY_TYPES, s, PACKAGE_ACTION_POLICY_NONE, PACKAGE_ACTION_POLICY_NONE);
198 }
199
200 /***************************************************************************/
201
Rlist2String(Rlist * list,char * sep)202 char *Rlist2String(Rlist *list, char *sep)
203 {
204 Writer *writer = StringWriter();
205
206 for (const Rlist *rp = list; rp != NULL; rp = rp->next)
207 {
208 RvalWrite(writer, rp->val);
209
210 if (rp->next != NULL)
211 {
212 WriterWrite(writer, sep);
213 }
214 }
215
216 return StringWriterClose(writer);
217 }
218
219 /***************************************************************************/
220
SignalFromString(const char * s)221 int SignalFromString(const char *s)
222 {
223 int i = 0;
224 Item *ip, *names = SplitString(CF_SIGNALRANGE, ',');
225
226 for (ip = names; ip != NULL; ip = ip->next)
227 {
228 if (strcmp(s, ip->name) == 0)
229 {
230 break;
231 }
232 i++;
233 }
234
235 DeleteItemList(names);
236
237 switch (i)
238 {
239 case cfa_hup:
240 return SIGHUP;
241 case cfa_int:
242 return SIGINT;
243 case cfa_trap:
244 return SIGTRAP;
245 case cfa_kill:
246 return SIGKILL;
247 case cfa_pipe:
248 return SIGPIPE;
249 case cfa_cont:
250 return SIGCONT;
251 case cfa_abrt:
252 return SIGABRT;
253 case cfa_stop:
254 return SIGSTOP;
255 case cfa_quit:
256 return SIGQUIT;
257 case cfa_term:
258 return SIGTERM;
259 case cfa_child:
260 return SIGCHLD;
261 case cfa_usr1:
262 return SIGUSR1;
263 case cfa_usr2:
264 return SIGUSR2;
265 case cfa_bus:
266 return SIGBUS;
267 case cfa_segv:
268 return SIGSEGV;
269 default:
270 return -1;
271 }
272
273 }
274
ContextScopeFromString(const char * scope_str)275 ContextScope ContextScopeFromString(const char *scope_str)
276 {
277 static const char *const CONTEXT_SCOPES[] = { "namespace", "bundle" };
278 return FindTypeInArray(CONTEXT_SCOPES, scope_str, CONTEXT_SCOPE_NAMESPACE, CONTEXT_SCOPE_NONE);
279 }
280
FileLinkTypeFromString(const char * s)281 FileLinkType FileLinkTypeFromString(const char *s)
282 {
283 static const char *const LINK_TYPES[] =
284 { "symlink", "hardlink", "relative", "absolute", NULL };
285
286 return FindTypeInArray(LINK_TYPES, s, FILE_LINK_TYPE_SYMLINK, FILE_LINK_TYPE_SYMLINK);
287 }
288
FileComparatorFromString(const char * s)289 FileComparator FileComparatorFromString(const char *s)
290 {
291 static const char *const FILE_COMPARISON_TYPES[] =
292 { "atime", "mtime", "ctime", "digest", "hash", "binary", "exists", NULL };
293
294 return FindTypeInArray(FILE_COMPARISON_TYPES, s, FILE_COMPARATOR_NONE, FILE_COMPARATOR_NONE);
295 }
296
297 static const char *const datatype_strings[] =
298 {
299 [CF_DATA_TYPE_STRING] = "string",
300 [CF_DATA_TYPE_INT] = "int",
301 [CF_DATA_TYPE_REAL] = "real",
302 [CF_DATA_TYPE_STRING_LIST] = "slist",
303 [CF_DATA_TYPE_INT_LIST] = "ilist",
304 [CF_DATA_TYPE_REAL_LIST] = "rlist",
305 [CF_DATA_TYPE_OPTION] = "option",
306 [CF_DATA_TYPE_OPTION_LIST] = "olist",
307 [CF_DATA_TYPE_BODY] = "body",
308 [CF_DATA_TYPE_BUNDLE] = "bundle",
309 [CF_DATA_TYPE_CONTEXT] = "context",
310 [CF_DATA_TYPE_CONTEXT_LIST] = "clist",
311 [CF_DATA_TYPE_INT_RANGE] = "irange",
312 [CF_DATA_TYPE_REAL_RANGE] = "rrange",
313 [CF_DATA_TYPE_COUNTER] = "counter",
314 [CF_DATA_TYPE_CONTAINER] = "data",
315 [CF_DATA_TYPE_NONE] = "none"
316 };
317
DataTypeFromString(const char * name)318 DataType DataTypeFromString(const char *name)
319 {
320 for (int i = 0; i < CF_DATA_TYPE_NONE; i++)
321 {
322 if (strcmp(datatype_strings[i], name) == 0)
323 {
324 return i;
325 }
326 }
327
328 return CF_DATA_TYPE_NONE;
329 }
330
DataTypeToString(DataType type)331 const char *DataTypeToString(DataType type)
332 {
333 assert(type < CF_DATA_TYPE_NONE);
334 return datatype_strings[type];
335 }
336
ConstraintSyntaxGetDataType(const ConstraintSyntax * body_syntax,const char * lval)337 DataType ConstraintSyntaxGetDataType(const ConstraintSyntax *body_syntax, const char *lval)
338 {
339 int i = 0;
340
341 for (i = 0; body_syntax[i].lval != NULL; i++)
342 {
343 if (lval && (strcmp(body_syntax[i].lval, lval) == 0))
344 {
345 return body_syntax[i].dtype;
346 }
347 }
348
349 return CF_DATA_TYPE_NONE;
350 }
351
352 /****************************************************************************/
353
BooleanFromString(const char * s)354 bool BooleanFromString(const char *s)
355 {
356 Item *list = SplitString(CF_BOOL, ','), *ip;
357 int count = 0;
358
359 for (ip = list; ip != NULL; ip = ip->next)
360 {
361 if (strcmp(s, ip->name) == 0)
362 {
363 break;
364 }
365
366 count++;
367 }
368
369 DeleteItemList(list);
370
371 if (count % 2)
372 {
373 return false;
374 }
375 else
376 {
377 return true;
378 }
379 }
380
381 /****************************************************************************/
382
383 /**
384 * @NOTE parameter #s might be NULL. It's already used by design like that,
385 * for example parsing inexistent attributes in GetVolumeConstraints().
386 *
387 * @TODO see DoubleFromString(): return bool, have a value_out parameter, and
388 * kill CF_NOINT (goes deep!)
389 */
IntFromString(const char * s)390 long IntFromString(const char *s)
391 {
392 long long ll = CF_NOINT;
393 char quantifier, remainder;
394
395 if (s == NULL)
396 {
397 return CF_NOINT;
398 }
399 if (strcmp(s, "inf") == 0)
400 {
401 return (long) CF_INFINITY;
402 }
403 if (strcmp(s, "now") == 0)
404 {
405 return (long) CFSTARTTIME;
406 }
407
408 int ret = sscanf(s, "%lld%c %c", &ll, &quantifier, &remainder);
409
410 if (ret < 1 || ll == CF_NOINT)
411 {
412 if (strchr(s, '$') != NULL) /* don't log error, might converge */
413 {
414 Log(LOG_LEVEL_VERBOSE,
415 "Ignoring failed to parse integer '%s'"
416 " because of possibly unexpanded variable", s);
417 }
418 else
419 {
420 Log(LOG_LEVEL_ERR, "Failed to parse integer number: %s", s);
421 }
422 }
423 else if (ret == 3)
424 {
425 ll = CF_NOINT;
426 if (quantifier == '$') /* don't log error, might converge */
427 {
428 Log(LOG_LEVEL_VERBOSE,
429 "Ignoring failed to parse integer '%s'"
430 " because of possibly unexpanded variable", s);
431 }
432 else
433 {
434 Log(LOG_LEVEL_ERR,
435 "Anomalous ending '%c%c' while parsing integer number: %s",
436 quantifier, remainder, s);
437 }
438 }
439 else if (ret == 1) /* no quantifier */
440 {
441 /* nop */
442 }
443 else
444 {
445 assert(ret == 2);
446
447 switch (quantifier)
448 {
449 case 'k':
450 ll *= 1000;
451 break;
452 case 'K':
453 ll *= 1024;
454 break;
455 case 'm':
456 ll *= 1000 * 1000;
457 break;
458 case 'M':
459 ll *= 1024 * 1024;
460 break;
461 case 'g':
462 ll *= 1000 * 1000 * 1000;
463 break;
464 case 'G':
465 ll *= 1024 * 1024 * 1024;
466 break;
467 case '%':
468 if ((ll < 0) || (ll > 100))
469 {
470 Log(LOG_LEVEL_ERR, "Percentage out of range: %lld", ll);
471 return CF_NOINT;
472 }
473 else
474 {
475 /* Represent percentages internally as negative numbers */
476 /* TODO fix? */
477 ll *= -1;
478 }
479 break;
480
481 case ' ':
482 break;
483
484 default:
485 Log(LOG_LEVEL_VERBOSE,
486 "Ignoring bad quantifier '%c' in integer: %s",
487 quantifier, s);
488 break;
489 }
490 }
491
492 /* TODO Use strtol() instead of scanf(), it properly checks for overflow
493 * but it is prone to coding errors, so even better bring OpenBSD's
494 * strtonum() for proper conversions. */
495
496 if (ll < LONG_MIN)
497 {
498 Log(LOG_LEVEL_VERBOSE,
499 "Number '%s' underflows a long int, truncating to %ld",
500 s, LONG_MIN);
501 return LONG_MIN;
502 }
503 else if (ll > LONG_MAX)
504 {
505 Log(LOG_LEVEL_VERBOSE,
506 "Number '%s' overflows a long int, truncating to %ld",
507 s, LONG_MAX);
508 return LONG_MAX;
509 }
510
511 return (long) ll;
512 }
513
IntervalFromString(const char * string)514 Interval IntervalFromString(const char *string)
515 {
516 static const char *const INTERVAL_TYPES[] = { "hourly", "daily", NULL };
517
518 return FindTypeInArray(INTERVAL_TYPES, string, INTERVAL_NONE, INTERVAL_NONE);
519 }
520
DoubleFromString(const char * s,double * value_out)521 bool DoubleFromString(const char *s, double *value_out)
522 {
523 double d;
524 char quantifier, remainder;
525
526 assert(s != NULL);
527 assert(value_out != NULL);
528
529 int ret = sscanf(s, "%lf%c %c", &d, &quantifier, &remainder);
530
531 if (ret < 1)
532 {
533 Log(LOG_LEVEL_ERR, "Failed to parse real number: %s", s);
534 return false;
535 }
536 else if (ret == 3) /* non-space remainder */
537 {
538 Log(LOG_LEVEL_ERR,
539 "Anomalous ending '%c%c' while parsing real number: %s",
540 quantifier, remainder, s);
541 return false;
542 }
543 else if (ret == 1) /* no quantifier char */
544 {
545 /* nop */
546 }
547 else /* got quantifier */
548 {
549 assert(ret == 2);
550
551 switch (quantifier)
552 {
553 case 'k':
554 d *= 1000;
555 break;
556 case 'K':
557 d *= 1024;
558 break;
559 case 'm':
560 d *= 1000 * 1000;
561 break;
562 case 'M':
563 d *= 1024 * 1024;
564 break;
565 case 'g':
566 d *= 1000 * 1000 * 1000;
567 break;
568 case 'G':
569 d *= 1024 * 1024 * 1024;
570 break;
571 case '%':
572 if ((d < 0) || (d > 100))
573 {
574 Log(LOG_LEVEL_ERR, "Percentage out of range: %.2lf", d);
575 return false;
576 }
577 else
578 {
579 /* Represent percentages internally as negative numbers */
580 /* TODO fix? */
581 d *= -1;
582 }
583 break;
584
585 case ' ':
586 break;
587
588 default:
589 Log(LOG_LEVEL_VERBOSE,
590 "Ignoring bad quantifier '%c' in real number: %s",
591 quantifier, s);
592 break;
593 }
594 }
595
596 assert(ret == 1 || ret == 2);
597
598 *value_out = d;
599 return true;
600 }
601
602 /****************************************************************************/
603
604 /**
605 * @return true if successful
606 */
IntegerRangeFromString(const char * intrange,long * min_out,long * max_out)607 bool IntegerRangeFromString(const char *intrange, long *min_out, long *max_out)
608 {
609 Item *split;
610 long lmax = CF_LOWINIT, lmin = CF_HIGHINIT;
611
612 /* Numeric types are registered by range separated by comma str "min,max" */
613
614 if (intrange == NULL)
615 {
616 *min_out = CF_NOINT;
617 *max_out = CF_NOINT;
618 return true;
619 }
620
621 split = SplitString(intrange, ',');
622
623 sscanf(split->name, "%ld", &lmin);
624
625 if (strcmp(split->next->name, "inf") == 0)
626 {
627 lmax = (long) CF_INFINITY;
628 }
629 else
630 {
631 sscanf(split->next->name, "%ld", &lmax);
632 }
633
634 DeleteItemList(split);
635
636 if ((lmin == CF_HIGHINIT) || (lmax == CF_LOWINIT))
637 {
638 return false;
639 }
640
641 *min_out = lmin;
642 *max_out = lmax;
643 return true;
644 }
645
AclMethodFromString(const char * string)646 AclMethod AclMethodFromString(const char *string)
647 {
648 static const char *const ACL_METHOD_TYPES[] =
649 { "append", "overwrite", NULL };
650
651 return FindTypeInArray(ACL_METHOD_TYPES, string, ACL_METHOD_NONE, ACL_METHOD_NONE);
652 }
653
AclTypeFromString(const char * string)654 AclType AclTypeFromString(const char *string)
655 {
656 static const char *const ACL_TYPES[]=
657 { "generic", "posix", "ntfs", NULL };
658
659 return FindTypeInArray(ACL_TYPES, string, ACL_TYPE_NONE, ACL_TYPE_NONE);
660 }
661
662 /* For the deprecated attribute acl_directory_inherit. */
AclInheritanceFromString(const char * string)663 AclDefault AclInheritanceFromString(const char *string)
664 {
665 static const char *const ACL_INHERIT_TYPES[5] =
666 { "nochange", "specify", "parent", "clear", NULL };
667
668 return FindTypeInArray(ACL_INHERIT_TYPES, string, ACL_DEFAULT_NONE, ACL_DEFAULT_NONE);
669 }
670
AclDefaultFromString(const char * string)671 AclDefault AclDefaultFromString(const char *string)
672 {
673 static const char *const ACL_DEFAULT_TYPES[5] =
674 { "nochange", "specify", "access", "clear", NULL };
675
676 return FindTypeInArray(ACL_DEFAULT_TYPES, string, ACL_DEFAULT_NONE, ACL_DEFAULT_NONE);
677 }
678
AclInheritFromString(const char * string)679 AclInherit AclInheritFromString(const char *string)
680 {
681 char *start, *end;
682 char *options = CF_BOOL ",nochange";
683 int i;
684 int size;
685
686 if (string == NULL)
687 {
688 return ACL_INHERIT_NOCHANGE;
689 }
690
691 start = options;
692 size = strlen(string);
693 for (i = 0;; i++)
694 {
695 end = strchr(start, ',');
696 if (end == NULL)
697 {
698 break;
699 }
700 if (size == end - start && strncmp(string, start, end - start) == 0)
701 {
702 // Even i is true, odd i is false (from CF_BOOL).
703 return (i & 1) ? ACL_INHERIT_FALSE : ACL_INHERIT_TRUE;
704 }
705 start = end + 1;
706 }
707 return ACL_INHERIT_NOCHANGE;
708 }
709
DataTypeShortToType(char * short_type)710 const char *DataTypeShortToType(char *short_type)
711 {
712 assert(short_type);
713
714 if(strcmp(short_type, "s") == 0)
715 {
716 return "string";
717 }
718
719 if(strcmp(short_type, "i") == 0)
720 {
721 return "int";
722 }
723
724 if(strcmp(short_type, "r") == 0)
725 {
726 return "real";
727 }
728
729 if(strcmp(short_type, "m") == 0)
730 {
731 return "menu";
732 }
733
734 if(strcmp(short_type, "sl") == 0)
735 {
736 return "string list";
737 }
738
739 if(strcmp(short_type, "il") == 0)
740 {
741 return "int list";
742 }
743
744 if(strcmp(short_type, "rl") == 0)
745 {
746 return "real list";
747 }
748
749 if(strcmp(short_type, "ml") == 0)
750 {
751 return "menu list";
752 }
753
754 return "unknown type";
755 }
756
DataTypeIsIterable(DataType t)757 bool DataTypeIsIterable(DataType t)
758 {
759 if (t == CF_DATA_TYPE_STRING_LIST ||
760 t == CF_DATA_TYPE_INT_LIST ||
761 t == CF_DATA_TYPE_REAL_LIST ||
762 t == CF_DATA_TYPE_CONTAINER)
763 {
764 return true;
765 }
766 else
767 {
768 return false;
769 }
770 }
771
CoarseLaterThan(const char * bigger,const char * smaller)772 bool CoarseLaterThan(const char *bigger, const char *smaller)
773 {
774 char month_small[CF_SMALLBUF];
775 char month_big[CF_SMALLBUF];
776 int m_small, day_small, year_small, m_big, year_big, day_big;
777
778 sscanf(smaller, "%d %s %d", &day_small, month_small, &year_small);
779 sscanf(bigger, "%d %s %d", &day_big, month_big, &year_big);
780
781 if (year_big < year_small)
782 {
783 return false;
784 }
785
786 m_small = Month2Int(month_small);
787 m_big = Month2Int(month_big);
788
789 if (m_big < m_small)
790 {
791 return false;
792 }
793
794 if (day_big < day_small && m_big == m_small && year_big == year_small)
795 {
796 return false;
797 }
798
799 return true;
800 }
801
Month2Int(const char * string)802 int Month2Int(const char *string)
803 {
804 int i;
805
806 if (string == NULL)
807 {
808 return -1;
809 }
810
811 for (i = 0; i < 12; i++)
812 {
813 if (strncmp(MONTH_TEXT[i], string, strlen(string)) == 0)
814 {
815 return i + 1;
816 break;
817 }
818 }
819
820 return -1;
821 }
822
823 /*********************************************************************/
824
TimeToDateStr(time_t t,char * outStr,int outStrSz)825 void TimeToDateStr(time_t t, char *outStr, int outStrSz)
826 /**
827 * Formats a time as "30 Sep 2010".
828 */
829 {
830 char month[CF_SMALLBUF], day[CF_SMALLBUF], year[CF_SMALLBUF];
831 char tmp[CF_SMALLBUF];
832
833 snprintf(tmp, sizeof(tmp), "%s", ctime(&t));
834 sscanf(tmp, "%*s %5s %3s %*s %5s", month, day, year);
835 snprintf(outStr, outStrSz, "%s %s %s", day, month, year);
836 }
837
838 /*********************************************************************/
839
840 /**
841 * Copy first argument of #src to #dst. Argument is delimited either by double
842 * quotes if first character is double quotes, or by space.
843 *
844 * @note Thread-safe version of CommandArg0().
845 *
846 * @return The length of #dst, or (size_t) -1 in case of overflow.
847 */
CommandArg0_bound(char * dst,const char * src,size_t dst_size)848 size_t CommandArg0_bound(char *dst, const char *src, size_t dst_size)
849 {
850 const char *start;
851 char end_delimiter;
852
853 if(src[0] == '\"')
854 {
855 start = &src[1];
856 end_delimiter = '\"';
857 }
858 else
859 {
860 start = src;
861 end_delimiter = ' ';
862 }
863
864 char *end = strchrnul(start, end_delimiter);
865 size_t len = end - start;
866 if (len < dst_size)
867 {
868 memcpy(dst, start, len);
869 dst[len] = '\0';
870 return len;
871 }
872 else
873 {
874 /* Check return value of CommandArg0_bound! If -1, the user should
875 * never use dst, but just in case we are writing a bogus string. */
876 const char trap[] = "BUG: COMMANDARG0_TOO_LONG";
877 strlcpy(dst, trap, dst_size);
878 return (size_t) -1;
879 }
880 }
881
CommandArg0(const char * execstr)882 const char *CommandArg0(const char *execstr)
883 /**
884 * WARNING: Not thread-safe.
885 **/
886 {
887 static char arg[CF_BUFSIZE]; /* GLOBAL_R, no initialization needed */
888
889 const char *start;
890 char end_delimiter;
891
892 if(execstr[0] == '\"')
893 {
894 start = execstr + 1;
895 end_delimiter = '\"';
896 }
897 else
898 {
899 start = execstr;
900 end_delimiter = ' ';
901 }
902
903 strlcpy(arg, start, sizeof(arg));
904
905 char *cut = strchr(arg, end_delimiter);
906
907 if(cut)
908 {
909 *cut = '\0';
910 }
911
912 return arg;
913 }
914
915 /*************************************************************/
916
CommandPrefix(char * execstr,char * comm)917 void CommandPrefix(char *execstr, char *comm)
918 {
919 char *sp;
920
921 for (sp = execstr; (*sp != ' ') && (*sp != '\0'); sp++)
922 {
923 }
924
925 if (sp - 10 >= execstr)
926 {
927 sp -= 10; /* copy 15 most relevant characters of command */
928 }
929 else
930 {
931 sp = execstr;
932 }
933
934 memset(comm, 0, 20);
935 strncpy(comm, sp, 15);
936 }
937
938 /*******************************************************************/
939
IsRealNumber(const char * s)940 bool IsRealNumber(const char *s)
941 {
942 double d;
943 int ret = sscanf(s, "%lf", &d);
944
945 if (ret != 1)
946 {
947 return false;
948 }
949
950 return true;
951 }
952
953 #ifndef __MINGW32__
954
955 /*******************************************************************/
956 /* Unix-only functions */
957 /*******************************************************************/
958
959 /****************************************************************************/
960 /* Rlist to Uid/Gid lists */
961 /****************************************************************************/
962
UidListDestroy(UidList * uids)963 void UidListDestroy(UidList *uids)
964 {
965
966 while (uids)
967 {
968 UidList *ulp = uids;
969 uids = uids->next;
970 free(ulp->uidname);
971 free(ulp);
972 }
973 }
974
AddSimpleUidItem(UidList ** uidlist,uid_t uid,char * uidname)975 static void AddSimpleUidItem(UidList ** uidlist, uid_t uid, char *uidname)
976 {
977 UidList *ulp = xcalloc(1, sizeof(UidList));
978
979 ulp->uid = uid;
980
981 if (uid == CF_UNKNOWN_OWNER) /* unknown user */
982 {
983 ulp->uidname = xstrdup(uidname);
984 }
985
986 if (*uidlist == NULL)
987 {
988 *uidlist = ulp;
989 }
990 else /* Hang new element off end of list: */
991 {
992 UidList *u = *uidlist;
993
994 while (u->next != NULL)
995 {
996 u = u->next;
997 }
998 u->next = ulp;
999 }
1000 }
1001
Rlist2UidList(Rlist * uidnames,const Promise * pp)1002 UidList *Rlist2UidList(Rlist *uidnames, const Promise *pp)
1003 {
1004 UidList *uidlist = NULL;
1005 Rlist *rp;
1006 char username[CF_MAXVARSIZE];
1007 uid_t uid;
1008
1009 for (rp = uidnames; rp != NULL; rp = rp->next)
1010 {
1011 username[0] = '\0';
1012 uid = Str2Uid(RlistScalarValue(rp), username, pp);
1013 AddSimpleUidItem(&uidlist, uid, username);
1014 }
1015
1016 if (uidlist == NULL)
1017 {
1018 AddSimpleUidItem(&uidlist, CF_SAME_OWNER, NULL);
1019 }
1020
1021 return uidlist;
1022 }
1023
1024 /*********************************************************************/
1025
GidListDestroy(GidList * gids)1026 void GidListDestroy(GidList *gids)
1027 {
1028 while (gids)
1029 {
1030 GidList *glp = gids;
1031 gids = gids->next;
1032 free(glp->gidname);
1033 free(glp);
1034 }
1035 }
1036
AddSimpleGidItem(GidList ** gidlist,gid_t gid,char * gidname)1037 static void AddSimpleGidItem(GidList ** gidlist, gid_t gid, char *gidname)
1038 {
1039 GidList *glp = xcalloc(1, sizeof(GidList));
1040
1041 glp->gid = gid;
1042
1043 if (gid == CF_UNKNOWN_GROUP) /* unknown group */
1044 {
1045 glp->gidname = xstrdup(gidname);
1046 }
1047
1048 if (*gidlist == NULL)
1049 {
1050 *gidlist = glp;
1051 }
1052 else /* Hang new element off end of list: */
1053 {
1054 GidList *g = *gidlist;
1055 while (g->next != NULL)
1056 {
1057 g = g->next;
1058 }
1059 g->next = glp;
1060 }
1061 }
1062
Rlist2GidList(Rlist * gidnames,const Promise * pp)1063 GidList *Rlist2GidList(Rlist *gidnames, const Promise *pp)
1064 {
1065 GidList *gidlist = NULL;
1066 Rlist *rp;
1067 char groupname[CF_MAXVARSIZE];
1068 gid_t gid;
1069
1070 for (rp = gidnames; rp != NULL; rp = rp->next)
1071 {
1072 groupname[0] = '\0';
1073 gid = Str2Gid(RlistScalarValue(rp), groupname, pp);
1074 AddSimpleGidItem(&gidlist, gid, groupname);
1075 }
1076
1077 if (gidlist == NULL)
1078 {
1079 AddSimpleGidItem(&gidlist, CF_SAME_GROUP, NULL);
1080 }
1081
1082 return gidlist;
1083 }
1084
1085 /*********************************************************************/
1086
Str2Uid(const char * uidbuff,char * usercopy,const Promise * pp)1087 uid_t Str2Uid(const char *uidbuff, char *usercopy, const Promise *pp)
1088 {
1089 Item *ip, *tmplist;
1090 struct passwd *pw;
1091 int offset, uid = -2, tmp = -2;
1092 char *machine, *user, *domain;
1093
1094 if (uidbuff[0] == '+') /* NIS group - have to do this in a roundabout */
1095 { /* way because calling getpwnam spoils getnetgrent */
1096 offset = 1;
1097 if (uidbuff[1] == '@')
1098 {
1099 offset++;
1100 }
1101
1102 setnetgrent(uidbuff + offset);
1103 tmplist = NULL;
1104
1105 while (getnetgrent(&machine, &user, &domain))
1106 {
1107 if (user != NULL)
1108 {
1109 AppendItem(&tmplist, user, NULL);
1110 }
1111 }
1112
1113 endnetgrent();
1114
1115 for (ip = tmplist; ip != NULL; ip = ip->next)
1116 {
1117 if ((pw = getpwnam(ip->name)) == NULL)
1118 {
1119 Log(LOG_LEVEL_INFO, "Unknown user in promise '%s'", ip->name);
1120
1121 if (pp != NULL)
1122 {
1123 PromiseRef(LOG_LEVEL_INFO, pp);
1124 }
1125
1126 uid = CF_UNKNOWN_OWNER; /* signal user not found */
1127 }
1128 else
1129 {
1130 uid = pw->pw_uid;
1131
1132 if (usercopy != NULL)
1133 {
1134 strcpy(usercopy, ip->name);
1135 }
1136 }
1137 }
1138
1139 DeleteItemList(tmplist);
1140 return uid;
1141 }
1142
1143 if (StringIsNumeric(uidbuff))
1144 {
1145 sscanf(uidbuff, "%d", &tmp);
1146 uid = (uid_t) tmp;
1147 }
1148 else
1149 {
1150 if (strcmp(uidbuff, "*") == 0)
1151 {
1152 uid = CF_SAME_OWNER; /* signals wildcard */
1153 }
1154 else if ((pw = getpwnam(uidbuff)) == NULL)
1155 {
1156 Log(LOG_LEVEL_INFO, "Unknown user '%s' in promise", uidbuff);
1157 uid = CF_UNKNOWN_OWNER; /* signal user not found */
1158
1159 if (usercopy != NULL)
1160 {
1161 strcpy(usercopy, uidbuff);
1162 }
1163 }
1164 else
1165 {
1166 uid = pw->pw_uid;
1167 }
1168 }
1169
1170 return uid;
1171 }
1172
1173 /*********************************************************************/
1174
Str2Gid(const char * gidbuff,char * groupcopy,const Promise * pp)1175 gid_t Str2Gid(const char *gidbuff, char *groupcopy, const Promise *pp)
1176 {
1177 struct group *gr;
1178 int gid = -2, tmp = -2;
1179
1180 if (StringIsNumeric(gidbuff))
1181 {
1182 sscanf(gidbuff, "%d", &tmp);
1183 gid = (gid_t) tmp;
1184 }
1185 else
1186 {
1187 if (strcmp(gidbuff, "*") == 0)
1188 {
1189 gid = CF_SAME_GROUP; /* signals wildcard */
1190 }
1191 else if ((gr = getgrnam(gidbuff)) == NULL)
1192 {
1193 Log(LOG_LEVEL_INFO, "Unknown group '%s' in promise", gidbuff);
1194
1195 if (pp)
1196 {
1197 PromiseRef(LOG_LEVEL_INFO, pp);
1198 }
1199
1200 gid = CF_UNKNOWN_GROUP;
1201 }
1202 else
1203 {
1204 gid = gr->gr_gid;
1205
1206 if (groupcopy != NULL)
1207 {
1208 strcpy(groupcopy, gidbuff);
1209 }
1210 }
1211 }
1212
1213 return gid;
1214 }
1215
1216 #else /* !__MINGW32__ */
1217
1218 /* Release everything NovaWin_Rlist2SidList() allocates: */
UidListDestroy(UidList * uids)1219 void UidListDestroy(UidList *uids)
1220 {
1221 while (uids)
1222 {
1223 UidList *ulp = uids;
1224 uids = uids->next;
1225 free(ulp);
1226 }
1227 }
1228
GidListDestroy(ARG_UNUSED GidList * gids)1229 void GidListDestroy(ARG_UNUSED GidList *gids)
1230 {
1231 assert(gids == NULL);
1232 }
1233
1234 #endif
1235