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 #include <pipes.h>
26 
27 #include <mutex.h>
28 #include <global_mutex.h>
29 #include <exec_tools.h>
30 #include <rlist.h>
31 #include <policy.h>
32 #include <eval_context.h>
33 #include <file_lib.h>
34 #include <signals.h>
35 #include <string_lib.h>
36 
37 static bool CfSetuid(uid_t uid, gid_t gid);
38 
39 static int cf_pwait(pid_t pid);
40 
41 static pid_t *CHILDREN = NULL; /* GLOBAL_X */
42 static int MAX_FD = 2048; /* GLOBAL_X */ /* Max number of simultaneous pipes */
43 
44 
ChildrenFDInit()45 static void ChildrenFDInit()
46 {
47     ThreadLock(cft_count);
48     if (CHILDREN == NULL)       /* first time */
49     {
50         CHILDREN = xcalloc(MAX_FD, sizeof(pid_t));
51     }
52 
53     ThreadUnlock(cft_count);
54 }
55 
56 /*****************************************************************************/
57 
58 /* This leaks memory and is not thread-safe! To be used only when you are
59  * about to exec() or _exit(), and only async-signal-safe code is allowed. */
ChildrenFDUnsafeClose()60 static void ChildrenFDUnsafeClose()
61 {
62     /* GenericCreatePipeAndFork() must have been called to init this. */
63     assert(CHILDREN != NULL);
64 
65     for (int i = 0; i < MAX_FD; i++)
66     {
67         if (CHILDREN[i] > 0)
68         {
69             close(i);
70         }
71     }
72     CHILDREN = NULL;                                    /* leaks on purpose */
73 }
74 
75 /* This is the original safe version, but not signal-handler-safe.
76    It's currently unused. */
77 #if 0
78 static void ChildrenFDClose()
79 {
80     ThreadLock(cft_count);
81     int i;
82     for (i = 0; i < MAX_FD; i++)
83     {
84         if (CHILDREN[i] > 0)
85         {
86             close(i);
87         }
88     }
89     free(CHILDREN);
90     CHILDREN = NULL;
91     ThreadUnlock(cft_count);
92 }
93 #endif
94 
ChildOutputSelectDupClose(int pd[2],OutputSelect output_select)95 static void ChildOutputSelectDupClose(int pd[2], OutputSelect output_select)
96 {
97     close(pd[0]); // Don't need output from parent
98 
99     if (pd[1] != 1) // TODO: When is pd[1] == 1 ???
100     {
101         if ((output_select == OUTPUT_SELECT_BOTH)
102             || (output_select == OUTPUT_SELECT_STDOUT))
103         {
104             // close our(child) stdout(1) and open (pd[1]) as stdout
105             dup2(pd[1], 1);
106             // Subsequent stdout output will go to parent (pd[1])
107         }
108         else
109         {
110             // Close / discard stdout
111             int nullfd = open(NULLFILE, O_WRONLY);
112             dup2(nullfd, 1);
113             close(nullfd);
114         }
115 
116         if ((output_select == OUTPUT_SELECT_BOTH)
117             || (output_select == OUTPUT_SELECT_STDERR))
118         {
119             // close our(child) stderr(2) and open (pd[1]) as stderr
120             dup2(pd[1], 2);
121             // Subsequent stderr output will go to parent (pd[1])
122         }
123         else
124         {
125             // Close / discard stderr
126             int nullfd = open(NULLFILE, O_WRONLY);
127             dup2(nullfd, 2);
128             close(nullfd);
129         }
130 
131         close(pd[1]);
132     }
133 }
134 
135 /*****************************************************************************/
136 
ChildrenFDSet(int fd,pid_t pid)137 static void ChildrenFDSet(int fd, pid_t pid)
138 {
139     int new_max = 0;
140 
141     if (fd >= MAX_FD)
142     {
143         Log(LOG_LEVEL_WARNING,
144             "File descriptor %d of child %jd higher than MAX_FD, check for defunct children",
145             fd, (intmax_t) pid);
146         new_max = fd + 32;
147     }
148 
149     ThreadLock(cft_count);
150 
151     if (new_max)
152     {
153         CHILDREN = xrealloc(CHILDREN, new_max * sizeof(pid_t));
154         MAX_FD = new_max;
155     }
156 
157     CHILDREN[fd] = pid;
158     ThreadUnlock(cft_count);
159 }
160 
161 /*****************************************************************************/
162 
163 typedef struct
164 {
165     const char *type;
166     int pipe_desc[2];
167 } IOPipe;
168 
GenericCreatePipeAndFork(IOPipe * pipes)169 static pid_t GenericCreatePipeAndFork(IOPipe *pipes)
170 {
171     for (int i = 0; i < 2; i++)
172     {
173         if (pipes[i].type && !PipeTypeIsOk(pipes[i].type))
174         {
175             errno = EINVAL;
176             return -1;
177         }
178     }
179 
180     ChildrenFDInit();
181 
182     /* Create pair of descriptors to this process. */
183     if (pipes[0].type && pipe(pipes[0].pipe_desc) < 0)
184     {
185         return -1;
186     }
187 
188     /* Create second pair of descriptors (if exists) to this process.
189      * This will allow full I/O operations. */
190     if (pipes[1].type && pipe(pipes[1].pipe_desc) < 0)
191     {
192         close(pipes[0].pipe_desc[0]);
193         close(pipes[0].pipe_desc[1]);
194         return -1;
195     }
196 
197     pid_t pid = -1;
198 
199     if ((pid = fork()) == (pid_t) -1)
200     {
201         /* One pipe will be always here. */
202         close(pipes[0].pipe_desc[0]);
203         close(pipes[0].pipe_desc[1]);
204 
205         /* Second pipe is optional so we have to check existence. */
206         if (pipes[1].type)
207         {
208             close(pipes[1].pipe_desc[0]);
209             close(pipes[1].pipe_desc[1]);
210         }
211         return -1;
212     }
213 
214     /* Ignore SIGCHLD, by setting the handler to SIG_DFL. NOTE: this is
215      * different than setting to SIG_IGN. In the latter case no zombies are
216      * generated ever and you can't wait() for the child to finish. */
217     struct sigaction sa = {
218         .sa_handler = SIG_DFL,
219     };
220     sigemptyset(&sa.sa_mask);
221     sigaction(SIGCHLD, &sa, NULL);
222 
223     if (pid == 0)                                               /* child */
224     {
225         /* WARNING only call async-signal-safe functions in child. */
226 
227         /* The fork()ed child is always single-threaded, but we are only
228          * allowed to call async-signal-safe functions (man 3p fork). */
229 
230         // Redmine #2971: reset SIGPIPE signal handler in the child to have a
231         // sane behavior of piped commands within child
232         signal(SIGPIPE, SIG_DFL);
233 
234         /* The child should always accept all signals after it has exec'd,
235          * else the child might be unkillable! (happened in ENT-3147). */
236         sigset_t sigmask;
237         sigemptyset(&sigmask);
238         sigprocmask(SIG_SETMASK, &sigmask, NULL);
239     }
240 
241     ALARM_PID = (pid != 0 ? pid : -1);
242 
243     return pid;
244 }
245 
246 /*****************************************************************************/
247 
CreatePipeAndFork(const char * type,int * pd)248 static pid_t CreatePipeAndFork(const char *type, int *pd)
249 {
250     IOPipe pipes[2];
251     pipes[0].type = type;
252     pipes[1].type = NULL; /* We don't want to create this one. */
253 
254     pid_t pid = GenericCreatePipeAndFork(pipes);
255 
256     pd[0] = pipes[0].pipe_desc[0];
257     pd[1] = pipes[0].pipe_desc[1];
258 
259     return pid;
260 }
261 
262 /*****************************************************************************/
263 
CreatePipesAndFork(const char * type,int * pd,int * pdb)264 static pid_t CreatePipesAndFork(const char *type, int *pd, int *pdb)
265 {
266     IOPipe pipes[2];
267     /* Both pipes MUST have the same type. */
268     pipes[0].type = type;
269     pipes[1].type = type;
270 
271     pid_t pid =  GenericCreatePipeAndFork(pipes);
272 
273     pd[0] = pipes[0].pipe_desc[0];
274     pd[1] =  pipes[0].pipe_desc[1];
275     pdb[0] = pipes[1].pipe_desc[0];
276     pdb[1] = pipes[1].pipe_desc[1];
277     return pid;
278 }
279 
280 /*****************************************************************************/
281 
cf_popen_full_duplex(const char * command,bool capture_stderr,bool require_full_path)282 IOData cf_popen_full_duplex(const char *command, bool capture_stderr, bool require_full_path)
283 {
284     /* For simplifying reading and writing directions */
285     const int READ=0, WRITE=1;
286     int child_pipe[2];  /* From child to parent */
287     int parent_pipe[2]; /* From parent to child */
288     pid_t pid;
289 
290     char **argv = ArgSplitCommand(command);
291 
292     fflush(NULL); /* Empty file buffers */
293     pid = CreatePipesAndFork("r+t", child_pipe, parent_pipe);
294 
295     if (pid == (pid_t) -1)
296     {
297         Log(LOG_LEVEL_ERR, "Couldn't fork child process: %s", GetErrorStr());
298         ArgFree(argv);
299         return (IOData) {-1, -1, NULL, NULL};
300     }
301 
302     else if (pid > 0) // parent
303     {
304         close(child_pipe[WRITE]);
305         close(parent_pipe[READ]);
306 
307         IOData io_desc;
308         io_desc.write_fd = parent_pipe[WRITE];
309         io_desc.read_fd = child_pipe[READ];
310         io_desc.read_stream = NULL;
311         io_desc.write_stream = NULL;
312 
313         ChildrenFDSet(parent_pipe[WRITE], pid);
314         ChildrenFDSet(child_pipe[READ], pid);
315         ArgFree(argv);
316         return io_desc;
317     }
318     else // child
319     {
320         close(child_pipe[READ]);
321         close(parent_pipe[WRITE]);
322 
323         /* Open stdin from parant process and stdout from child */
324         if (dup2(parent_pipe[READ], 0) == -1 || dup2(child_pipe[WRITE],1) == -1)
325         {
326             Log(LOG_LEVEL_ERR, "Can not execute dup2: %s", GetErrorStr());
327             _exit(EXIT_FAILURE);
328         }
329 
330         if (capture_stderr)
331         {
332             /* Merge stdout/stderr */
333             if(dup2(child_pipe[WRITE], 2) == -1)
334             {
335                 Log(LOG_LEVEL_ERR, "Can not execute dup2 for merging stderr: %s",
336                     GetErrorStr());
337                 _exit(EXIT_FAILURE);
338             }
339         }
340         else
341         {
342             /* leave stderr open */
343         }
344 
345         close(child_pipe[WRITE]);
346         close(parent_pipe[READ]);
347 
348         ChildrenFDUnsafeClose();
349 
350         int res;
351         if (require_full_path)
352         {
353             res = execv(argv[0], argv);
354         }
355         else
356         {
357             res = execvp(argv[0], argv);
358         }
359 
360         if (res == -1)
361         {
362             /* NOTE: exec functions return only when error have occurred. */
363             Log(LOG_LEVEL_ERR, "Couldn't run '%s'. (%s: %s)",
364                 argv[0],
365                 require_full_path ? "execv" : "execvp",
366                 GetErrorStr());
367         }
368 
369         /* We shouldn't reach this point */
370         _exit(EXIT_FAILURE);
371     }
372 }
373 
cf_popen_select(const char * command,const char * type,OutputSelect output_select)374 FILE *cf_popen_select(const char *command, const char *type, OutputSelect output_select)
375 {
376     int pd[2];
377     pid_t pid;
378     FILE *pp = NULL;
379 
380     char **argv = ArgSplitCommand(command);
381 
382     pid = CreatePipeAndFork(type, pd);
383     if (pid == (pid_t) -1)
384     {
385         ArgFree(argv);
386         return NULL;
387     }
388 
389     if (pid == 0)                                               /* child */
390     {
391         /* WARNING only call async-signal-safe functions in child. */
392 
393         switch (*type)
394         {
395         case 'r':
396             ChildOutputSelectDupClose(pd, output_select);
397             break;
398 
399         case 'w':
400 
401             close(pd[1]);
402 
403             if (pd[0] != 0)
404             {
405                 dup2(pd[0], 0);
406                 close(pd[0]);
407             }
408         }
409 
410         ChildrenFDUnsafeClose();
411 
412         if (execv(argv[0], argv) == -1)
413         {
414             Log(LOG_LEVEL_ERR, "Couldn't run '%s'. (execv: %s)", argv[0], GetErrorStr());
415         }
416 
417         _exit(EXIT_FAILURE);
418     }
419     else                                                        /* parent */
420     {
421         switch (*type)
422         {
423         case 'r':
424 
425             close(pd[1]);
426 
427             if ((pp = fdopen(pd[0], type)) == NULL)
428             {
429                 cf_pwait(pid);
430                 ArgFree(argv);
431                 return NULL;
432             }
433             break;
434 
435         case 'w':
436 
437             close(pd[0]);
438 
439             if ((pp = fdopen(pd[1], type)) == NULL)
440             {
441                 cf_pwait(pid);
442                 ArgFree(argv);
443                 return NULL;
444             }
445         }
446 
447         ChildrenFDSet(fileno(pp), pid);
448         ArgFree(argv);
449         return pp;
450     }
451 
452     ProgrammingError("Unreachable code");
453     return NULL;
454 }
455 
cf_popen(const char * command,const char * type,bool capture_stderr)456 FILE *cf_popen(const char *command, const char *type, bool capture_stderr)
457 {
458     return cf_popen_select(
459         command,
460         type,
461         capture_stderr ? OUTPUT_SELECT_BOTH : OUTPUT_SELECT_STDOUT);
462 }
463 
464 /*****************************************************************************/
465 
466 /*
467  * WARNING: this is only allowed to be called from single-threaded code,
468  *          because of the safe_chdir() call in the forked child.
469  */
cf_popensetuid(const char * command,const char * type,uid_t uid,gid_t gid,char * chdirv,char * chrootv,ARG_UNUSED int background)470 FILE *cf_popensetuid(const char *command, const char *type,
471                      uid_t uid, gid_t gid, char *chdirv, char *chrootv,
472                      ARG_UNUSED int background)
473 {
474     int pd[2];
475     pid_t pid;
476     FILE *pp = NULL;
477 
478     char **argv = ArgSplitCommand(command);
479 
480     pid = CreatePipeAndFork(type, pd);
481     if (pid == (pid_t) -1)
482     {
483         ArgFree(argv);
484         return NULL;
485     }
486 
487     if (pid == 0)                                               /* child */
488     {
489         /* WARNING only call async-signal-safe functions in child. */
490 
491         switch (*type)
492         {
493         case 'r':
494 
495             close(pd[0]);       /* Don't need output from parent */
496 
497             if (pd[1] != 1)
498             {
499                 dup2(pd[1], 1); /* Attach pp=pd[1] to our stdout */
500                 dup2(pd[1], 2); /* Merge stdout/stderr */
501                 close(pd[1]);
502             }
503 
504             break;
505 
506         case 'w':
507 
508             close(pd[1]);
509 
510             if (pd[0] != 0)
511             {
512                 dup2(pd[0], 0);
513                 close(pd[0]);
514             }
515         }
516 
517         ChildrenFDUnsafeClose();
518 
519         if (chrootv && (strlen(chrootv) != 0))
520         {
521             if (chroot(chrootv) == -1)
522             {
523                 Log(LOG_LEVEL_ERR, "Couldn't chroot to '%s'. (chroot: %s)", chrootv, GetErrorStr());
524                 _exit(EXIT_FAILURE);
525             }
526         }
527 
528         if (chdirv && (strlen(chdirv) != 0))
529         {
530             if (safe_chdir(chdirv) == -1)
531             {
532                 Log(LOG_LEVEL_ERR, "Couldn't chdir to '%s'. (chdir: %s)", chdirv, GetErrorStr());
533                 _exit(EXIT_FAILURE);
534             }
535         }
536 
537         if (!CfSetuid(uid, gid))
538         {
539             _exit(EXIT_FAILURE);
540         }
541 
542         if (execv(argv[0], argv) == -1)
543         {
544             Log(LOG_LEVEL_ERR, "Couldn't run '%s'. (execv: %s)", argv[0], GetErrorStr());
545         }
546 
547         _exit(EXIT_FAILURE);
548     }
549     else                                                        /* parent */
550     {
551         switch (*type)
552         {
553         case 'r':
554 
555             close(pd[1]);
556 
557             if ((pp = fdopen(pd[0], type)) == NULL)
558             {
559                 cf_pwait(pid);
560                 ArgFree(argv);
561                 return NULL;
562             }
563             break;
564 
565         case 'w':
566 
567             close(pd[0]);
568 
569             if ((pp = fdopen(pd[1], type)) == NULL)
570             {
571                 cf_pwait(pid);
572                 ArgFree(argv);
573                 return NULL;
574             }
575         }
576 
577         ChildrenFDSet(fileno(pp), pid);
578         ArgFree(argv);
579         return pp;
580     }
581 
582     ProgrammingError("Unreachable code");
583     return NULL;
584 }
585 
586 /*****************************************************************************/
587 /* Shell versions of commands - not recommended for security reasons         */
588 /*****************************************************************************/
589 
cf_popen_sh_select(const char * command,const char * type,OutputSelect output_select)590 FILE *cf_popen_sh_select(const char *command, const char *type, OutputSelect output_select)
591 {
592     int pd[2];
593     pid_t pid;
594     FILE *pp = NULL;
595 
596     pid = CreatePipeAndFork(type, pd);
597     if (pid == (pid_t) -1)
598     {
599         return NULL;
600     }
601 
602     if (pid == 0)                                               /* child */
603     {
604         /* WARNING only call async-signal-safe functions in child. */
605 
606         switch (*type)
607         {
608         case 'r':
609             ChildOutputSelectDupClose(pd, output_select);
610 
611             break;
612 
613         case 'w':
614 
615             close(pd[1]);
616 
617             if (pd[0] != 0)
618             {
619                 dup2(pd[0], 0);
620                 close(pd[0]);
621             }
622         }
623 
624         ChildrenFDUnsafeClose();
625 
626         execl(SHELL_PATH, "sh", "-c", command, NULL);
627 
628         Log(LOG_LEVEL_ERR, "Couldn't run: '%s'  (execl: %s)", command, GetErrorStr());
629         _exit(EXIT_FAILURE);
630     }
631     else                                                        /* parent */
632     {
633         switch (*type)
634         {
635         case 'r':
636 
637             close(pd[1]);
638 
639             if ((pp = fdopen(pd[0], type)) == NULL)
640             {
641                 cf_pwait(pid);
642                 return NULL;
643             }
644             break;
645 
646         case 'w':
647 
648             close(pd[0]);
649 
650             if ((pp = fdopen(pd[1], type)) == NULL)
651             {
652                 cf_pwait(pid);
653                 return NULL;
654             }
655         }
656 
657         ChildrenFDSet(fileno(pp), pid);
658         return pp;
659     }
660 
661     ProgrammingError("Unreachable code");
662     return NULL;
663 }
664 
cf_popen_sh(const char * command,const char * type)665 FILE *cf_popen_sh(const char *command, const char *type)
666 {
667     return cf_popen_sh_select(command, type, OUTPUT_SELECT_BOTH);
668 }
669 
670 /******************************************************************************/
671 
672 /*
673  * WARNING: this is only allowed to be called from single-threaded code,
674  *          because of the safe_chdir() call in the forked child.
675  */
cf_popen_shsetuid(const char * command,const char * type,uid_t uid,gid_t gid,char * chdirv,char * chrootv,ARG_UNUSED int background)676 FILE *cf_popen_shsetuid(const char *command, const char *type,
677                         uid_t uid, gid_t gid, char *chdirv, char *chrootv,
678                         ARG_UNUSED int background)
679 {
680     int pd[2];
681     pid_t pid;
682     FILE *pp = NULL;
683 
684     pid = CreatePipeAndFork(type, pd);
685     if (pid == (pid_t) -1)
686     {
687         return NULL;
688     }
689 
690     if (pid == 0)                                               /* child */
691     {
692         /* WARNING only call async-signal-safe functions in child. */
693 
694         switch (*type)
695         {
696         case 'r':
697 
698             close(pd[0]);       /* Don't need output from parent */
699 
700             if (pd[1] != 1)
701             {
702                 dup2(pd[1], 1); /* Attach pp=pd[1] to our stdout */
703                 dup2(pd[1], 2); /* Merge stdout/stderr */
704                 close(pd[1]);
705             }
706 
707             break;
708 
709         case 'w':
710 
711             close(pd[1]);
712 
713             if (pd[0] != 0)
714             {
715                 dup2(pd[0], 0);
716                 close(pd[0]);
717             }
718         }
719 
720         ChildrenFDUnsafeClose();
721 
722         if (chrootv && (strlen(chrootv) != 0))
723         {
724             if (chroot(chrootv) == -1)
725             {
726                 Log(LOG_LEVEL_ERR, "Couldn't chroot to '%s'. (chroot: %s)", chrootv, GetErrorStr());
727                 _exit(EXIT_FAILURE);
728             }
729         }
730 
731         if (chdirv && (strlen(chdirv) != 0))
732         {
733             if (safe_chdir(chdirv) == -1)
734             {
735                 Log(LOG_LEVEL_ERR, "Couldn't chdir to '%s'. (chdir: %s)", chdirv, GetErrorStr());
736                 _exit(EXIT_FAILURE);
737             }
738         }
739 
740         if (!CfSetuid(uid, gid))
741         {
742             _exit(EXIT_FAILURE);
743         }
744 
745         execl(SHELL_PATH, "sh", "-c", command, NULL);
746 
747         Log(LOG_LEVEL_ERR, "Couldn't run: '%s'  (execl: %s)", command, GetErrorStr());
748         _exit(EXIT_FAILURE);
749     }
750     else                                                        /* parent */
751     {
752         switch (*type)
753         {
754         case 'r':
755 
756             close(pd[1]);
757 
758             if ((pp = fdopen(pd[0], type)) == NULL)
759             {
760                 cf_pwait(pid);
761                 return NULL;
762             }
763             break;
764 
765         case 'w':
766 
767             close(pd[0]);
768 
769             if ((pp = fdopen(pd[1], type)) == NULL)
770             {
771                 cf_pwait(pid);
772                 return NULL;
773             }
774         }
775 
776         ChildrenFDSet(fileno(pp), pid);
777         return pp;
778     }
779 
780     ProgrammingError("Unreachable code");
781     return NULL;
782 }
783 
cf_pwait(pid_t pid)784 static int cf_pwait(pid_t pid)
785 {
786     Log(LOG_LEVEL_DEBUG,
787         "cf_pwait - waiting for process %jd", (intmax_t) pid);
788 
789     int status;
790     while (waitpid(pid, &status, 0) < 0)
791     {
792         if (errno != EINTR)
793         {
794             Log(LOG_LEVEL_ERR,
795                 "Waiting for child PID %jd failed (waitpid: %s)",
796                 (intmax_t) pid, GetErrorStr());
797             return -1;
798         }
799     }
800 
801     if (!WIFEXITED(status))
802     {
803         Log(LOG_LEVEL_VERBOSE,
804             "Child PID %jd exited abnormally (%s)", (intmax_t) pid,
805             WIFSIGNALED(status) ? "signalled" : (
806                 WIFSTOPPED(status) ? "stopped" : (
807                     WIFCONTINUED(status) ? "continued" : "unknown" )));
808         return -1;
809     }
810 
811     int retcode = WEXITSTATUS(status);
812 
813     Log(LOG_LEVEL_DEBUG, "cf_pwait - process %jd exited with code: %d",
814         (intmax_t) pid, retcode);
815     return retcode;
816 }
817 
818 /*******************************************************************/
819 
820 /**
821  * Closes the pipe without waiting the child.
822  *
823  * @param pp pipe to the child process
824  */
cf_pclose_nowait(FILE * pp)825 void cf_pclose_nowait(FILE *pp)
826 {
827     if (fclose(pp) == EOF)
828     {
829         Log(LOG_LEVEL_ERR,
830             "Could not close the pipe to the executed subcommand (fclose: %s)",
831             GetErrorStr());
832     }
833 }
834 
835 /**
836  * Closes the pipe and wait()s for PID of the child,
837  * in order to reap the zombies.
838  */
cf_pclose(FILE * pp)839 int cf_pclose(FILE *pp)
840 {
841     int fd = fileno(pp);
842     pid_t pid;
843 
844     ThreadLock(cft_count);
845 
846     if (CHILDREN == NULL)       /* popen hasn't been called */
847     {
848         ThreadUnlock(cft_count);
849         fclose(pp);
850         return -1;
851     }
852 
853     ALARM_PID = -1;
854 
855     if (fd >= MAX_FD)
856     {
857         ThreadUnlock(cft_count);
858         Log(LOG_LEVEL_ERR,
859             "File descriptor %d of child higher than MAX_FD in cf_pclose!",
860             fd);
861         fclose(pp);
862         return -1;
863     }
864 
865     pid = CHILDREN[fd];
866     CHILDREN[fd] = 0;
867     ThreadUnlock(cft_count);
868 
869     if (fclose(pp) == EOF)
870     {
871         Log(LOG_LEVEL_ERR,
872             "Could not close the pipe to the executed subcommand (fclose: %s)",
873             GetErrorStr());
874     }
875 
876     return cf_pwait(pid);
877 }
878 
cf_pclose_full_duplex_side(int fd)879 int cf_pclose_full_duplex_side(int fd)
880 {
881     ThreadLock(cft_count);
882 
883     if (CHILDREN == NULL)       /* popen hasn't been called */
884     {
885         ThreadUnlock(cft_count);
886         close(fd);
887         return -1;
888     }
889 
890     if (fd >= MAX_FD)
891     {
892         ThreadUnlock(cft_count);
893         Log(LOG_LEVEL_ERR,
894             "File descriptor %d of child higher than MAX_FD in cf_pclose_full_duplex_side!",
895             fd);
896     }
897     else
898     {
899         CHILDREN[fd] = 0;
900         ThreadUnlock(cft_count);
901     }
902     return close(fd);
903 }
904 
905 
906 /* We are assuming that read_fd part will be always open at this point. */
cf_pclose_full_duplex(IOData * data)907 int cf_pclose_full_duplex(IOData *data)
908 {
909     assert(data != NULL);
910     ThreadLock(cft_count);
911 
912     if (CHILDREN == NULL)
913     {
914         ThreadUnlock(cft_count);
915         if (data->read_stream != NULL)
916         {
917             // fclose closes the underlying fd / socket,
918             // but we need the fd for handling of processes below.
919             assert(data->read_fd >= 0);
920             fclose(data->read_stream);
921         }
922         else if (data->read_fd >= 0)
923         {
924             close(data->read_fd);
925         }
926 
927         if (data->write_stream != NULL)
928         {
929             // fclose closes the underlying fd / socket,
930             // but we need the fd for handling of processes below.
931             assert(data->write_fd >= 0);
932             fclose(data->write_stream);
933         }
934         else if (data->write_fd >= 0)
935         {
936             close(data->write_fd);
937         }
938         return -1;
939     }
940 
941     ALARM_PID = -1;
942     pid_t pid = 0;
943 
944     /* Safe as pipes[1] is -1 if not initialized */
945     if (data->read_fd >= MAX_FD || data->write_fd >= MAX_FD)
946     {
947         ThreadUnlock(cft_count);
948         Log(LOG_LEVEL_ERR,
949             "File descriptor %d of child higher than MAX_FD in cf_pclose!",
950             data->read_fd > data->write_fd ? data->read_fd : data->write_fd);
951     }
952     else
953     {
954         pid = CHILDREN[data->read_fd];
955         if (data->write_fd >= 0)
956         {
957             assert(pid == CHILDREN[data->write_fd]);
958             CHILDREN[data->write_fd] = 0;
959         }
960         CHILDREN[data->read_fd] = 0;
961         ThreadUnlock(cft_count);
962     }
963 
964     if (data->read_stream != NULL)
965     {
966         // Stream is open, fclose it
967         if (fclose(data->read_stream) != 0)
968         {
969             return -1;
970         }
971     }
972     else
973     {
974         // No stream, just close fd
975         if (close(data->read_fd) != 0)
976         {
977             return -1;
978         }
979     }
980 
981     if (data->write_fd >= 0)
982     {
983         // write fd needs to be closed
984         if (data->write_stream != NULL)
985         {
986             // Stream is open, fclose it
987             if (fclose(data->write_stream) != 0)
988             {
989                 return -1;
990             }
991         }
992         else
993         {
994             // No stream, close fd directly
995             if (close(data->write_fd) != 0)
996             {
997                 return -1;
998             }
999         }
1000     }
1001 
1002     if (pid == 0)
1003     {
1004         return -1;
1005     }
1006 
1007     return cf_pwait(pid);
1008 }
1009 
PipeToPid(pid_t * pid,FILE * pp)1010 bool PipeToPid(pid_t *pid, FILE *pp)
1011 {
1012     int fd = fileno(pp);
1013     ThreadLock(cft_count);
1014 
1015     if (CHILDREN == NULL)       /* popen hasn't been called */
1016     {
1017         ThreadUnlock(cft_count);
1018         return false;
1019     }
1020 
1021     *pid = CHILDREN[fd];
1022     ThreadUnlock(cft_count);
1023 
1024     return true;
1025 }
1026 
1027 /*******************************************************************/
1028 
CfSetuid(uid_t uid,gid_t gid)1029 static bool CfSetuid(uid_t uid, gid_t gid)
1030 {
1031     struct passwd *pw;
1032 
1033     if (gid != (gid_t) - 1)
1034     {
1035         Log(LOG_LEVEL_VERBOSE, "Changing gid to %ju", (uintmax_t)gid);
1036 
1037         if (setgid(gid) == -1)
1038         {
1039             Log(LOG_LEVEL_ERR, "Couldn't set gid to '%ju'. (setgid: %s)", (uintmax_t)gid, GetErrorStr());
1040             return false;
1041         }
1042 
1043         /* Now eliminate any residual privileged groups */
1044 
1045         if ((pw = getpwuid(uid)) == NULL)
1046         {
1047             Log(LOG_LEVEL_ERR, "Unable to get login groups when dropping privilege to '%ju'. (getpwuid: %s)", (uintmax_t)uid, GetErrorStr());
1048             return false;
1049         }
1050 
1051         if (initgroups(pw->pw_name, pw->pw_gid) == -1)
1052         {
1053             Log(LOG_LEVEL_ERR, "Unable to set login groups when dropping privilege to '%s=%ju'. (initgroups: %s)", pw->pw_name,
1054                   (uintmax_t)uid, GetErrorStr());
1055             return false;
1056         }
1057     }
1058 
1059     if (uid != (uid_t) - 1)
1060     {
1061         Log(LOG_LEVEL_VERBOSE, "Changing uid to '%ju'", (uintmax_t)uid);
1062 
1063         if (setuid(uid) == -1)
1064         {
1065             Log(LOG_LEVEL_ERR, "Couldn't set uid to '%ju'. (setuid: %s)", (uintmax_t)uid, GetErrorStr());
1066             return false;
1067         }
1068     }
1069 
1070     return true;
1071 }
1072 
1073 /* For Windows we need a different method because select() does not */
1074 /* work with non-socket file descriptors. */
PipeIsReadWriteReady(const IOData * io,int timeout_sec)1075 int PipeIsReadWriteReady(const IOData *io, int timeout_sec)
1076 {
1077     fd_set  rset;
1078     FD_ZERO(&rset);
1079     FD_SET(io->read_fd, &rset);
1080 
1081     struct timeval tv = {
1082         .tv_sec = timeout_sec,
1083         .tv_usec = 0,
1084     };
1085 
1086     Log(LOG_LEVEL_DEBUG,
1087         "PipeIsReadWriteReady: wait max %ds for data on fd %d",
1088         timeout_sec, io->read_fd);
1089 
1090     int ret = select(io->read_fd + 1, &rset, NULL, NULL, &tv);
1091 
1092     if (ret < 0)
1093     {
1094         Log(LOG_LEVEL_VERBOSE, "Failed checking for data (select: %s)",
1095             GetErrorStr());
1096         return -1;
1097     }
1098     else if (FD_ISSET(io->read_fd, &rset))
1099     {
1100         return io->read_fd;
1101     }
1102     else if (ret == 0)
1103     {
1104         /* timeout_sec has elapsed but no data was available. */
1105         return 0;
1106     }
1107     else
1108     {
1109         UnexpectedError("select() returned > 0 but our only fd is not set!");
1110         return -1;
1111     }
1112 }
1113