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