Implement open_run_as, using clone() CLONE_FILES
[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 return (*cmd)(data);
208 }
209
210 child_stack = malloc(CHILD_STACK_SIZE * 2);
211 if (!child_stack) {
212 return -ENOMEM;
213 }
214 ret = pipe(retval_pipe);
215 if (ret < 0) {
216 perror("pipe");
217 goto end;
218 }
219 run_as_data.data = data;
220 run_as_data.cmd = cmd;
221 run_as_data.uid = uid;
222 run_as_data.gid = gid;
223 run_as_data.retval_pipe = retval_pipe[1]; /* write end */
224
225 pid = clone(child_run_as, child_stack + CHILD_STACK_SIZE,
226 CLONE_FILES | SIGCHLD, &run_as_data, NULL);
227 if (pid < 0) {
228 perror("clone");
229 ret = pid;
230 goto close_pipe;
231 }
232 /* receive return value */
233 readleft = sizeof(retval);
234 index = 0;
235 do {
236 readlen = read(retval_pipe[0], &retval.c[index], readleft);
237 if (readlen < 0) {
238 perror("read");
239 ret = -1;
240 break;
241 }
242 readleft -= readlen;
243 index += readlen;
244 } while (readleft > 0);
245
246 /*
247 * Parent: wait for child to return, in which case the
248 * shared memory map will have been created.
249 */
250 pid = wait(&status);
251 if (pid < 0 || !WIFEXITED(status) || WEXITSTATUS(status) != 0) {
252 perror("wait");
253 ret = -1;
254 }
255 close_pipe:
256 close(retval_pipe[0]);
257 close(retval_pipe[1]);
258 end:
259 free(child_stack);
260 return retval.i;
261 }
262
263 int mkdir_recursive_run_as(const char *path, mode_t mode, uid_t uid, gid_t gid)
264 {
265 struct mkdir_data data;
266
267 DBG3("mkdir() recursive %s with mode %d for uid %d and gid %d",
268 path, mode, uid, gid);
269 data.path = path;
270 data.mode = mode;
271 return run_as(_mkdir_recursive, &data, uid, gid);
272 }
273
274 int mkdir_run_as(const char *path, mode_t mode, uid_t uid, gid_t gid)
275 {
276 struct mkdir_data data;
277
278 DBG3("mkdir() %s with mode %d for uid %d and gid %d",
279 path, mode, uid, gid);
280 data.path = path;
281 data.mode = mode;
282 return run_as(_mkdir, &data, uid, gid);
283 }
284
285 /*
286 * Note: open_run_as is currently not working. We'd need to pass the fd
287 * opened in the child to the parent.
288 */
289 int open_run_as(const char *path, int flags, mode_t mode, uid_t uid, gid_t gid)
290 {
291 struct open_data data;
292
293 data.path = path;
294 data.flags = flags;
295 data.mode = mode;
296 return run_as(_open, &data, uid, gid);
297 }
This page took 0.035617 seconds and 5 git commands to generate.