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