librunas: clone another process also if not root
[lttng-tools.git] / librunas / runas.c
1 /*
2 * Copyright (C) 2011 - David Goulet <david.goulet@polymtl.ca>
3 * Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by the Free
7 * Software Foundation; only version 2 of the License.
8 *
9 * This program is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
12 * more details.
13 *
14 * You should have received a copy of the GNU General Public License along with
15 * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
16 * Place - Suite 330, Boston, MA 02111-1307, USA.
17 */
18
19 #define _GNU_SOURCE
20 #include <errno.h>
21 #include <limits.h>
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <sys/wait.h>
26 #include <sys/types.h>
27 #include <sys/stat.h>
28 #include <unistd.h>
29 #include <fcntl.h>
30 #include <sched.h>
31
32 #include <lttngerr.h>
33
34 /*
35 * We allocate a stack of twice this size and point in the middle of it
36 * to support archs with stack growing up and down.
37 */
38 #define CHILD_STACK_SIZE 2048
39
40 struct run_as_data {
41 int (*cmd)(void *data);
42 void *data;
43 uid_t uid;
44 gid_t gid;
45 int retval_pipe;
46 };
47
48 struct mkdir_data {
49 const char *path;
50 mode_t mode;
51 };
52
53 struct open_data {
54 const char *path;
55 int flags;
56 mode_t mode;
57 };
58
59 /*
60 * Create recursively directory using the FULL path.
61 */
62 static
63 int _mkdir_recursive(void *_data)
64 {
65 struct mkdir_data *data = _data;
66 const char *path;
67 char *p, tmp[PATH_MAX];
68 struct stat statbuf;
69 mode_t mode;
70 size_t len;
71 int ret;
72
73 path = data->path;
74 mode = data->mode;
75
76 ret = snprintf(tmp, sizeof(tmp), "%s", path);
77 if (ret < 0) {
78 PERROR("snprintf mkdir");
79 goto error;
80 }
81
82 len = ret;
83 if (tmp[len - 1] == '/') {
84 tmp[len - 1] = 0;
85 }
86
87 for (p = tmp + 1; *p; p++) {
88 if (*p == '/') {
89 *p = 0;
90 ret = stat(tmp, &statbuf);
91 if (ret < 0) {
92 ret = mkdir(tmp, mode);
93 if (ret < 0) {
94 if (!(errno == EEXIST)) {
95 PERROR("mkdir recursive");
96 ret = -errno;
97 goto error;
98 }
99 }
100 }
101 *p = '/';
102 }
103 }
104
105 ret = mkdir(tmp, mode);
106 if (ret < 0) {
107 if (!(errno == EEXIST)) {
108 PERROR("mkdir recursive last piece");
109 ret = -errno;
110 } else {
111 ret = 0;
112 }
113 }
114
115 error:
116 return ret;
117 }
118
119 static
120 int _mkdir(void *_data)
121 {
122 struct mkdir_data *data = _data;
123 return mkdir(data->path, data->mode);
124 }
125
126 static
127 int _open(void *_data)
128 {
129 struct open_data *data = _data;
130 return open(data->path, data->flags, data->mode);
131 }
132
133 static
134 int child_run_as(void *_data)
135 {
136 struct run_as_data *data = _data;
137 size_t writelen, writeleft, index;
138 union {
139 int i;
140 char c[sizeof(int)];
141 } sendret;
142 int ret;
143
144 /*
145 * Child: it is safe to drop egid and euid while sharing the
146 * file descriptors with the parent process, since we do not
147 * drop "uid": therefore, the user we are dropping egid/euid to
148 * cannot attach to this process with, e.g. ptrace, nor map this
149 * process memory.
150 */
151 ret = setegid(data->gid);
152 if (ret < 0) {
153 perror("setegid");
154 exit(EXIT_FAILURE);
155 }
156 ret = seteuid(data->uid);
157 if (ret < 0) {
158 perror("seteuid");
159 exit(EXIT_FAILURE);
160 }
161 /*
162 * Also set umask to 0 for mkdir executable bit.
163 */
164 umask(0);
165 sendret.i = (*data->cmd)(data->data);
166 /* send back return value */
167 writeleft = sizeof(sendret);
168 index = 0;
169 do {
170 writelen = write(data->retval_pipe, &sendret.c[index],
171 writeleft);
172 if (writelen < 0) {
173 perror("write");
174 exit(EXIT_FAILURE);
175 }
176 writeleft -= writelen;
177 index += writelen;
178 } while (writeleft > 0);
179
180 exit(EXIT_SUCCESS);
181 }
182
183 static
184 int run_as(int (*cmd)(void *data), void *data, uid_t uid, gid_t gid)
185 {
186 struct run_as_data run_as_data;
187 int ret = 0;
188 int status;
189 pid_t pid;
190 char *child_stack;
191 int retval_pipe[2];
192 ssize_t readlen, readleft, index;
193 union {
194 int i;
195 char c[sizeof(int)];
196 } retval;
197
198 /*
199 * If we are non-root, we can only deal with our own uid.
200 */
201 if (geteuid() != 0) {
202 if (uid != geteuid()) {
203 ERR("Client (%d)/Server (%d) UID mismatch (and sessiond is not root)",
204 uid, geteuid());
205 return -EPERM;
206 }
207 }
208
209 child_stack = malloc(CHILD_STACK_SIZE * 2);
210 if (!child_stack) {
211 return -ENOMEM;
212 }
213 ret = pipe(retval_pipe);
214 if (ret < 0) {
215 perror("pipe");
216 goto end;
217 }
218 run_as_data.data = data;
219 run_as_data.cmd = cmd;
220 run_as_data.uid = uid;
221 run_as_data.gid = gid;
222 run_as_data.retval_pipe = retval_pipe[1]; /* write end */
223
224 pid = clone(child_run_as, child_stack + CHILD_STACK_SIZE,
225 CLONE_FILES | SIGCHLD, &run_as_data, NULL);
226 if (pid < 0) {
227 perror("clone");
228 ret = pid;
229 goto close_pipe;
230 }
231 /* receive return value */
232 readleft = sizeof(retval);
233 index = 0;
234 do {
235 readlen = read(retval_pipe[0], &retval.c[index], readleft);
236 if (readlen < 0) {
237 perror("read");
238 ret = -1;
239 break;
240 }
241 readleft -= readlen;
242 index += readlen;
243 } while (readleft > 0);
244
245 /*
246 * Parent: wait for child to return, in which case the
247 * shared memory map will have been created.
248 */
249 pid = wait(&status);
250 if (pid < 0 || !WIFEXITED(status) || WEXITSTATUS(status) != 0) {
251 perror("wait");
252 ret = -1;
253 }
254 close_pipe:
255 close(retval_pipe[0]);
256 close(retval_pipe[1]);
257 end:
258 free(child_stack);
259 return retval.i;
260 }
261
262 int mkdir_recursive_run_as(const char *path, mode_t mode, uid_t uid, gid_t gid)
263 {
264 struct mkdir_data data;
265
266 DBG3("mkdir() recursive %s with mode %d for uid %d and gid %d",
267 path, mode, uid, gid);
268 data.path = path;
269 data.mode = mode;
270 return run_as(_mkdir_recursive, &data, uid, gid);
271 }
272
273 int mkdir_run_as(const char *path, mode_t mode, uid_t uid, gid_t gid)
274 {
275 struct mkdir_data data;
276
277 DBG3("mkdir() %s with mode %d for uid %d and gid %d",
278 path, mode, uid, gid);
279 data.path = path;
280 data.mode = mode;
281 return run_as(_mkdir, &data, uid, gid);
282 }
283
284 /*
285 * Note: open_run_as is currently not working. We'd need to pass the fd
286 * opened in the child to the parent.
287 */
288 int open_run_as(const char *path, int flags, mode_t mode, uid_t uid, gid_t gid)
289 {
290 struct open_data data;
291
292 data.path = path;
293 data.flags = flags;
294 data.mode = mode;
295 return run_as(_open, &data, uid, gid);
296 }
This page took 0.035667 seconds and 4 git commands to generate.