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 <unix.h>
26 #include <exec_tools.h>
27 #include <file_lib.h>
28 
29 #ifdef HAVE_SYS_UIO_H
30 # include <sys/uio.h>
31 #endif
32 
33 #ifndef __MINGW32__
34 
35 /* Max size of the 'passwd' string in the getpwuid_r() function,
36  * man:getpwuid_r(3) says that this value "Should be more than enough". */
37 #define GETPW_R_SIZE_MAX 16384
38 
39 static bool IsProcessRunning(pid_t pid);
40 
ProcessSignalTerminate(pid_t pid)41 void ProcessSignalTerminate(pid_t pid)
42 {
43     if(!IsProcessRunning(pid))
44     {
45         return;
46     }
47 
48 
49     if(kill(pid, SIGINT) == -1)
50     {
51         Log(LOG_LEVEL_ERR, "Could not send SIGINT to pid '%jd'. (kill: %s)",
52             (intmax_t)pid, GetErrorStr());
53     }
54 
55     sleep(1);
56     if (kill(pid, 0) != 0)
57     {
58         /* can no longer send signals to the process => it's dead now */
59         return;
60     }
61 
62     if(kill(pid, SIGTERM) == -1)
63     {
64         Log(LOG_LEVEL_ERR, "Could not send SIGTERM to pid '%jd'. (kill: %s)",
65             (intmax_t)pid, GetErrorStr());
66     }
67 
68     sleep(5);
69     if (kill(pid, 0) != 0)
70     {
71         /* can no longer send signals to the process => it's dead now */
72         return;
73     }
74 
75     if(kill(pid, SIGKILL) == -1)
76     {
77         Log(LOG_LEVEL_ERR, "Could not send SIGKILL to pid '%jd'. (kill: %s)",
78             (intmax_t)pid, GetErrorStr());
79     }
80 
81     sleep(1);
82 }
83 
84 /*************************************************************/
85 
IsProcessRunning(pid_t pid)86 static bool IsProcessRunning(pid_t pid)
87 {
88     int res = kill(pid, 0);
89 
90     if(res == 0)
91     {
92         return true;
93     }
94 
95     if(res == -1 && errno == ESRCH)
96     {
97         return false;
98     }
99 
100     Log(LOG_LEVEL_ERR, "Failed checking for process existence. (kill: %s)", GetErrorStr());
101 
102     return false;
103 }
104 
105 /*************************************************************/
106 
GetCurrentUserName(char * userName,int userNameLen)107 bool GetCurrentUserName(char *userName, int userNameLen)
108 {
109     char buf[GETPW_R_SIZE_MAX] = {0};
110     struct passwd pwd;
111     struct passwd *result;
112 
113     memset(userName, 0, userNameLen);
114     int ret = getpwuid_r(getuid(), &pwd, buf, GETPW_R_SIZE_MAX, &result);
115 
116     if (result == NULL)
117     {
118         Log(LOG_LEVEL_ERR, "Could not get user name of current process, using 'UNKNOWN'. (getpwuid: %s)",
119             ret == 0 ? "not found" : GetErrorStrFromCode(ret));
120         strlcpy(userName, "UNKNOWN", userNameLen);
121         return false;
122     }
123 
124     strlcpy(userName, result->pw_name, userNameLen);
125     return true;
126 }
127 
128 /*************************************************************/
129 
IsExecutable(const char * file)130 bool IsExecutable(const char *file)
131 {
132     struct stat sb;
133     gid_t grps[NGROUPS];
134     int n;
135 
136     if (stat(file, &sb) == -1)
137     {
138         Log(LOG_LEVEL_ERR, "Proposed executable file '%s' doesn't exist", file);
139         return false;
140     }
141 
142     if (sb.st_mode & 02)
143     {
144         Log(LOG_LEVEL_ERR, "SECURITY ALERT: promised executable '%s' is world writable! ", file);
145         Log(LOG_LEVEL_ERR, "SECURITY ALERT: CFEngine will not execute this - requires human inspection");
146         return false;
147     }
148 
149     if ((getuid() == sb.st_uid) || (getuid() == 0))
150     {
151         if (sb.st_mode & 0100)
152         {
153             return true;
154         }
155     }
156     else if (getgid() == sb.st_gid)
157     {
158         if (sb.st_mode & 0010)
159         {
160             return true;
161         }
162     }
163     else
164     {
165         if (sb.st_mode & 0001)
166         {
167             return true;
168         }
169 
170         if ((n = getgroups(NGROUPS, grps)) > 0)
171         {
172             int i;
173 
174             for (i = 0; i < n; i++)
175             {
176                 if (grps[i] == sb.st_gid)
177                 {
178                     if (sb.st_mode & 0010)
179                     {
180                         return true;
181                     }
182                 }
183             }
184         }
185     }
186 
187     return false;
188 }
189 
ShellCommandReturnsZero(const char * command,ShellType shell)190 bool ShellCommandReturnsZero(const char *command, ShellType shell)
191 {
192     int status;
193     pid_t pid;
194 
195     if (shell == SHELL_TYPE_POWERSHELL)
196     {
197         Log(LOG_LEVEL_ERR, "Powershell is only supported on Windows");
198         return false;
199     }
200 
201     if ((pid = fork()) < 0)
202     {
203         Log(LOG_LEVEL_ERR, "Failed to fork new process: %s", command);
204         return false;
205     }
206     else if (pid == 0)          /* child */
207     {
208         ALARM_PID = -1;
209 
210         if (shell == SHELL_TYPE_USE)
211         {
212             if (execl(SHELL_PATH, "sh", "-c", command, NULL) == -1)
213             {
214                 Log(LOG_LEVEL_ERR, "Command '%s' failed. (execl: %s)", command, GetErrorStr());
215                 exit(EXIT_FAILURE); /* OK since this is a forked proc and no registered cleanup functions */
216             }
217         }
218         else
219         {
220             char **argv = ArgSplitCommand(command);
221             int devnull;
222 
223             if (LogGetGlobalLevel() < LOG_LEVEL_INFO)
224             {
225                 if ((devnull = open("/dev/null", O_WRONLY)) == -1)
226                 {
227                     Log(LOG_LEVEL_ERR, "Command '%s' failed. (open: %s)", command, GetErrorStr());
228                     exit(EXIT_FAILURE); /* OK since this is a forked proc and no registered cleanup functions */
229                 }
230 
231                 if (dup2(devnull, STDOUT_FILENO) == -1 || dup2(devnull, STDERR_FILENO) == -1)
232                 {
233                     Log(LOG_LEVEL_ERR, "Command '%s' failed. (dup2: %s)", command, GetErrorStr());
234                     exit(EXIT_FAILURE); /* OK since this is a forked proc and no registered cleanup functions */
235                 }
236 
237                 close(devnull);
238             }
239 
240             if (execv(argv[0], argv) == -1)
241             {
242                 Log(LOG_LEVEL_ERR, "Command '%s' failed. (execv: %s)", argv[0], GetErrorStr());
243                 exit(EXIT_FAILURE); /* OK since this is a forked proc and no registered cleanup functions */
244             }
245         }
246     }
247     else                        /* parent */
248     {
249         ALARM_PID = pid;
250 
251         while (waitpid(pid, &status, 0) < 0)
252         {
253             if (errno != EINTR)
254             {
255                 return false;
256             }
257         }
258 
259         return (WEXITSTATUS(status) == 0);
260     }
261 
262     return false;
263 }
264 
265 #endif /* !__MINGW32__ */
266