1 /*
2  * FCRON - periodic command scheduler
3  *
4  *  Copyright 2000-2016 Thibault Godouet <fcron@free.fr>
5  *
6  *  This program is free software; you can redistribute it and/or modify
7  *  it under the terms of the GNU General Public License as published by
8  *  the Free Software Foundation; either version 2 of the License, or
9  *  (at your option) any later version.
10  *
11  *  This program is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *  GNU General Public License for more details.
15  *
16  *  You should have received a copy of the GNU General Public License
17  *  along with this program; if not, write to the Free Software
18  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  *
20  *  The GNU General Public License can also be found in the file
21  *  `LICENSE' that comes with the fcron source distribution.
22  */
23 
24 
25 #include "fcrontab.h"
26 
27 #include "fileconf.h"
28 
29 char *get_string(char *ptr);
30 int get_line(char *str, size_t size, FILE * file);
31 void init_default_line(cl_t * cl, cf_t * cf);
32 char *get_time(char *ptr, time_t * time, int zero_allowed);
33 char *get_num(char *ptr, int *num, int max, short int decimal,
34               const char **names);
35 char *get_nice(char *ptr, int *nice);
36 char *get_bool(char *ptr, int *i);
37 char *read_field(char *ptr, bitstr_t * ary, int max, const char **names);
38 void read_freq(char *ptr, cf_t * cf);
39 void read_arys(char *ptr, cf_t * cf);
40 void read_period(char *ptr, cf_t * cf);
41 int read_shortcut(char *ptr, cf_t * cf);
42 void read_env(char *ptr, cf_t * cf);
43 char *read_opt(char *ptr, cl_t * cl);
44 char *check_username(char *ptr, cf_t * cf, cl_t * cl);
45 size_t option_strlen(char *value);
46 
47 char need_correction;
48 cl_t default_line;              /* default options for a line */
49 char *file_name;
50 int line;
51 
52 /* warning : all names must have the same length */
53 const char *dows_ary[] = {
54     "sun", "mon", "tue", "wed", "thu", "fri", "sat",
55     NULL
56 };
57 
58 /* warning : all names must have the same length */
59 const char *mons_ary[] = {
60     "jan", "feb", "mar", "apr", "may", "jun",
61     "jul", "aug", "sep", "oct", "nov", "dec",
62     NULL
63 };
64 
65 
66 #define GET_LINE_EOF 999
67 
68 char *
get_string(char * ptr)69 get_string(char *ptr)
70     /* read string pointed by ptr, remove blanks and manage
71      * string placed in quotes */
72     /* return NULL on mismatched quotes */
73 {
74     char quote = 0;
75     int length = 0;
76     char *rtn_string = NULL;
77 
78     if (*ptr == '\"' || *ptr == '\'') {
79         quote = *ptr;
80         ptr++;
81     }
82 
83     length = remove_blanks(ptr);
84 
85     if (quote != 0) {
86         if (*(ptr + length - 1) == quote)
87             *(ptr + length - 1) = '\0';
88         else {
89             /* mismatched quotes */
90             need_correction = 1;
91             return NULL;
92         }
93     }
94 
95     Set(rtn_string, ptr);
96     return rtn_string;
97 
98 }
99 
100 
101 int
get_line(char * str,size_t size,FILE * file)102 get_line(char *str, size_t size, FILE * file)
103     /* similar to fgets, but increase line if necessary,
104      * and continue over an "\" followed by an "\n" char */
105 {
106     size_t size_max = size - 1;
107     int i = 0;
108     int c;
109 
110     while (i < size_max) {
111 
112         switch (c = getc(file)) {
113 
114         case '\n':
115             /* check if the \n char is preceded by a "\" char :
116              *  in this case, suppress the "\", don't copy the \n,
117              *  and continue */
118             if (i > 0 && *(str + i - 1) == '\\') {
119                 i--;
120                 line++;
121                 continue;
122             }
123             else {
124                 *(str + i) = (char)'\0';
125                 return OK;
126             }
127             break;
128 
129         case EOF:
130             *(str + i) = (char)'\0';
131             /* we couldn't return EOF ( equal to ERR by default )
132              * nor ERR, which is used for another error */
133             return GET_LINE_EOF;
134 
135         default:
136             *(str + i) = (char)c;
137             i++;
138 
139         }
140 
141     }
142 
143     /* line is too long : goto next line and return ERR */
144     while (((c = getc(file)) != EOF) && (c != '\n')) ;
145     line++;
146     need_correction = 1;
147     return ERR;
148 
149 }
150 
151 void
init_default_line(cl_t * cl,cf_t * cf)152 init_default_line(cl_t * cl, cf_t * cf)
153 /* clear all context/options from cl */
154 {
155     bzero(cl, sizeof(cl_t));
156     Set(cl->cl_runas, runas);
157     Set(cl->cl_mailto, runas);
158     Free_safe(cl->cl_tz);
159     set_default_opt(cl->cl_option);
160     cl->cl_file = cf;
161 }
162 
163 
164 int
read_file(char * filename,int fd)165 read_file(char *filename, int fd)
166     /* read file "name" and append cf_t list */
167 {
168     cf_t *cf = NULL;
169     FILE *file = NULL;
170     char buf[LINE_LEN];
171     int max_lines;
172     int max_entries = MAXENTRIES;
173     int entries = 0;
174     char *ptr = NULL;
175     int ret;
176 
177     bzero(buf, sizeof(buf));
178     need_correction = 0;
179     line = 1;
180     file_name = filename;
181 
182     /* open file */
183 
184     if ((file = fdopen(fd, "r")) == NULL) {
185         fprintf(stderr, "Could not open \"%s\": %s\n", file_name,
186                 strerror(errno));
187         return ERR;
188     }
189 
190     /* Rewind, just in case */
191     rewind(file);
192 
193     Alloc(cf, cf_t);
194     cf->cf_env_list = env_list_init();
195     Set(cf->cf_user, user);
196     init_default_line(&default_line, cf);
197 
198     if (debug_opt)
199         fprintf(stderr, "FILE %s\n", file_name);
200 
201     if (strcmp(runas, ROOTNAME) == 0)
202         max_entries = 65535;
203 
204     /* max_lines acts here as a security counter to avoid endless loop. */
205     max_lines = (max_entries * 10) + 10;
206 
207     while (entries < max_entries && line <= max_lines) {
208 
209         ret = get_line(buf, sizeof(buf), file);
210 
211         if (ret == ERR) {
212             fprintf(stderr,
213                     "%s:%d: Line is too long (more than %d): skipping line.\n",
214                     file_name, line, (int)sizeof(buf));
215             continue;
216         }
217 
218         ptr = buf;
219         Skip_blanks(ptr);
220 
221         if (debug_opt && *ptr != '#' && *ptr != '\0')
222             fprintf(stderr, "      %s\n", buf);
223 
224         switch (*ptr) {
225         case '#':
226         case '\0':
227             /* comments or empty line: skipping */
228             break;
229         case '@':
230             /* if it is not a shortcut line then read_shortcut() won't do anything. */
231             if (!read_shortcut(ptr, cf))
232                 read_freq(ptr, cf);
233             entries++;
234             break;
235         case '&':
236             read_arys(ptr, cf);
237             entries++;
238             break;
239         case '%':
240             read_period(ptr, cf);
241             entries++;
242             break;
243         case '!':
244             ptr = read_opt(ptr, &default_line);
245             if (ptr != NULL && *ptr != '\0') {
246                 fprintf(stderr, "%s:%d: Syntax error: string \"%s\" ignored\n",
247                         file_name, line, ptr);
248                 need_correction = 1;
249             }
250             break;
251         default:
252             if (isdigit((int)*ptr) || *ptr == '*') {
253                 read_arys(ptr, cf);
254                 entries++;
255             }
256             else
257                 read_env(ptr, cf);
258         }
259 
260         line++;
261 
262         if (ret != OK)
263             /* in this case, ret == GET_LINE_EOF :
264              * no more lines, so we exit the loop */
265             break;
266 
267     }
268 
269     if (entries == max_entries) {
270         error("%s:%d: maximum number of entries (%d) has been reached by %s",
271               file_name, line, user);
272         fprintf(stderr, "Anything after this line will be ignored\n");
273     }
274     else if (line == max_lines)
275         error("%s:%d: maximum number of lines (%d) has been reached by %s",
276               file_name, line, user);
277 
278     cf->cf_next = file_base;
279     file_base = cf;
280 
281     Free_safe(default_line.cl_runas);
282     Free_safe(default_line.cl_mailfrom);
283     Free_safe(default_line.cl_mailto);
284     Free_safe(default_line.cl_tz);
285 
286     if (!need_correction)
287         return OK;
288     else
289         return 2;
290 
291 }
292 
293 void
read_env(char * ptr,cf_t * cf)294 read_env(char *ptr, cf_t * cf)
295     /* append env variable list.
296      * (remove blanks) */
297 {
298     char name[LINE_LEN];
299     int j = 0;
300     char *val = NULL;
301 
302     bzero(name, sizeof(name));
303 
304     /* copy env variable's name */
305     while ((isalnum((int)*ptr) || *ptr == '_') && *ptr != '='
306            && !isspace((int)*ptr) && j < sizeof(name)) {
307         name[j++] = *ptr;
308         ptr++;
309     }
310     name[j] = '\0';
311 
312     if (name[0] == '\0')
313         goto error;
314 
315     /* skip '=' and spaces around */
316     while (isspace((int)*ptr))
317         ptr++;
318 
319     /* if j == 0 name is a zero length string */
320     if (*ptr++ != '=' || j == 0)
321         goto error;
322 
323     while (isspace((int)*ptr))
324         ptr++;
325 
326     /* get value */
327     if ((val = get_string(ptr)) == NULL) {
328         fprintf(stderr, "%s:%d: Mismatched  quotes: skipping line.\n",
329                 file_name, line);
330         need_correction = 1;
331         return;
332     }
333 
334     if (debug_opt)
335         fprintf(stderr, "  Env : '%s=%s'\n", name, val);
336 
337     /* we ignore USER/LOGNAME's assignment */
338     if (strcmp(name, "USER") == 0 || strcmp(name, "LOGNAME") == 0) {
339         fprintf(stderr,
340                 "%s:%d: USER or LOGNAME assignement is not allowed: ignored.\n",
341                 file_name, line);
342         return;
343     }
344 
345     /* the MAILFROM/MAILTO assignment is, in fact, an fcron option :
346      *  we don't store it in the same way. */
347     /* please note that we check if the mailto is valid in conf.c */
348     if (strcmp(name, "MAILFROM") == 0) {
349         Set(default_line.cl_mailfrom, val);
350     }
351     else if (strcmp(name, "MAILTO") == 0) {
352         /* cl_mailto must not be NULL (as expected in
353          * conf.c:add_line_to_file()), so we check if the length is >= 0 */
354         if (strcmp(val, "\0") == 0) {
355             clear_mail(default_line.cl_option);
356             clear_mailzerolength(default_line.cl_option);
357         }
358         else {
359             Set(default_line.cl_mailto, val);
360             set_mail(default_line.cl_option);
361         }
362 
363     }
364     else {
365         env_list_setenv(cf->cf_env_list, name, val, 1);
366     }
367 
368     Free_safe(val);
369 
370     return;
371 
372  error:
373     fprintf(stderr, "%s:%d: Syntax error: skipping line.\n", file_name, line);
374     need_correction = 1;
375     return;
376 
377 }
378 
379 
380 char *
get_nice(char * ptr,int * nice)381 get_nice(char *ptr, int *nice)
382     /* read a nice value and put it in variable nice */
383 {
384     char negative = 0;
385 
386     if (*ptr == '-') {
387         negative = 1;
388         ptr++;
389     }
390 
391     if ((ptr = get_num(ptr, nice, 20, 0, NULL)) == NULL)
392         return NULL;
393 
394     if (negative == 1) {
395         if (getuid() != rootuid) {
396             fprintf(stderr, "must be privileged to use a negative argument "
397                     "with nice: set to 0\n");
398             need_correction = 1;
399             *nice = 0;
400         }
401 
402         *nice *= (-1);
403     }
404 
405     return ptr;
406 
407 }
408 
409 size_t
option_strlen(char * value)410 option_strlen(char *value)
411 /* return the length of the string value of an option */
412 {
413     char *ptr = value;
414     size_t len = 0;
415 
416     /* look for the end of the option value */
417     while (ptr != NULL && *ptr != ')' && *ptr != '\0') {
418         ptr++;
419         len++;
420     }
421 
422     return len;
423 }
424 
425 int
assign_option_string(char ** var,char * value)426 assign_option_string(char **var, char *value)
427 /* Read the value of an option: if non-empty, assign it to the var,
428  * otherwise set to NULL.
429  * Returns the length of the value (0 if empty), or -1 on error. */
430 {
431     char start = value[0];
432     char end = '\0';
433     size_t len = 0;
434 
435     Free_safe(*var);
436 
437     len = option_strlen(value);
438 
439     if (len <= 0) {
440         return len;
441     }
442 
443     end = value[len - 1];
444 
445     /* spaces and quotes are not allowed before or after the value */
446     if (isspace((int)start) || isspace((int)end)
447         || start == '\'' || start == '"' || end == '\'' || end == '"') {
448         return -1;
449     }
450 
451     *var = strndup2(value, len);
452 
453     return len;
454 }
455 
456 
457 char *
get_bool(char * ptr,int * i)458 get_bool(char *ptr, int *i)
459     /* get a bool value : either true (1) or false (0)
460      * return NULL on error */
461 {
462     if (*ptr == '1')
463         goto true;
464     else if (*ptr == '0')
465         goto false;
466     else if (strncmp(ptr, "true", 4) == 0) {
467         ptr += 3;
468         goto true;
469     }
470     else if (strncmp(ptr, "yes", 3) == 0) {
471         ptr += 2;
472         goto true;
473     }
474     else if (strncmp(ptr, "false", 5) == 0) {
475         ptr += 4;
476         goto false;
477     }
478     else if (strncmp(ptr, "no", 2) == 0) {
479         ptr += 1;
480         goto false;
481     }
482     else
483         return NULL;
484 
485  true:
486     *i = 1;
487     ptr++;
488     return ptr;
489 
490  false:
491     *i = 0;
492     ptr++;
493     return ptr;
494 
495 }
496 
497 
498 char *
read_opt(char * ptr,cl_t * cl)499 read_opt(char *ptr, cl_t * cl)
500     /* read one or several options and fill in the field "option" */
501 {
502     char opt_name[20];
503     int i;
504     char in_brackets;
505 
506 #define Handle_err \
507     { \
508         fprintf(stderr, "%s:%d: Argument(s) for option \"%s\" not valid: " \
509 		"skipping end of line.\n", file_name, line, opt_name); \
510         need_correction = 1; \
511         return NULL; \
512     }
513 
514     if (*ptr == '!')
515         ptr++;
516 
517     do {
518         i = 0;
519         bzero(opt_name, sizeof(opt_name));
520 
521         while (isalnum((int)*ptr) && i < sizeof(opt_name))
522             opt_name[i++] = *ptr++;
523 
524         i = 1;
525         in_brackets = 0;
526 
527         if (*ptr == '(') {
528             in_brackets = 1;
529             ptr++;
530 
531             /* spaces are not allowed -- make sure there is no leading space. */
532             if (isspace((int)*ptr)) {
533                 Handle_err;
534             }
535         }
536 
537         /* global options for a file */
538 
539         if (strcmp(opt_name, "tzdiff") == 0) {
540             char negative = 0;
541 
542             if (!in_brackets)
543                 Handle_err;
544             if (*ptr == '-') {
545                 negative = 1;
546                 ptr++;
547             }
548             if ((ptr = get_num(ptr, &i, 24, 0, NULL)) == NULL)
549                 Handle_err;
550             if (negative)
551                 cl->cl_file->cf_tzdiff = (-i);
552             else
553                 cl->cl_file->cf_tzdiff = i;
554 
555             if (debug_opt)
556                 fprintf(stderr, "  Opt : \"%s\" (-)%d\n", opt_name, i);
557         }
558 
559         /* options related to a line (or a set of lines) */
560 
561         else if (strcmp(opt_name, "timezone") == 0) {
562             int len = -1;
563 
564             if (!in_brackets) {
565                 Handle_err;
566             }
567 
568             /* assign_option_string() will set cl_tz to NULL is the value is empty,
569              * which means "use the system timezone" */
570             len = assign_option_string(&(cl->cl_tz), ptr);
571             if (len < 0) {
572                 Handle_err;
573             }
574             else {
575                 ptr += len;
576             }
577 
578             if (debug_opt) {
579                 fprintf(stderr, "  Opt : \"%s\" \"%s\"\n", opt_name, cl->cl_tz);
580             }
581         }
582 
583 
584         else if (strcmp(opt_name, "s") == 0 || strcmp(opt_name, "serial") == 0) {
585             if (in_brackets && (ptr = get_bool(ptr, &i)) == NULL)
586                 Handle_err;
587             if (i == 0)
588                 clear_serial(cl->cl_option);
589             else
590                 set_serial(cl->cl_option);
591             if (debug_opt)
592                 fprintf(stderr, "  Opt : \"%s\" %d\n", opt_name, i);
593         }
594 
595         else if (strcmp(opt_name, "serialonce") == 0) {
596             if (in_brackets && (ptr = get_bool(ptr, &i)) == NULL)
597                 Handle_err;
598             if (i == 0)
599                 set_serial_sev(cl->cl_option);
600             else
601                 clear_serial_sev(cl->cl_option);
602             if (debug_opt)
603                 fprintf(stderr, "  Opt : \"%s\" %d\n", opt_name, i);
604         }
605 
606         else if (strcmp(opt_name, "lavgonce") == 0) {
607             if (in_brackets && (ptr = get_bool(ptr, &i)) == NULL)
608                 Handle_err;
609             if (i == 0)
610                 set_lavg_sev(cl->cl_option);
611             else
612                 clear_lavg_sev(cl->cl_option);
613             if (debug_opt)
614                 fprintf(stderr, "  Opt : \"%s\" %d\n", opt_name, i);
615         }
616 
617         else if (strcmp(opt_name, "exesev") == 0) {
618             if (in_brackets && (ptr = get_bool(ptr, &i)) == NULL)
619                 Handle_err;
620             if (i == 0)
621                 clear_exe_sev(cl->cl_option);
622             else
623                 set_exe_sev(cl->cl_option);
624             if (debug_opt)
625                 fprintf(stderr, "  Opt : \"%s\" %d\n", opt_name, i);
626         }
627 
628         else if (strcmp(opt_name, "b") == 0 || strcmp(opt_name, "bootrun") == 0) {
629             if (in_brackets && (ptr = get_bool(ptr, &i)) == NULL)
630                 Handle_err;
631             if (i == 0)
632                 clear_bootrun(cl->cl_option);
633             else
634                 set_bootrun(cl->cl_option);
635             if (debug_opt)
636                 fprintf(stderr, "  Opt : \"%s\" %d\n", opt_name, i);
637         }
638 
639         else if (strcmp(opt_name, "rebootreset") == 0) {
640             if (in_brackets && (ptr = get_bool(ptr, &i)) == NULL)
641                 Handle_err;
642             if (i == 0)
643                 clear_rebootreset(cl->cl_option);
644             else
645                 set_rebootreset(cl->cl_option);
646             if (debug_opt)
647                 fprintf(stderr, "  Opt : \"%s\" %d\n", opt_name, i);
648         }
649 
650 
651         else if (strcmp(opt_name, "runatreboot") == 0) {
652             if (in_brackets && (ptr = get_bool(ptr, &i)) == NULL)
653                 Handle_err;
654             if (i == 0)
655                 clear_runatreboot(cl->cl_option);
656             else
657                 set_runatreboot(cl->cl_option);
658             if (debug_opt)
659                 fprintf(stderr, "  Opt : \"%s\" %d\n", opt_name, i);
660         }
661 
662 
663         else if (strcmp(opt_name, "runonce") == 0) {
664             if (in_brackets && (ptr = get_bool(ptr, &i)) == NULL)
665                 Handle_err;
666             if (i == 0)
667                 clear_runonce(cl->cl_option);
668             else
669                 set_runonce(cl->cl_option);
670             if (debug_opt)
671                 fprintf(stderr, "  Opt : \"%s\" %d\n", opt_name, i);
672         }
673 
674         else if (strcmp(opt_name, "reset") == 0) {
675             if (in_brackets && (ptr = get_bool(ptr, &i)) == NULL)
676                 Handle_err;
677             if (i == 1) {
678                 init_default_line(cl, cl->cl_file);
679             }
680             if (debug_opt)
681                 fprintf(stderr, "  Opt : \"%s\"\n", opt_name);
682         }
683 
684         else if (strcmp(opt_name, "f") == 0 || strcmp(opt_name, "first") == 0) {
685             if (!in_brackets
686                 || (ptr = get_time(ptr, &(cl->cl_first), 1)) == NULL)
687                 Handle_err;
688             if (debug_opt)
689                 fprintf(stderr, "  Opt : \"%s\" %ld\n", opt_name,
690                         (long int)cl->cl_first);
691         }
692 
693         else if (strcmp(opt_name, "r") == 0 || strcmp(opt_name, "runfreq") == 0) {
694             if (cl->cl_runfreq == 1) {
695                 fprintf(stderr, "cannot change runfreq value in a %%-line");
696                 Handle_err;
697             }
698             if (!in_brackets
699                 || (ptr = get_num(ptr, &i, USHRT_MAX, 0, NULL)) == NULL)
700                 Handle_err;
701             if (i <= 1) {
702                 fprintf(stderr, "runfreq must be 2 or more.\n");
703                 Handle_err;
704             }
705             cl->cl_runfreq = i;
706             if (debug_opt)
707                 fprintf(stderr, "  Opt : \"%s\" %d\n", opt_name, i);
708         }
709 
710         else if (strcmp(opt_name, "strict") == 0) {
711             if (in_brackets && (ptr = get_bool(ptr, &i)) == NULL)
712                 Handle_err;
713             if (i == 0)
714                 clear_strict(cl->cl_option);
715             else
716                 set_strict(cl->cl_option);
717             if (debug_opt)
718                 fprintf(stderr, "  Opt : \"%s\" %d\n", opt_name, i);
719         }
720 
721         else if (strcmp(opt_name, "noticenotrun") == 0) {
722             if (in_brackets && (ptr = get_bool(ptr, &i)) == NULL)
723                 Handle_err;
724             if (i == 0)
725                 clear_notice_notrun(cl->cl_option);
726             else
727                 set_notice_notrun(cl->cl_option);
728             if (debug_opt)
729                 fprintf(stderr, "  Opt : \"%s\" %d\n", opt_name, i);
730         }
731 
732         else if (strcmp(opt_name, "lavg") == 0) {
733             if (!in_brackets
734                 || (ptr = get_num(ptr, &i, UCHAR_MAX, 1, NULL)) == NULL)
735                 Handle_err;
736             cl->cl_lavg[0] = i;
737             if (debug_opt)
738                 fprintf(stderr, "  Opt : 'lavg1' %d\n", i);
739             if (*ptr++ != ',')
740                 Handle_err;
741             if (!in_brackets
742                 || (ptr = get_num(ptr, &i, UCHAR_MAX, 1, NULL)) == NULL)
743                 Handle_err;
744             cl->cl_lavg[1] = i;
745             if (debug_opt)
746                 fprintf(stderr, "  Opt : 'lavg5' %d\n", i);
747             if (*ptr++ != ',')
748                 Handle_err;
749             if (!in_brackets
750                 || (ptr = get_num(ptr, &i, UCHAR_MAX, 1, NULL)) == NULL)
751                 Handle_err;
752             cl->cl_lavg[2] = i;
753             if (debug_opt)
754                 fprintf(stderr, "  Opt : 'lavg15' %d\n", i);
755             if (cl->cl_lavg[0] || cl->cl_lavg[1] || cl->cl_lavg[2])
756                 set_lavg(cl->cl_option);
757             else
758                 clear_lavg(cl->cl_option);
759 #ifdef NOLOADAVG
760             warn("As fcron has been compiled with no procfs support,\n"
761                  "you will not be able to use the lavg* options");
762 #endif                          /* NOLOADAVG */
763         }
764 
765         else if (strcmp(opt_name, "lavg1") == 0) {
766             if (!in_brackets
767                 || (ptr = get_num(ptr, &i, UCHAR_MAX, 1, NULL)) == NULL)
768                 Handle_err;
769             cl->cl_lavg[0] = i;
770             if (cl->cl_lavg[0] || cl->cl_lavg[1] || cl->cl_lavg[2])
771                 set_lavg(cl->cl_option);
772             else
773                 clear_lavg(cl->cl_option);
774 #if NOLOADAVG
775             warn("As fcron has been compiled with no procfs support,\n"
776                  "you will not be able to use the lavg* options");
777 #endif                          /* NOLOADAVG */
778             if (debug_opt)
779                 fprintf(stderr, "  Opt : 'lavg1' %d\n", i);
780         }
781 
782         else if (strcmp(opt_name, "lavg5") == 0) {
783             if (!in_brackets
784                 || (ptr = get_num(ptr, &i, UCHAR_MAX, 1, NULL)) == NULL)
785                 Handle_err;
786             cl->cl_lavg[1] = i;
787             if (cl->cl_lavg[0] || cl->cl_lavg[1] || cl->cl_lavg[2])
788                 set_lavg(cl->cl_option);
789             else
790                 clear_lavg(cl->cl_option);
791 #ifdef NOLOADAVG
792             warn("As fcron has been compiled with no procfs support,\n"
793                  "you will not be able to use the lavg* options");
794 #endif                          /* NOLOADAVG = 0 */
795             if (debug_opt)
796                 fprintf(stderr, "  Opt : 'lavg5' %d\n", i);
797         }
798 
799         else if (strcmp(opt_name, "lavg15") == 0) {
800             if (!in_brackets
801                 || (ptr = get_num(ptr, &i, UCHAR_MAX, 1, NULL)) == NULL)
802                 Handle_err;
803             cl->cl_lavg[2] = i;
804             if (cl->cl_lavg[0] || cl->cl_lavg[1] || cl->cl_lavg[2])
805                 set_lavg(cl->cl_option);
806             else
807                 clear_lavg(cl->cl_option);
808 #ifdef NOLOADAVG
809             warn("As fcron has been compiled with no procfs support,\n"
810                  "you will not be able to use the lavg* options");
811 #endif                          /* NOLOADAVG = 0 */
812             if (debug_opt)
813                 fprintf(stderr, "  Opt : 'lavg15' %d\n", i);
814         }
815 
816         else if (strcmp(opt_name, "lavgand") == 0) {
817             if (in_brackets && (ptr = get_bool(ptr, &i)) == NULL)
818                 Handle_err;
819             if (i == 0)
820                 set_lor(cl->cl_option);
821             else
822                 set_land(cl->cl_option);
823             if (debug_opt)
824                 fprintf(stderr, "  Opt : \"%s\" %d\n", opt_name, i);
825         }
826 
827         else if (strcmp(opt_name, "lavgor") == 0) {
828             if (in_brackets && (ptr = get_bool(ptr, &i)) == NULL)
829                 Handle_err;
830             if (i == 0)
831                 set_land(cl->cl_option);
832             else
833                 set_lor(cl->cl_option);
834             if (debug_opt)
835                 fprintf(stderr, "  Opt : \"%s\" %d\n", opt_name, i);
836         }
837 
838         else if (strcmp(opt_name, "u") == 0 || strcmp(opt_name, "until") == 0) {
839             if (!in_brackets
840                 || (ptr = get_time(ptr, &(cl->cl_until), 0)) == NULL)
841                 Handle_err;
842             if (debug_opt)
843                 fprintf(stderr, "  Opt : \"%s\" %ld\n", opt_name,
844                         (long int)cl->cl_until);
845         }
846 
847         else if (strcmp(opt_name, "m") == 0 || strcmp(opt_name, "mail") == 0) {
848             if (in_brackets && (ptr = get_bool(ptr, &i)) == NULL)
849                 Handle_err;
850             if (i == 0) {
851                 clear_mail(cl->cl_option);
852                 clear_mailzerolength(cl->cl_option);
853             }
854             else
855                 set_mail(cl->cl_option);
856             if (debug_opt)
857                 fprintf(stderr, "  Opt : \"%s\" %d\n", opt_name, i);
858         }
859 
860         else if (strcmp(opt_name, "forcemail") == 0) {
861             if (in_brackets && (ptr = get_bool(ptr, &i)) == NULL)
862                 Handle_err;
863             if (i == 0)
864                 clear_mailzerolength(cl->cl_option);
865             else {
866                 set_mailzerolength(cl->cl_option);
867                 set_mail(cl->cl_option);
868             }
869             if (debug_opt)
870                 fprintf(stderr, "  Opt : \"%s\" %d\n", opt_name, i);
871         }
872 
873         else if (strcmp(opt_name, "mailfrom") == 0) {
874             int len = -1;
875 
876             if (!in_brackets) {
877                 Handle_err;
878             }
879 
880             /* Also please note that we check if the mailfrom is valid in conf.c */
881             len = assign_option_string(&(cl->cl_mailfrom), ptr);
882             if (len < 0) {
883                 Handle_err;
884             }
885             else {
886                 ptr += len;
887             }
888 
889             if (debug_opt)
890                 fprintf(stderr, "  Opt : \"%s\" \"%s\"\n", opt_name,
891                         cl->cl_mailfrom);
892         }
893 
894         else if (strcmp(opt_name, "mailto") == 0) {
895             int len = -1;
896 
897             if (!in_brackets) {
898                 Handle_err;
899             }
900 
901             /* assign_option_string() set the value to NULL if the length is zero.
902              * However cl_mailto must not be NULL (as expected in
903              * conf.c:add_line_to_file()), so we check if the length is >= 0
904              * before calling assign_option_string() */
905             /* Also please note that we check if the mailto is valid in conf.c */
906             len = option_strlen(ptr);
907             if (len <= 0) {
908                 clear_mail(cl->cl_option);
909                 clear_mailzerolength(cl->cl_option);
910                 if (debug_opt) {
911                     fprintf(stderr, "  Opt : \"mail\" 0\n");
912                     fprintf(stderr, "  Opt : \"forcemail\" 0\n");
913                 }
914             }
915             else {
916                 len = assign_option_string(&(cl->cl_mailto), ptr);
917                 if (len < 0) {
918                     Handle_err;
919                 }
920                 else {
921                     ptr += len;
922                     set_mail(cl->cl_option);
923                 }
924             }
925 
926             if (debug_opt)
927                 fprintf(stderr, "  Opt : \"%s\" \"%s\"\n", opt_name,
928                         cl->cl_mailto);
929         }
930 
931         else if (strcmp(opt_name, "erroronlymail") == 0) {
932             if (in_brackets && (ptr = get_bool(ptr, &i)) == NULL)
933                 Handle_err;
934             if (i == 0)
935                 clear_erroronlymail(cl->cl_option);
936             else
937                 set_erroronlymail(cl->cl_option);
938             if (debug_opt)
939                 fprintf(stderr, "  Opt : \"%s\" %d\n", opt_name, i);
940         }
941 
942         else if (strcmp(opt_name, "dayand") == 0) {
943             if (in_brackets && (ptr = get_bool(ptr, &i)) == NULL)
944                 Handle_err;
945             if (i == 0)
946                 set_dayor(cl->cl_option);
947             else
948                 set_dayand(cl->cl_option);
949             if (debug_opt)
950                 fprintf(stderr, "  Opt : \"%s\" %d\n", opt_name, i);
951         }
952 
953         else if (strcmp(opt_name, "dayor") == 0) {
954             if (in_brackets && (ptr = get_bool(ptr, &i)) == NULL)
955                 Handle_err;
956             if (i == 0)
957                 set_dayand(cl->cl_option);
958             else
959                 set_dayor(cl->cl_option);
960             if (debug_opt)
961                 fprintf(stderr, "  Opt : \"%s\" %d\n", opt_name, i);
962         }
963 
964         else if (strcmp(opt_name, "nolog") == 0) {
965             if (in_brackets && (ptr = get_bool(ptr, &i)) == NULL)
966                 Handle_err;
967             if (i == 0)
968                 clear_nolog(cl->cl_option);
969             else
970                 set_nolog(cl->cl_option);
971             if (debug_opt)
972                 fprintf(stderr, "  Opt : \"%s\" %d\n", opt_name, i);
973         }
974 
975         else if (strcmp(opt_name, "volatile") == 0) {
976             if (in_brackets && (ptr = get_bool(ptr, &i)) == NULL)
977                 Handle_err;
978             if (i == 0)
979                 clear_volatile(cl->cl_option);
980             else
981                 set_volatile(cl->cl_option);
982             if (debug_opt)
983                 fprintf(stderr, "  Opt : \"%s\" %d\n", opt_name, i);
984         }
985 
986         else if (strcmp(opt_name, "stdout") == 0) {
987             if (in_brackets && (ptr = get_bool(ptr, &i)) == NULL)
988                 Handle_err;
989             if (i == 0)
990                 clear_stdout(cl->cl_option);
991             else
992                 set_stdout(cl->cl_option);
993             if (debug_opt)
994                 fprintf(stderr, "  Opt : \"%s\" %d\n", opt_name, i);
995         }
996 
997         else if (strcmp(opt_name, "n") == 0 || strcmp(opt_name, "nice") == 0) {
998             if (!in_brackets || (ptr = get_nice(ptr, &i)) == NULL)
999                 Handle_err;
1000             cl->cl_nice = (char)i;
1001             if (debug_opt)
1002                 fprintf(stderr, "  Opt : \"%s\" (-)%d\n", opt_name, i);
1003         }
1004 
1005         else if (strcmp(opt_name, "runas") == 0) {
1006             int len = -1;
1007             char *runas = NULL;
1008             struct passwd *pas;
1009 
1010             if (!in_brackets) {
1011                 Handle_err;
1012             }
1013 
1014             len = assign_option_string(&runas, ptr);
1015             if (len <= 0) {
1016                 Handle_err;
1017             }
1018             else {
1019                 ptr += len;
1020             }
1021 
1022             if (getuid() != rootuid) {
1023                 fprintf(stderr, "must be privileged to use option runas: "
1024                         "skipping option\n");
1025                 need_correction = 1;
1026             }
1027             else if (len > USER_NAME_LEN) {
1028                 fprintf(stderr, "runas: user name \"%s\" longer than %d"
1029                         "characters: skipping option\n", runas, USER_NAME_LEN);
1030                 need_correction = 1;
1031             }
1032             else if ((pas = getpwnam(runas)) == NULL) {
1033                 fprintf(stderr, "runas: \"%s\" is not in passwd file : "
1034                         "ignored", runas);
1035                 need_correction = 1;
1036             }
1037 
1038             if (need_correction) {
1039                 Free_safe(runas);
1040             }
1041             else {
1042                 /* only set cl_runas if all is well, as cl_runas MUST always
1043                  * be set to a valid value */
1044                 Set(cl->cl_runas, runas);
1045             }
1046 
1047             /* all good */
1048             if (debug_opt)
1049                 fprintf(stderr, "  Opt : \"%s\" %s ptr=%p\n", opt_name,
1050                         cl->cl_runas, cl->cl_runas);
1051 
1052         }
1053 
1054         else if (strcmp(opt_name, "random") == 0) {
1055             if (in_brackets && (ptr = get_bool(ptr, &i)) == NULL)
1056                 Handle_err;
1057             if (i == 0)
1058                 clear_random(cl->cl_option);
1059             else
1060                 set_random(cl->cl_option);
1061             if (debug_opt)
1062                 fprintf(stderr, "  Opt : \"%s\" %d\n", opt_name, i);
1063         }
1064 
1065         else if (strcmp(opt_name, "jitter") == 0) {
1066             if (!in_brackets
1067                 || (ptr = get_num(ptr, &i, UCHAR_MAX, 0, NULL)) == NULL)
1068                 Handle_err;
1069             cl->cl_jitter = i;
1070             if (debug_opt)
1071                 fprintf(stderr, "  Opt : \"%s\" %d\n", opt_name, i);
1072         }
1073 
1074         /* handle %-line : we check if we are really in a %-line (which we do not do
1075          * for other options), because writing "&hourly" in a fcrontab results in an
1076          * error (hourly ignored) hard to find, and, in any case, annoying. */
1077         else if (cl->cl_runfreq == 1) {
1078             /* options to run once per interval :
1079              * ignore every fields below the limit */
1080             if (strcmp(opt_name, "mins") == 0) {
1081                 /* nothing to do */
1082                 if (debug_opt)
1083                     fprintf(stderr, "  Opt : \"%s\"\n", opt_name);
1084             }
1085             else if (strcmp(opt_name, "hours") == 0) {
1086                 set_freq_mins(cl->cl_option);
1087                 if (debug_opt)
1088                     fprintf(stderr, "  Opt : \"%s\"\n", opt_name);
1089             }
1090             else if (strcmp(opt_name, "days") == 0) {
1091                 set_freq_mins(cl->cl_option);
1092                 set_freq_hrs(cl->cl_option);
1093                 if (debug_opt)
1094                     fprintf(stderr, "  Opt : \"%s\"\n", opt_name);
1095             }
1096             else if (strcmp(opt_name, "mons") == 0) {
1097                 set_freq_mins(cl->cl_option);
1098                 set_freq_hrs(cl->cl_option);
1099                 set_freq_days(cl->cl_option);
1100                 if (debug_opt)
1101                     fprintf(stderr, "  Opt : \"%s\"\n", opt_name);
1102             }
1103             else if (strcmp(opt_name, "dow") == 0) {
1104                 set_freq_mins(cl->cl_option);
1105                 set_freq_hrs(cl->cl_option);
1106                 set_freq_days(cl->cl_option);
1107                 set_freq_mons(cl->cl_option);
1108                 if (debug_opt)
1109                     fprintf(stderr, "  Opt : \"%s\"\n", opt_name);
1110             }
1111 
1112             /* run once an element of the selected field
1113              * (once an hour, once a day, etc) */
1114             else if (strcmp(opt_name, "hourly") == 0) {
1115                 set_freq_hrs(cl->cl_option);
1116                 set_freq_periodically(cl->cl_option);
1117                 if (debug_opt)
1118                     fprintf(stderr, "  Opt : \"%s\"\n", opt_name);
1119             }
1120             else if (strcmp(opt_name, "daily") == 0) {
1121                 set_freq_days(cl->cl_option);
1122                 set_freq_periodically(cl->cl_option);
1123                 if (debug_opt)
1124                     fprintf(stderr, "  Opt : \"%s\"\n", opt_name);
1125             }
1126             else if (strcmp(opt_name, "monthly") == 0) {
1127                 set_freq_mons(cl->cl_option);
1128                 set_freq_periodically(cl->cl_option);
1129                 if (debug_opt)
1130                     fprintf(stderr, "  Opt : \"%s\"\n", opt_name);
1131             }
1132             else if (strcmp(opt_name, "weekly") == 0) {
1133                 set_freq_dow(cl->cl_option);
1134                 set_freq_periodically(cl->cl_option);
1135                 if (debug_opt)
1136                     fprintf(stderr, "  Opt : \"%s\"\n", opt_name);
1137             }
1138 
1139             /* run once an element of the selected field
1140              * from middle to middle of that field
1141              * (ie once from 12h to 12h the following day) */
1142             else if (strcmp(opt_name, "midhourly") == 0) {
1143                 set_freq_hrs(cl->cl_option);
1144                 set_freq_periodically(cl->cl_option);
1145                 set_freq_mid(cl->cl_option);
1146                 if (debug_opt)
1147                     fprintf(stderr, "  Opt : \"%s\"\n", opt_name);
1148             }
1149             else if (strcmp(opt_name, "middaily") == 0
1150                      || strcmp(opt_name, "nightly") == 0) {
1151                 set_freq_days(cl->cl_option);
1152                 set_freq_periodically(cl->cl_option);
1153                 set_freq_mid(cl->cl_option);
1154                 if (debug_opt)
1155                     fprintf(stderr, "  Opt : \"%s\"\n", opt_name);
1156             }
1157             else if (strcmp(opt_name, "midmonthly") == 0) {
1158                 set_freq_mons(cl->cl_option);
1159                 set_freq_periodically(cl->cl_option);
1160                 set_freq_mid(cl->cl_option);
1161                 if (debug_opt)
1162                     fprintf(stderr, "  Opt : \"%s\"\n", opt_name);
1163             }
1164             else if (strcmp(opt_name, "midweekly") == 0) {
1165                 set_freq_dow(cl->cl_option);
1166                 set_freq_periodically(cl->cl_option);
1167                 set_freq_mid(cl->cl_option);
1168                 if (debug_opt)
1169                     fprintf(stderr, "  Opt : \"%s\"\n", opt_name);
1170             }
1171         }
1172 
1173         else {
1174             fprintf(stderr, "%s:%d: Option \"%s\" unknown: "
1175                     "skipping option.\n", file_name, line, opt_name);
1176             need_correction = 1;
1177         }
1178 
1179         if (in_brackets) {
1180             if (*ptr != ')') {
1181             Handle_err}
1182             else
1183                 ptr++;
1184         }
1185 
1186     } while (*ptr == ',' && ptr++);
1187 
1188     Skip_blanks(ptr);
1189     return ptr;
1190 }
1191 
1192 
1193 char *
get_time(char * ptr,time_t * time,int zero_allowed)1194 get_time(char *ptr, time_t * time, int zero_allowed)
1195     /* convert time read in string in time_t format */
1196 {
1197     time_t sum;
1198 
1199     *time = 0;
1200 
1201     while ((*ptr != ' ') && (*ptr != '\t') && (*ptr != '\0') && (*ptr != ')')) {
1202 
1203         sum = 0;
1204 
1205         while (isdigit((int)*ptr)) {
1206             sum *= 10;
1207             sum += *ptr - 48;
1208             ptr++;
1209         }
1210 
1211         /* interpret multipliers */
1212         switch (*ptr) {
1213         case 'm':              /* months */
1214             sum *= 4;
1215         case 'w':              /* weeks */
1216             sum *= 7;
1217         case 'd':              /* days */
1218             sum *= 24;
1219         case 'h':              /* hours */
1220             sum *= 3600;
1221         case 's':              /* seconds */
1222             ptr++;
1223             break;
1224         case ' ':
1225         case '\t':
1226         case ')':
1227             sum *= 60;          /* minutes */
1228             break;
1229         default:
1230             need_correction = 1;
1231             return NULL;
1232         }
1233 
1234         *time += sum;
1235 
1236     }
1237 
1238     Skip_blanks(ptr);
1239     if (*time == 0 && !zero_allowed) {
1240         need_correction = 1;
1241         return NULL;
1242     }
1243     else
1244         return ptr;
1245 }
1246 
1247 
1248 char *
check_username(char * ptr,cf_t * cf,cl_t * cl)1249 check_username(char *ptr, cf_t * cf, cl_t * cl)
1250     /* check ptr to see if the first word is a username, returns new ptr */
1251 {
1252     short int indx = 0;
1253     char username[USER_NAME_LEN];
1254     struct passwd *userpwent;
1255 
1256     /* check to see if next word is a username */
1257     /* we don't allow quotes, to be able to distinguish a user name from
1258      * a command line (where quotes are allowed) */
1259     while (isalnum((int)ptr[indx]) || ptr[indx] == '-' || ptr[indx] == '_')
1260         indx++;
1261     if (indx >= USER_NAME_LEN)
1262         indx = USER_NAME_LEN - 1;
1263     strncpy(username, ptr, indx);
1264     username[indx] = '\0';
1265 
1266     if ((userpwent = getpwnam(username)) != NULL) {
1267         /* found the user */
1268         ptr = ptr + indx;       /* move ptr to the next word */
1269         Skip_blanks(ptr);
1270 
1271         if (getuid() != rootuid) {
1272             fprintf(stderr, "must be privileged to run as another user : "
1273                     "ignoring\n");
1274         }
1275         else {
1276             Set(cl->cl_runas, username);
1277             if (debug_opt)
1278                 fprintf(stderr, "  Opt : inline_runas %s\n", username);
1279         }
1280     }
1281 
1282     return ptr;
1283 }
1284 
1285 
1286 char *
read_word(char * ptr,char * buf,size_t buf_size)1287 read_word(char *ptr, char *buf, size_t buf_size)
1288 /* copy a word to buf with a max_size of buf_size and return a pointer
1289  * to the next char after the word */
1290 {
1291     int i = 0;
1292 
1293     bzero(buf, buf_size);
1294 
1295     while (isalnum((int)*ptr) && i < buf_size)
1296         buf[i++] = *ptr++;
1297 
1298     return ptr;
1299 }
1300 
1301 int
read_shortcut(char * ptr,cf_t * cf)1302 read_shortcut(char *ptr, cf_t * cf)
1303 /* try to read a shortcut entry, and if it is one then append a line to cf
1304  * Return 1 if that was a shortcut entry. If it wasn't, return 0 and make sure
1305  * ptr is back to its orig value. */
1306 {
1307     cl_t *cl = NULL;
1308     char shortcut[20];
1309     char *ptr_orig = ptr;
1310 
1311     cl = dups_cl(&default_line);
1312 
1313     /* skip the @ */
1314     ptr++;
1315 
1316     ptr = read_word(ptr, shortcut, sizeof(shortcut));
1317 
1318     /* time&date by default -- we'll switch to freq if @reboot */
1319     set_td(cl->cl_option);
1320     cl->cl_remain = cl->cl_runfreq;     /* FIXME: necessary?? */
1321 
1322     if (strcmp(shortcut, "reboot") == 0) {
1323         set_freq(cl->cl_option);
1324         set_runatreboot(cl->cl_option);
1325         set_runonce(cl->cl_option);
1326         clear_volatile(cl->cl_option);
1327         cl->cl_runfreq = 0;
1328         cl->cl_first = 0;
1329         /* the job will not be rescheduled after the first execution (flag is_hasrun),
1330          * we set timefreq to LONG_MAX just in case */
1331         cl->cl_timefreq = LONG_MAX;
1332 
1333         if (debug_opt)
1334             fprintf(stderr, "  Shc : @reboot\n");
1335     }
1336     else if (strcmp(shortcut, "yearly") == 0
1337              || strcmp(shortcut, "annually") == 0) {
1338         bit_set(cl->cl_mins, 0);
1339         bit_set(cl->cl_hrs, 0);
1340         bit_set(cl->cl_days, 1);
1341         bit_set(cl->cl_mons, 0);
1342         bit_nset(cl->cl_dow, 0, 7);
1343 
1344         if (debug_opt)
1345             fprintf(stderr, "  Shc : @yearly\n");
1346     }
1347     else if (strcmp(shortcut, "monthly") == 0) {
1348         bit_set(cl->cl_mins, 0);
1349         bit_set(cl->cl_hrs, 0);
1350         bit_set(cl->cl_days, 1);
1351         bit_nset(cl->cl_mons, 0, 11);
1352         bit_nset(cl->cl_dow, 0, 7);
1353 
1354         if (debug_opt)
1355             fprintf(stderr, "  Shc : @monthly\n");
1356     }
1357     else if (strcmp(shortcut, "weekly") == 0) {
1358         bit_set(cl->cl_mins, 0);
1359         bit_set(cl->cl_hrs, 0);
1360         bit_nset(cl->cl_days, 0, 31);
1361         bit_nset(cl->cl_mons, 0, 11);
1362         bit_set(cl->cl_dow, 0);
1363         bit_set(cl->cl_dow, 7); /* 0 and 7 are both sunday */
1364 
1365         if (debug_opt)
1366             fprintf(stderr, "  Shc : @weekly\n");
1367     }
1368     else if (strcmp(shortcut, "daily") == 0
1369              || strcmp(shortcut, "midnight") == 0) {
1370         bit_set(cl->cl_mins, 0);
1371         bit_set(cl->cl_hrs, 0);
1372         bit_nset(cl->cl_days, 0, 31);
1373         bit_nset(cl->cl_mons, 0, 11);
1374         bit_nset(cl->cl_dow, 0, 7);
1375 
1376         if (debug_opt)
1377             fprintf(stderr, "  Shc : @daily\n");
1378     }
1379     else if (strcmp(shortcut, "hourly") == 0) {
1380         bit_set(cl->cl_mins, 0);
1381         bit_nset(cl->cl_hrs, 0, 23);
1382         bit_nset(cl->cl_days, 0, 31);
1383         bit_nset(cl->cl_mons, 0, 11);
1384         bit_nset(cl->cl_dow, 0, 7);
1385 
1386         if (debug_opt)
1387             fprintf(stderr, "  Shc : @hourly\n");
1388     }
1389     else {
1390         /* this is not a shortcut line but a normal @-line:  */
1391         ptr = ptr_orig;
1392         return 0;
1393     }
1394 
1395     /* The next char must be a space (no other option allowed when using
1396      * a shortcut: if the user wants to use options, they should use the
1397      * native fcron lines */
1398     if (!isspace((int)*ptr)) {
1399         fprintf(stderr, "%s:%d: No space after shortcut: skipping line.\n",
1400                 file_name, line);
1401         goto exiterr;
1402     }
1403 
1404     /* skip spaces before the username / shell command */
1405     while (isspace((int)*ptr))
1406         ptr++;
1407 
1408     /* check for inline runas */
1409     ptr = check_username(ptr, cf, cl);
1410 
1411     /* get cl_shell field ( remove trailing blanks ) */
1412     if ((cl->cl_shell = get_string(ptr)) == NULL) {
1413         fprintf(stderr, "%s:%d: Mismatched quotes: skipping line.\n",
1414                 file_name, line);
1415         goto exiterr;
1416     }
1417     if (strcmp(cl->cl_shell, "\0") == 0) {
1418         fprintf(stderr, "%s:%d: No shell command: skipping line.\n",
1419                 file_name, line);
1420         Free_safe(cl->cl_shell);
1421         goto exiterr;
1422     }
1423 
1424 #ifndef USE_SENDMAIL
1425     clear_mail(cl->cl_option);
1426     clear_mailzerolength(cl->cl_option);
1427 #endif
1428 
1429     cl->cl_next = cf->cf_line_base;
1430     cf->cf_line_base = cl;
1431 
1432     if (debug_opt)
1433         fprintf(stderr, "  Cmd \"%s\" (shortcut)\n", cl->cl_shell);
1434     return 1;
1435 
1436  exiterr:
1437     Free_safe(cl);
1438     need_correction = 1;
1439     return 1;
1440 }
1441 
1442 void
read_freq(char * ptr,cf_t * cf)1443 read_freq(char *ptr, cf_t * cf)
1444     /* read a freq entry, and append a line to cf */
1445 {
1446     cl_t *cl = NULL;
1447 
1448     cl = dups_cl(&default_line);
1449 
1450     cl->cl_first = -1;          /* 0 is a valid value, so we have to use -1 to detect unset */
1451 
1452     /* skip the @ */
1453     ptr++;
1454 
1455     /* get the time before first execution or the options */
1456     if (isdigit((int)*ptr)) {
1457         if ((ptr = get_time(ptr, &(cl->cl_first), 1)) == NULL) {
1458             fprintf(stderr, "%s:%d: Error while reading first delay:"
1459                     " skipping line.\n", file_name, line);
1460             goto exiterr;
1461         }
1462 
1463         Skip_blanks(ptr);
1464     }
1465     else if (isalnum((int)*ptr)) {
1466         if ((ptr = read_opt(ptr, cl)) == NULL)
1467             goto exiterr;
1468     }
1469     else
1470         Skip_blanks(ptr);
1471 
1472     /* we set this here, because it may be unset by read_opt (reset option) */
1473     cl->cl_runfreq = 0;
1474     set_freq(cl->cl_option);
1475 
1476     /* then cl_timefreq */
1477     if ((ptr = get_time(ptr, (time_t *) & (cl->cl_timefreq), 0)) == NULL
1478         || cl->cl_timefreq < 1) {
1479         fprintf(stderr,
1480                 "%s:%d: Error while reading frequency %s: skipping line.\n",
1481                 file_name, line,
1482                 (cl->cl_timefreq < 1) ? "(lower than 1s) " : "");
1483         goto exiterr;
1484     }
1485 
1486     if (cl->cl_timefreq == 0) {
1487         fprintf(stderr, "%s:%d: no freq specified: skipping line.\n",
1488                 file_name, line);
1489         goto exiterr;
1490     }
1491 
1492     if (cl->cl_first == -1)
1493         /* time before first execution was not specified explicitely */
1494         cl->cl_first = cl->cl_timefreq;
1495 
1496     /* check for inline runas */
1497     ptr = check_username(ptr, cf, cl);
1498 
1499     /* get cl_shell field ( remove trailing blanks ) */
1500     if ((cl->cl_shell = get_string(ptr)) == NULL) {
1501         fprintf(stderr, "%s:%d: Mismatched quotes: skipping line.\n",
1502                 file_name, line);
1503         goto exiterr;
1504     }
1505     if (strcmp(cl->cl_shell, "\0") == 0) {
1506         fprintf(stderr, "%s:%d: No shell command: skipping line.\n",
1507                 file_name, line);
1508         Free_safe(cl->cl_shell);
1509         goto exiterr;
1510     }
1511 
1512 #ifndef USE_SENDMAIL
1513     clear_mail(cl->cl_option);
1514     clear_mailzerolength(cl->cl_option);
1515 #endif
1516 
1517     cl->cl_next = cf->cf_line_base;
1518     cf->cf_line_base = cl;
1519 
1520     if (debug_opt)
1521         fprintf(stderr, "  Cmd \"%s\", timefreq %ld, first %ld\n",
1522                 cl->cl_shell, cl->cl_timefreq, (long int)cl->cl_first);
1523 
1524     return;
1525 
1526  exiterr:
1527     free_line(cl);
1528     need_correction = 1;
1529     return;
1530 }
1531 
1532 
1533 
1534 #define R_field(PTR, ARY, MAX, ARYCONST, DESCRP) \
1535   if((PTR = read_field(PTR, ARY, MAX, ARYCONST)) == NULL) { \
1536       if (debug_opt) \
1537           fprintf(stderr, "\n"); \
1538       fprintf(stderr, "%s:%d: Error while reading " DESCRP " field: " \
1539              "skipping line.\n", file_name, line); \
1540       Free_safe(cl); \
1541       return; \
1542   }
1543 
1544 
1545 void
read_arys(char * ptr,cf_t * cf)1546 read_arys(char *ptr, cf_t * cf)
1547     /* read a run freq number plus a normal fcron line */
1548 {
1549     cl_t *cl = NULL;
1550     int i = 0;
1551 
1552     cl = dups_cl(&default_line);
1553 
1554     /* set cl_remain if not specified */
1555     if (*ptr == '&') {
1556         ptr++;
1557         if (isdigit((int)*ptr)) {
1558             if ((ptr = get_num(ptr, &i, USHRT_MAX, 0, NULL)) == NULL) {
1559                 fprintf(stderr, "%s:%d: Error while reading runfreq:"
1560                         " skipping line.\n", file_name, line);
1561                 goto exiterr;
1562             }
1563             else {
1564                 if (i <= 1) {
1565                     fprintf(stderr, "%s:%d: runfreq must be 2 or more :"
1566                             " skipping line.\n", file_name, line);
1567                     goto exiterr;
1568                 }
1569                 cl->cl_runfreq = (unsigned short)i;
1570             }
1571         }
1572         else if (isalnum((int)*ptr))
1573             if ((ptr = read_opt(ptr, cl)) == NULL) {
1574                 goto exiterr;
1575             }
1576         Skip_blanks(ptr);
1577     }
1578 
1579     cl->cl_remain = cl->cl_runfreq;
1580 
1581     /* we set this here, because it may be unset by read_opt (reset option) */
1582     set_td(cl->cl_option);
1583 
1584     if (debug_opt)
1585         fprintf(stderr, "     ");
1586 
1587     /* get the fields (check for errors) */
1588     R_field(ptr, cl->cl_mins, 59, NULL, "minutes");
1589     R_field(ptr, cl->cl_hrs, 23, NULL, "hours");
1590     R_field(ptr, cl->cl_days, 31, NULL, "days");
1591     /* month are defined by user from 1 to 12 : max is 12 */
1592     R_field(ptr, cl->cl_mons, 12, mons_ary, "months");
1593     R_field(ptr, cl->cl_dow, 7, dows_ary, "days of week");
1594 
1595     if (debug_opt)
1596         /* if debug_opt is set, we print informations in read_field function,
1597          *  but no end line : we print it here */
1598         fprintf(stderr, " remain %d\n", cl->cl_remain);
1599 
1600     /* check for inline runas */
1601     ptr = check_username(ptr, cf, cl);
1602 
1603     /* get the shell command (remove trailing blanks) */
1604     if ((cl->cl_shell = get_string(ptr)) == NULL) {
1605         fprintf(stderr, "%s:%d: Mismatched quotes: skipping line.\n",
1606                 file_name, line);
1607         goto exiterr;
1608     }
1609     if (strcmp(cl->cl_shell, "\0") == 0) {
1610         fprintf(stderr, "%s:%d: No shell command: skipping line.\n",
1611                 file_name, line);
1612         Free_safe(cl->cl_shell);
1613         goto exiterr;
1614     }
1615 
1616 #ifndef USE_SENDMAIL
1617     clear_mail(cl->cl_option);
1618     clear_mailzerolength(cl->cl_option);
1619 #endif
1620 
1621     cl->cl_next = cf->cf_line_base;
1622     cf->cf_line_base = cl;
1623 
1624     if (debug_opt)
1625         fprintf(stderr, "  Cmd \"%s\"\n", cl->cl_shell);
1626     return;
1627 
1628  exiterr:
1629     need_correction = 1;
1630     free_line(cl);
1631     return;
1632 
1633 }
1634 
1635 void
read_period(char * ptr,cf_t * cf)1636 read_period(char *ptr, cf_t * cf)
1637     /* read a line to run periodically (i.e. once a day, once a week, etc) */
1638 {
1639     cl_t *cl = NULL;
1640     short int remain = 8;
1641 
1642     cl = dups_cl(&default_line);
1643 
1644     /* skip the % */
1645     ptr++;
1646 
1647     /* a runfreq set to 1 means : this is a periodical line
1648      * (runfreq cannot be changed by read_opt() if already set to 1) */
1649     cl->cl_remain = cl->cl_runfreq = 1;
1650 
1651     /* set cl_remain if not specified */
1652     if ((ptr = read_opt(ptr, cl)) == NULL) {
1653         goto exiterr;
1654     }
1655     Skip_blanks(ptr);
1656 
1657     /* we set this here, because it may be unset by read_opt (reset option) */
1658     set_td(cl->cl_option);
1659 
1660     if (debug_opt)
1661         fprintf(stderr, "     ");
1662 
1663     if (is_freq_periodically(cl->cl_option)) {
1664         if (is_freq_mins(cl->cl_option))
1665             remain = 0;
1666         else if (is_freq_hrs(cl->cl_option))
1667             remain = 1;
1668         else if (is_freq_days(cl->cl_option))
1669             remain = 2;
1670         else if (is_freq_mons(cl->cl_option))
1671             remain = 3;
1672         else if (is_freq_dow(cl->cl_option))
1673             remain = 2;
1674     }
1675 
1676     /* get the fields (check for errors) */
1677     if (remain-- > 0) {
1678     R_field(ptr, cl->cl_mins, 59, NULL, "minutes")}
1679     else
1680         bit_nset(cl->cl_mins, 0, 59);
1681     if (remain-- > 0) {
1682     R_field(ptr, cl->cl_hrs, 23, NULL, "hours")}
1683     else
1684         bit_nset(cl->cl_hrs, 0, 23);
1685     if (remain-- > 0) {
1686     R_field(ptr, cl->cl_days, 31, NULL, "days")}
1687     else
1688         bit_nset(cl->cl_days, 1, 32);
1689     /* month are defined by user from 1 to 12 : max is 12 */
1690     if (remain-- > 0) {
1691     R_field(ptr, cl->cl_mons, 12, mons_ary, "months")}
1692     else
1693         bit_nset(cl->cl_mons, 0, 11);
1694     if (remain-- > 0) {
1695     R_field(ptr, cl->cl_dow, 7, dows_ary, "days of week")}
1696     else
1697         bit_nset(cl->cl_dow, 0, 7);
1698 
1699     if (debug_opt)
1700         /* if debug_opt is set, we print informations in read_field function,
1701          *  but no end line : we print it here */
1702         fprintf(stderr, " remain %d\n", cl->cl_remain);
1703 
1704     /* check for inline runas */
1705     ptr = check_username(ptr, cf, cl);
1706 
1707     /* get the shell command (remove trailing blanks) */
1708     if ((cl->cl_shell = get_string(ptr)) == NULL) {
1709         fprintf(stderr, "%s:%d: Mismatched quotes: skipping line.\n",
1710                 file_name, line);
1711         goto exiterr;
1712     }
1713     if (strcmp(cl->cl_shell, "\0") == 0) {
1714         fprintf(stderr, "%s:%d: No shell command: skipping line.\n",
1715                 file_name, line);
1716         Free_safe(cl->cl_shell);
1717         goto exiterr;
1718     }
1719     else if (cl->cl_shell[0] == '*' || isdigit((int)cl->cl_shell[0]))
1720         fprintf(stderr, "%s:%d: Warning : shell command beginning by '%c'.\n",
1721                 file_name, line, cl->cl_shell[0]);
1722 
1723     /* check for non matching if option runfreq is set to 1 */
1724     if (!is_freq_periodically(cl->cl_option)) {
1725         const size_t s_mins = 60, s_hrs = 24;
1726         const size_t s_days = 32, s_mons = 12;
1727         const size_t s_dow = 8;
1728         int j = 0;
1729 
1730         if (!is_freq_mins(cl->cl_option)) {
1731             bit_ffc(cl->cl_mins, s_mins, &j);
1732             if (j != -1 && j < s_mins)
1733                 goto ok;
1734         }
1735         if (!is_freq_hrs(cl->cl_option)) {
1736             bit_ffc(cl->cl_hrs, s_hrs, &j);
1737             if (j != -1 && j < s_hrs)
1738                 goto ok;
1739         }
1740         if (!is_freq_days(cl->cl_option)) {
1741             bit_ffc(cl->cl_days, s_days, &j);
1742             if (j != -1 && j < s_days) {
1743                 if (is_dayand(cl->cl_option))
1744                     goto ok;
1745                 else {
1746                     if (!is_freq_dow(cl->cl_option)) {
1747                         bit_ffc(cl->cl_dow, s_dow, &j);
1748                         if (j != -1 && j < s_dow)
1749                             goto ok;
1750                     }
1751                 }
1752             }
1753         }
1754         if (!is_freq_mons(cl->cl_option)) {
1755             bit_ffc(cl->cl_mons, s_mons, &j);
1756             if (j != -1 && j < s_mons)
1757                 goto ok;
1758         }
1759         if (!is_freq_dow(cl->cl_option)) {
1760             bit_ffc(cl->cl_dow, s_dow, &j);
1761             if (j != -1 && j < s_dow && is_dayand(cl->cl_option))
1762                 goto ok;
1763         }
1764 
1765         fprintf(stderr, "%s:%d: periodical line with no intervals: "
1766                 "skipping line.\n", file_name, line);
1767         goto exiterr;
1768     }
1769 
1770  ok:
1771 #ifndef USE_SENDMAIL
1772     clear_mail(cl->cl_option);
1773     clear_mailzerolength(cl->cl_option);
1774 #endif
1775 
1776     cl->cl_next = cf->cf_line_base;
1777     cf->cf_line_base = cl;
1778 
1779     if (debug_opt)
1780         fprintf(stderr, "  Cmd \"%s\"\n", cl->cl_shell);
1781     return;
1782 
1783  exiterr:
1784     need_correction = 1;
1785     free_line(cl);
1786     return;
1787 
1788 }
1789 
1790 char *
get_num(char * ptr,int * num,int max,short int decimal,const char ** names)1791 get_num(char *ptr, int *num, int max, short int decimal, const char **names)
1792     /* read a string's number and return it under int format.
1793      *  Also check if that number is less than max */
1794 {
1795     int i = 0;
1796     *num = 0;
1797 
1798     if (isalpha((int)*ptr)) {
1799 
1800         if (names == NULL) {
1801             need_correction = 1;
1802             return NULL;
1803         }
1804 
1805         /* set string to lower case */
1806         for (i = 0; i < strlen(names[0]); i++)
1807             *(ptr + i) = tolower(*(ptr + i));
1808 
1809         for (i = 0; names[i]; ++i)
1810             if (strncmp(ptr, names[i], strlen(names[i])) == 0) {
1811                 *num = i;
1812                 ptr += strlen(names[i]);
1813                 return ptr;
1814                 break;
1815             }
1816 
1817         /* string is not in name list */
1818         need_correction = 1;
1819         return NULL;
1820 
1821     }
1822     else {
1823 
1824         while (isdigit((int)*ptr) || *ptr == '.') {
1825 
1826             if (*ptr == '.' && ptr++ && i++ > 0)
1827                 return NULL;
1828             if (i > 0 && --decimal < 0) {
1829                 /* the decimal number is exceeded : we round off,
1830                  * skip the other decimals and return */
1831                 if (*ptr >= '5')
1832                     *num += 1;
1833                 while (isdigit((int)*(++ptr))) ;
1834                 ptr--;
1835             }
1836             else {
1837                 *num *= 10;
1838                 *num += *ptr - 48;
1839             }
1840 
1841             if (*num > max) {
1842                 need_correction = 1;
1843                 return NULL;
1844             }
1845 
1846             ptr++;
1847 
1848         }
1849 
1850         if (decimal > 0)
1851             *num *= 10 * decimal;
1852 
1853     }
1854 
1855     return ptr;
1856 }
1857 
1858 
1859 char *
read_field(char * ptr,bitstr_t * ary,int max,const char ** names)1860 read_field(char *ptr, bitstr_t * ary, int max, const char **names)
1861     /* read a field like "2,5-8,10-20/2,21-30~25" and fill ary */
1862 {
1863     int start = 0;
1864     int stop = 0;
1865     int step = 0;
1866     int rm = 0;
1867     int i = 0;
1868 
1869     while ((*ptr != ' ') && (*ptr != '\t') && (*ptr != '\0')) {
1870 
1871         start = stop = step = 0;
1872 
1873         /* there may be a "," */
1874         if (*ptr == ',')
1875             ptr++;
1876 
1877         if (*ptr == '*') {
1878             /* we have to fill everything (may be modified by a step ) */
1879             start = 0;
1880             /* user set month from 1 to 12, but we manage it internally
1881              * as a number from 0 to 11 */
1882             stop = (max == 12) ? 11 : max;
1883             ptr++;
1884         }
1885         else {
1886 
1887             ptr = get_num(ptr, &start, max, 0, names);
1888             if (ptr == NULL)
1889                 return NULL;
1890             if (max == 12)
1891                 /* this number is part of the month field.
1892                  * user set it from 1 to 12, but we manage it internally
1893                  * as a number from 0 to 11 : we remove 1 to start */
1894                 start -= 1;
1895 
1896             if (*ptr == ',' || *ptr == ' ' || *ptr == '\t') {
1897                 /* this is a single number : set up array and continue */
1898                 if (debug_opt)
1899                     fprintf(stderr, " %d", start);
1900                 bit_set(ary, start);
1901                 continue;
1902             }
1903 
1904             /* check for a dash */
1905             else if (*ptr == '-') {
1906                 ptr++;
1907                 ptr = get_num(ptr, &stop, max, 0, names);
1908                 if (ptr == NULL)
1909                     /* we reached the end of the string to parse */
1910                     return NULL;
1911                 if (max == 12)
1912                     /* this number is part of the month field.
1913                      * user set it from 1 to 12, but we manage it internally
1914                      * as a number from 0 to 11 : we remove 1 to stop */
1915                     stop -= 1;
1916             }
1917             else {
1918                 /* syntax error */
1919                 need_correction = 1;
1920                 return NULL;
1921             }
1922         }
1923 
1924         /* check for step size */
1925         if (*ptr == '/') {
1926             ptr++;
1927             if ((ptr = get_num(ptr, &step, max, 0, names)) == NULL || step == 0)
1928                 return NULL;
1929         }
1930         else
1931             /* step undefined : default is 0 */
1932             step = 1;
1933 
1934         /* fill array */
1935         if (debug_opt)
1936             fprintf(stderr, " %d-%d/%d", start, stop, step);
1937 
1938         if (start < stop)
1939             for (i = start; i <= stop; i += step)
1940                 bit_set(ary, i);
1941         else {
1942             short int field_max = (max == 12) ? 11 : max;
1943             /* this is a field like (for hours field) "22-3" :
1944              * we should set from 22 to 3 (not from 3 to 22 or nothing :)) ) */
1945             for (i = start; i <= field_max; i += step)
1946                 bit_set(ary, i);
1947             for (i -= (field_max + 1); i <= stop; i += step)
1948                 bit_set(ary, i);
1949         }
1950 
1951         /* finally, remove unwanted values */
1952         while (*ptr == '~') {
1953             ptr++;
1954             rm = 0;
1955             if ((ptr = get_num(ptr, &rm, max, 0, names)) == NULL)
1956                 return NULL;
1957             if (max == 12)
1958                 /* this number is part of the month field.
1959                  * user set it from 1 to 12, but we manage it internally
1960                  * as a number from 0 to 11 : we remove 1 to rm */
1961                 rm -= 1;
1962 
1963             if (debug_opt)
1964                 fprintf(stderr, " ~%d", rm);
1965             bit_clear(ary, rm);
1966 
1967             /* if we remove one value of Sunday, remove the other */
1968             if (max == 7 && rm == 0) {
1969                 bit_clear(ary, 7);
1970                 if (debug_opt)
1971                     fprintf(stderr, " ~%d", 7);
1972             }
1973             else if (max == 7 && rm == 7) {
1974                 bit_clear(ary, 0);
1975                 if (debug_opt)
1976                     fprintf(stderr, " ~%d", 0);
1977             }
1978 
1979         }
1980 
1981     }
1982 
1983     /* Sunday is both 0 and 7 : if one is set, set the other */
1984     if (max == 7) {
1985         if (bit_test(ary, 0))
1986             bit_set(ary, 7);
1987         else if (bit_test(ary, 7))
1988             bit_set(ary, 0);
1989     }
1990 
1991     Skip_blanks(ptr);
1992 
1993     if (debug_opt)
1994         fprintf(stderr, " #");
1995 
1996     return ptr;
1997 }
1998 
1999 void
delete_file(const char * user_name)2000 delete_file(const char *user_name)
2001     /* free a file if user_name is not null
2002      *   otherwise free all files */
2003 {
2004     cf_t *file = NULL;
2005     cf_t *prev_file = NULL;
2006     cl_t *line = NULL;
2007     cl_t *cur_line = NULL;
2008 
2009     file = file_base;
2010     while (file != NULL) {
2011         if (strcmp(user_name, file->cf_user) == 0) {
2012 
2013             /* free lines */
2014             cur_line = file->cf_line_base;
2015             while ((line = cur_line) != NULL) {
2016                 cur_line = line->cl_next;
2017                 free_line(line);
2018             }
2019             break;
2020 
2021         }
2022         else {
2023             prev_file = file;
2024             file = file->cf_next;
2025         }
2026     }
2027 
2028     if (file == NULL)
2029         /* file not in list */
2030         return;
2031 
2032     /* remove file from list */
2033     if (prev_file == NULL)
2034         file_base = file->cf_next;
2035     else
2036         prev_file->cf_next = file->cf_next;
2037 
2038     /* free env variables */
2039     env_list_destroy(file->cf_env_list);
2040 
2041     /* finally free file itself */
2042     Free_safe(file->cf_user);
2043     Free_safe(file);
2044 
2045 }
2046 
2047 int
save_file(char * path)2048 save_file(char *path)
2049     /* Store the informations relatives to the executions
2050      * of tasks at a defined frequency of system's running time */
2051 {
2052     cf_t *file = NULL;
2053 
2054     if (debug_opt)
2055         fprintf(stderr, "Saving ...\n");
2056 
2057     for (file = file_base; file; file = file->cf_next) {
2058 
2059         /* save_file() is run under user's rights.
2060          * If fcrontab is run by root for a normal user, we must change the file's
2061          * ownership to this user, in order to make fcron check the runas fields.
2062          * (a malicious user could put a runas(root) and wait for the fcrontab to be
2063          * installed by root) */
2064 #ifdef USE_SETE_ID
2065         if (save_file_safe(file, path, "fcrontab", asuid, fcrontab_gid, 0) ==
2066             ERR)
2067             return ERR;
2068 #else
2069         if (save_file_safe
2070             (file, path, "fcrontab", fcrontab_uid, fcrontab_gid, 0) == ERR)
2071             return ERR;
2072 #endif
2073 
2074     }
2075 
2076     return OK;
2077 }
2078