1 krisbash 1.1 /*
2 **==============================================================================
3 **
4 ** Open Management Infrastructure (OMI)
5 **
6 ** Copyright (c) Microsoft Corporation
7 **
8 ** Licensed under the Apache License, Version 2.0 (the "License"); you may not
9 ** use this file except in compliance with the License. You may obtain a copy
10 ** of the License at
11 **
12 ** http://www.apache.org/licenses/LICENSE-2.0
13 **
14 ** THIS CODE IS PROVIDED *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 ** KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
16 ** WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
17 ** MERCHANTABLITY OR NON-INFRINGEMENT.
18 **
19 ** See the Apache 2 License for the specific language governing permissions
20 ** and limitations under the License.
21 **
22 krisbash 1.1 **==============================================================================
23 */
24
25 #include "preexec.h"
26 #include <common.h>
27 #include <base/base.h>
28 #include <pal/strings.h>
29 #include <base/paths.h>
30 #include <base/strarr.h>
31
32 #if defined(CONFIG_POSIX)
33 # include <pthread.h>
34 # include <errno.h>
35 # include <unistd.h>
36 # include <sys/types.h>
37 # include <sys/wait.h>
38 # include <signal.h>
39 #endif
40
41 /*
42 **==============================================================================
43 krisbash 1.1 **
44 ** Local Definitions:
45 **
46 **==============================================================================
47 */
48
49 static pid_t _Exec(
50 const char* path,
51 const char* uidStr,
52 const char* gidStr)
53 {
54 pid_t pid = fork();
55
56 /* Return if failure */
57 if (pid < 0)
58 return -1;
59
60 /* Return if parent */
61 if (pid > 0)
62 return pid;
63
64 krisbash 1.1 /* In child process here... */
65
66 /* Close any open file descriptors */
67 {
68 int fd;
69 int n = getdtablesize();
70
71 if (n > 2500 || n < 0)
72 n = 2500;
73
74 /* Leave stdin(0), stdout(1), stderr(2) open (for debugging) */
75 for (fd = 3; fd < n; ++fd)
76 close(fd);
77 }
78
79 /* Execute the program */
80 execl(
81 path, /* path of program */
82 path, /* argv[0] */
83 uidStr, /* argv[1] */
84 gidStr, /* argv[2] */
85 krisbash 1.1 NULL);
86
87 _exit(1);
88 /* Unreachable */
89 return -1;
90 }
91
92 static void _BlockSIGCHLD()
93 {
94 sigset_t set;
95 sigemptyset(&set);
96 sigaddset(&set, SIGCHLD);
97 sigprocmask(SIG_BLOCK, &set, NULL);
98 }
99
100 static void _UnblockSIGCHLD()
101 {
102 sigset_t set;
103 sigemptyset(&set);
104 sigaddset(&set, SIGCHLD);
105 sigprocmask(SIG_UNBLOCK, &set, NULL);
106 krisbash 1.1 }
107
108 typedef struct _Bucket /* derives from HashBucket */
109 {
110 struct _Bucket* next;
111 char* key;
112 }
113 Bucket;
114
115 static size_t _Hash(
116 const HashBucket* bucket_)
117 {
118 Bucket* bucket = (Bucket*)bucket_;
119 size_t h = 0;
120 char* key = bucket->key;
121
122 while (*key)
123 h += 5 * *key++;
124
125 return h;
126 }
127 krisbash 1.1
128 static int _Equal(
129 const HashBucket* bucket1_,
130 const HashBucket* bucket2_)
131 {
132 Bucket* bucket1 = (Bucket*)bucket1_;
133 Bucket* bucket2 = (Bucket*)bucket2_;
134 return strcmp(bucket1->key, bucket2->key) == 0;
135 }
136
137 static void _Release(
138 HashBucket* bucket_)
139 {
140 Bucket* bucket = (Bucket*)bucket_;
141 PAL_Free(bucket->key);
142 PAL_Free(bucket);
143 }
144
145 static MI_Boolean _Contains(
146 HashMap* self,
147 const char* key)
148 krisbash 1.1 {
149 Bucket bucket;
150 bucket.key = (char*)key;
151 return HashMap_Find(self, (const HashBucket*)&bucket) ? MI_TRUE : MI_FALSE;
152 }
153
154 static int _Insert(
155 HashMap* self,
156 const char* key)
157 {
158 Bucket* bucket = (Bucket*)PAL_Calloc(1, sizeof(Bucket));
159
160 if (!bucket)
161 return -1;
162
163 bucket->key = PAL_Strdup(key);
164
165 if (!bucket->key)
166 {
167 PAL_Free(bucket);
168 return -1;
169 krisbash 1.1 }
170
171 if (HashMap_Insert(self, (HashBucket*)bucket) != 0)
172 {
173 PAL_Free(bucket);
174 PAL_Free(bucket->key);
175 return -1;
176 }
177
178 return 0;
179 }
180
181 /*
182 **==============================================================================
183 **
184 ** Public Definitions:
185 **
186 **==============================================================================
187 */
188
189 int PreExec_Construct(
190 krisbash 1.1 PreExec* self)
191 {
192 const size_t NUMLISTS = 32;
193 memset(self, 0, sizeof(*self));
194
195 if (HashMap_Init(&self->cache, NUMLISTS, _Hash, _Equal, _Release) != 0)
196 return -1;
197
198 return 0;
199 }
200
201 void PreExec_Destruct(
202 PreExec* self)
203 {
204 HashMap_Destroy(&self->cache);
205 }
206
207 int PreExec_Exec(
208 PreExec* self,
209 const char* programPath,
210 uid_t uid,
211 krisbash 1.1 uid_t gid)
212 {
213 char path[PAL_MAX_PATH_SIZE];
214 char key[PAL_MAX_PATH_SIZE];
215 char uidBuf[11];
216 const char* uidStr;
217 char gidBuf[11];
218 const char* gidStr;
219
220 /* If no pre-exec program, nothing to do */
221 if (programPath == NULL)
222 return 0;
223
224 /* Form the UID string */
225 {
226 size_t dummy;
227 uidStr = Uint32ToStr(uidBuf, (PAL_Uint32)uid, &dummy);
228 }
229
230 /* Form the GID string */
231 {
232 krisbash 1.1 size_t dummy;
233 gidStr = Uint32ToStr(gidBuf, (PAL_Uint32)gid, &dummy);
234 }
235
236 /* Form a hash key from PREEXEC+UID+GID */
237 {
238 key[0] = '\0';
239 Strlcat(key, programPath, PAL_MAX_PATH_SIZE);
240 Strlcat(key, "+", PAL_MAX_PATH_SIZE);
241 Strlcat(key, uidStr, PAL_MAX_PATH_SIZE);
242 Strlcat(key, "+", PAL_MAX_PATH_SIZE);
243 Strlcat(key, gidStr, PAL_MAX_PATH_SIZE);
244 }
245
246 /* If key already in cache, then return without doing anything */
247 {
248 static pthread_mutex_t s_mutex = PTHREAD_MUTEX_INITIALIZER;
249
250 pthread_mutex_lock(&s_mutex);
251
252 if (_Contains(&self->cache, key))
253 krisbash 1.1 {
254 pthread_mutex_unlock(&s_mutex);
255 return 0;
256 }
257
258 /* Add key to cache */
259 _Insert(&self->cache, key);
260 pthread_mutex_unlock(&s_mutex);
261 }
262
263 /* If programPath is relative, form the full path of the pre-exec program */
264 {
265 path[0] = '\0';
266 if (*programPath != '/')
267 {
268 const char* bindir = OMI_GetPath(ID_BINDIR);
269
270 if (bindir != NULL)
271 {
272 Strlcpy(path, bindir, PAL_MAX_PATH_SIZE);
273 Strlcat(path, "/", PAL_MAX_PATH_SIZE);
274 krisbash 1.1 }
275 }
276
277 Strlcat(path, programPath, PAL_MAX_PATH_SIZE);
278 }
279
280 /* Execute and wait on the pre-exec program to exit */
281 _BlockSIGCHLD();
282 {
283 pid_t pid = _Exec(path, uidStr, gidStr);
284
285 if (pid == -1)
286 {
287 _UnblockSIGCHLD();
288 trace_PreExecFailed(path);
289 return -1;
290 }
291
292 {
293 pid_t r;
294 int status;
295 krisbash 1.1
296 r = waitpid(pid, &status, 0);
297
298 if (r != pid || WEXITSTATUS(status) != 0)
299 {
300 _UnblockSIGCHLD();
301 trace_PreExecFailed(path);
302 return -1;
303 }
304 }
305 }
306 _UnblockSIGCHLD();
307
308 trace_PreExecOk(path);
309 return 0;
310 }
|