Only seteuid/setegid if they differ from current values
[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 if (data->gid != getegid()) {
152 ret = setegid(data->gid);
153 if (ret < 0) {
154 perror("setegid");
155 exit(EXIT_FAILURE);
156 }
157 }
158 if (data->uid != geteuid()) {
159 ret = seteuid(data->uid);
160 if (ret < 0) {
161 perror("seteuid");
162 exit(EXIT_FAILURE);
163 }
164 }
165 /*
166 * Also set umask to 0 for mkdir executable bit.
167 */
168 umask(0);
169 sendret.i = (*data->cmd)(data->data);
170 /* send back return value */
171 writeleft = sizeof(sendret);
172 index = 0;
173 do {
174 writelen = write(data->retval_pipe, &sendret.c[index],
175 writeleft);
176 if (writelen < 0) {
177 perror("write");
178 exit(EXIT_FAILURE);
179 }
180 writeleft -= writelen;
181 index += writelen;
182 } while (writeleft > 0);
183
184 exit(EXIT_SUCCESS);
185 }
186
187 static
188 int run_as(int (*cmd)(void *data), void *data, uid_t uid, gid_t gid)
189 {
190 struct run_as_data run_as_data;
191 int ret = 0;
192 int status;
193 pid_t pid;
194 char *child_stack;
195 int retval_pipe[2];
196 ssize_t readlen, readleft, index;
197 union {
198 int i;
199 char c[sizeof(int)];
200 } retval;
201
202 /*
203 * If we are non-root, we can only deal with our own uid.
204 */
205 if (geteuid() != 0) {
206 if (uid != geteuid()) {
207 ERR("Client (%d)/Server (%d) UID mismatch (and sessiond is not root)",
208 uid, geteuid());
209 return -EPERM;
210 }
211 }
212
213 child_stack = malloc(CHILD_STACK_SIZE * 2);
214 if (!child_stack) {
215 return -ENOMEM;
216 }
217 ret = pipe(retval_pipe);
218 if (ret < 0) {
219 perror("pipe");
220 goto end;
221 }
222 run_as_data.data = data;
223 run_as_data.cmd = cmd;
224 run_as_data.uid = uid;
225 run_as_data.gid = gid;
226 run_as_data.retval_pipe = retval_pipe[1]; /* write end */
227
228 pid = clone(child_run_as, child_stack + CHILD_STACK_SIZE,
229 CLONE_FILES | SIGCHLD, &run_as_data, NULL);
230 if (pid < 0) {
231 perror("clone");
232 ret = pid;
233 goto close_pipe;
234 }
235 /* receive return value */
236 readleft = sizeof(retval);
237 index = 0;
238 do {
239 readlen = read(retval_pipe[0], &retval.c[index], readleft);
240 if (readlen < 0) {
241 perror("read");
242 ret = -1;
243 break;
244 }
245 readleft -= readlen;
246 index += readlen;
247 } while (readleft > 0);
248
249 /*
250 * Parent: wait for child to return, in which case the
251 * shared memory map will have been created.
252 */
253 pid = wait(&status);
254 if (pid < 0 || !WIFEXITED(status) || WEXITSTATUS(status) != 0) {
255 perror("wait");
256 ret = -1;
257 }
258 close_pipe:
259 close(retval_pipe[0]);
260 close(retval_pipe[1]);
261 end:
262 free(child_stack);
263 return retval.i;
264 }
265
266 int mkdir_recursive_run_as(const char *path, mode_t mode, uid_t uid, gid_t gid)
267 {
268 struct mkdir_data data;
269
270 DBG3("mkdir() recursive %s with mode %d for uid %d and gid %d",
271 path, mode, uid, gid);
272 data.path = path;
273 data.mode = mode;
274 return run_as(_mkdir_recursive, &data, uid, gid);
275 }
276
277 int mkdir_run_as(const char *path, mode_t mode, uid_t uid, gid_t gid)
278 {
279 struct mkdir_data data;
280
281 DBG3("mkdir() %s with mode %d for uid %d and gid %d",
282 path, mode, uid, gid);
283 data.path = path;
284 data.mode = mode;
285 return run_as(_mkdir, &data, uid, gid);
286 }
287
288 /*
289 * Note: open_run_as is currently not working. We'd need to pass the fd
290 * opened in the child to the parent.
291 */
292 int open_run_as(const char *path, int flags, mode_t mode, uid_t uid, gid_t gid)
293 {
294 struct open_data data;
295
296 data.path = path;
297 data.flags = flags;
298 data.mode = mode;
299 return run_as(_open, &data, uid, gid);
300 }
This page took 0.034244 seconds and 4 git commands to generate.