librunas: clone another process also if not root
[lttng-tools.git] / librunas / runas.c
CommitLineData
60b6c79c
MD
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>
c2b75c49 30#include <sched.h>
60b6c79c
MD
31
32#include <lttngerr.h>
33
c2b75c49
MD
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
40struct 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
60b6c79c
MD
48struct mkdir_data {
49 const char *path;
50 mode_t mode;
51};
52
53struct 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 */
62static
63int _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
115error:
116 return ret;
117}
118
119static
120int _mkdir(void *_data)
121{
122 struct mkdir_data *data = _data;
123 return mkdir(data->path, data->mode);
124}
125
126static
127int _open(void *_data)
128{
129 struct open_data *data = _data;
130 return open(data->path, data->flags, data->mode);
131}
132
c2b75c49
MD
133static
134int 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
60b6c79c
MD
183static
184int run_as(int (*cmd)(void *data), void *data, uid_t uid, gid_t gid)
185{
c2b75c49 186 struct run_as_data run_as_data;
60b6c79c 187 int ret = 0;
c2b75c49 188 int status;
60b6c79c 189 pid_t pid;
c2b75c49
MD
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;
60b6c79c
MD
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 }
60b6c79c
MD
207 }
208
c2b75c49
MD
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");
60b6c79c 216 goto end;
c2b75c49
MD
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;
60b6c79c 240 }
c2b75c49
MD
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;
60b6c79c 253 }
c2b75c49
MD
254close_pipe:
255 close(retval_pipe[0]);
256 close(retval_pipe[1]);
60b6c79c 257end:
c2b75c49
MD
258 free(child_stack);
259 return retval.i;
60b6c79c
MD
260}
261
262int 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
273int 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 */
288int open_run_as(const char *path, int flags, mode_t mode, uid_t uid, gid_t gid)
289{
c2b75c49
MD
290 struct open_data data;
291
60b6c79c
MD
292 data.path = path;
293 data.flags = flags;
294 data.mode = mode;
295 return run_as(_open, &data, uid, gid);
60b6c79c 296}
This page took 0.033323 seconds and 4 git commands to generate.