1 |
jpp |
1.1 |
diff -up sudo-1.8.6p3/src/sesh.c.sudoedit-selinux sudo-1.8.6p3/src/sesh.c |
2 |
|
|
--- sudo-1.8.6p3/src/sesh.c.sudoedit-selinux 2012-09-18 15:56:30.000000000 +0200 |
3 |
|
|
+++ sudo-1.8.6p3/src/sesh.c 2012-09-25 16:06:33.408584649 +0200 |
4 |
|
|
@@ -34,6 +34,10 @@ |
5 |
|
|
# include "compat/stdbool.h" |
6 |
|
|
#endif /* HAVE_STDBOOL_H */ |
7 |
|
|
|
8 |
|
|
+#include <sys/stat.h> |
9 |
|
|
+#include <fcntl.h> |
10 |
|
|
+#include <errno.h> |
11 |
|
|
+ |
12 |
|
|
#include "missing.h" |
13 |
|
|
#include "alloc.h" |
14 |
|
|
#include "error.h" |
15 |
|
|
@@ -43,6 +47,16 @@ |
16 |
|
|
#include "sudo_exec.h" |
17 |
|
|
#include "sudo_plugin.h" |
18 |
|
|
|
19 |
|
|
+/* |
20 |
|
|
+ * Return codes: |
21 |
|
|
+ * EXIT_FAILURE ... unspecified error |
22 |
|
|
+ * 0 ... everything ok |
23 |
|
|
+ * 30 ... invalid -e arg value |
24 |
|
|
+ * 31 ... odd number of paths |
25 |
|
|
+ * 32 ... copy operation failed, no files copied |
26 |
|
|
+ * 33 ... copy operation failed, some files copied |
27 |
|
|
+ */ |
28 |
|
|
+ |
29 |
|
|
sudo_conv_t sudo_conv; /* NULL in non-plugin */ |
30 |
|
|
|
31 |
|
|
/* |
32 |
|
|
@@ -77,19 +91,134 @@ main(int argc, char *argv[], char *envp[ |
33 |
|
|
if ((cp = strrchr(argv[0], '-')) != NULL && cp != argv[0]) |
34 |
|
|
noexec = strcmp(cp, "-noexec") == 0; |
35 |
|
|
|
36 |
|
|
- /* Shift argv and make a copy of the command to execute. */ |
37 |
|
|
- argv++; |
38 |
|
|
- argc--; |
39 |
|
|
- cmnd = estrdup(argv[0]); |
40 |
|
|
- |
41 |
|
|
- /* If invoked as a login shell, modify argv[0] accordingly. */ |
42 |
|
|
- if (argv[-1][0] == '-') { |
43 |
|
|
- if ((cp = strrchr(argv[0], '/')) == NULL) |
44 |
|
|
- cp = argv[0]; |
45 |
|
|
- *cp = '-'; |
46 |
|
|
+ /* check the first argument, if it's `-e' then we are in sudoedit mode */ |
47 |
|
|
+ if (strncmp(argv[1], "-e", 3) == 0) { |
48 |
|
|
+ int fd_src, fd_dst, post, n, ret = -1; |
49 |
|
|
+ ssize_t nread, nwritten; |
50 |
|
|
+ char *path_src, *path_dst, buf[BUFSIZ]; |
51 |
|
|
+ |
52 |
|
|
+ if (argc < 3) |
53 |
|
|
+ return EXIT_FAILURE; |
54 |
|
|
+ |
55 |
|
|
+ /* |
56 |
|
|
+ * We need to know whether we are performing the copy operation |
57 |
|
|
+ * before or after the editing. Without this we would not know |
58 |
|
|
+ * which files are temporary and which are the originals. |
59 |
|
|
+ * post = 0 ... before |
60 |
|
|
+ * post = 1 ... after |
61 |
|
|
+ */ |
62 |
|
|
+ if (strncmp(argv[2], "0", 2) == 0) |
63 |
|
|
+ post = 0; |
64 |
|
|
+ else if (strncmp(argv[2], "1", 2) == 0) |
65 |
|
|
+ post = 1; |
66 |
|
|
+ else /* invalid value */ |
67 |
|
|
+ return 30; |
68 |
|
|
+ |
69 |
|
|
+ /* align argv & argc to the beggining of the file list */ |
70 |
|
|
+ argv += 3; |
71 |
|
|
+ argc -= 3; |
72 |
|
|
+ |
73 |
|
|
+ /* no files specified, nothing to do */ |
74 |
|
|
+ if (argc == 0) |
75 |
|
|
+ return 0; |
76 |
|
|
+ /* odd number of paths specified */ |
77 |
|
|
+ if (argc % 2 == 1) |
78 |
|
|
+ return 31; |
79 |
|
|
+ |
80 |
|
|
+ for (n = 0; n < argc - 1; n += 2) { |
81 |
|
|
+ path_src = argv[n]; |
82 |
|
|
+ path_dst = argv[n+1]; |
83 |
|
|
+ /* |
84 |
|
|
+ * Try to open the source file for reading. If it |
85 |
|
|
+ * doesn't exist, it's ok, we'll create an empty |
86 |
|
|
+ * destination file. |
87 |
|
|
+ */ |
88 |
|
|
+ if ((fd_src = open(path_src, O_RDONLY, 0600)) < 0) { |
89 |
|
|
+ if (errno == ENOENT) { |
90 |
|
|
+ /* new file */ |
91 |
|
|
+ } else { |
92 |
|
|
+ warning(_("open(%s)"), path_src); |
93 |
|
|
+ if (post) { |
94 |
|
|
+ ret = 33; |
95 |
|
|
+ goto nocleanup; |
96 |
|
|
+ } else |
97 |
|
|
+ goto cleanup_0; |
98 |
|
|
+ } |
99 |
|
|
+ } |
100 |
|
|
+ |
101 |
|
|
+ /* |
102 |
|
|
+ * Use O_EXCL if we are not in the post editing stage |
103 |
|
|
+ * so that it's ensured that the temporary files are |
104 |
|
|
+ * created by us and that we are not opening any sym- |
105 |
|
|
+ * links. |
106 |
|
|
+ */ |
107 |
|
|
+ if ((fd_dst = open(path_dst, (post ? 0 : O_EXCL) | |
108 |
|
|
+ O_WRONLY|O_TRUNC|O_CREAT, post ? 0644 : 0600)) < 0) |
109 |
|
|
+ { |
110 |
|
|
+ /* error - cleanup */ |
111 |
|
|
+ warning(_("open(%s%s)"), path_dst, post ? "" : ", O_EXCL"); |
112 |
|
|
+ if (post) { |
113 |
|
|
+ ret = 33; |
114 |
|
|
+ goto nocleanup; |
115 |
|
|
+ } else |
116 |
|
|
+ goto cleanup_0; |
117 |
|
|
+ } |
118 |
|
|
+ |
119 |
|
|
+ if (fd_src != -1) { |
120 |
|
|
+ while ((nread = read(fd_src, buf, sizeof(buf))) > 0) { |
121 |
|
|
+ if ((nwritten = write(fd_dst, buf, nread)) != nread) { |
122 |
|
|
+ warning(_("write")); |
123 |
|
|
+ if (post) { |
124 |
|
|
+ ret = 33; |
125 |
|
|
+ goto nocleanup; |
126 |
|
|
+ } else |
127 |
|
|
+ goto cleanup_0; |
128 |
|
|
+ } |
129 |
|
|
+ } |
130 |
|
|
+ } |
131 |
|
|
+ |
132 |
|
|
+ if (fd_dst != -1) |
133 |
|
|
+ close(fd_dst); |
134 |
|
|
+ if (fd_src != -1) |
135 |
|
|
+ close(fd_src); |
136 |
|
|
+ fd_dst = fd_src = -1; |
137 |
|
|
+ } |
138 |
|
|
+ |
139 |
|
|
+ ret = 0; |
140 |
|
|
+ /* remove temporary files (post=1) */ |
141 |
|
|
+ for (n = 0; n < argc - 1; n += 2) |
142 |
|
|
+ unlink(argv[n]); |
143 |
|
|
+nocleanup: |
144 |
|
|
+ if (fd_dst != -1) |
145 |
|
|
+ close(fd_dst); |
146 |
|
|
+ if (fd_src != -1) |
147 |
|
|
+ close(fd_src); |
148 |
|
|
+ _exit(ret); |
149 |
|
|
+cleanup_0: |
150 |
|
|
+ /* remove temporary files (post=0) */ |
151 |
|
|
+ for (n = 0; n < argc - 1; n += 2) |
152 |
|
|
+ unlink(argv[n+1]); |
153 |
|
|
+ if (fd_dst != -1) |
154 |
|
|
+ close(fd_dst); |
155 |
|
|
+ if (fd_src != -1) |
156 |
|
|
+ close(fd_src); |
157 |
|
|
+ _exit(32); |
158 |
|
|
+ } else { |
159 |
|
|
+ |
160 |
|
|
+ /* Shift argv and make a copy of the command to execute. */ |
161 |
|
|
+ argv++; |
162 |
|
|
+ argc--; |
163 |
|
|
+ cmnd = estrdup(argv[0]); |
164 |
|
|
+ |
165 |
|
|
+ /* If invoked as a login shell, modify argv[0] accordingly. */ |
166 |
|
|
+ if (argv[-1][0] == '-') { |
167 |
|
|
+ if ((cp = strrchr(argv[0], '/')) == NULL) |
168 |
|
|
+ cp = argv[0]; |
169 |
|
|
+ *cp = '-'; |
170 |
|
|
+ } |
171 |
|
|
+ sudo_execve(cmnd, argv, envp, noexec); |
172 |
|
|
+ warning(_("unable to execute %s"), argv[0]); |
173 |
|
|
+ sudo_debug_exit_int(__func__, __FILE__, __LINE__, sudo_debug_subsys, EXIT_FAILURE); |
174 |
|
|
} |
175 |
|
|
- sudo_execve(cmnd, argv, envp, noexec); |
176 |
|
|
- warning(_("unable to execute %s"), argv[0]); |
177 |
|
|
- sudo_debug_exit_int(__func__, __FILE__, __LINE__, sudo_debug_subsys, EXIT_FAILURE); |
178 |
|
|
_exit(EXIT_FAILURE); |
179 |
|
|
} |
180 |
|
|
diff -up sudo-1.8.6p3/src/sudo.c.sudoedit-selinux sudo-1.8.6p3/src/sudo.c |
181 |
|
|
--- sudo-1.8.6p3/src/sudo.c.sudoedit-selinux 2012-09-18 15:57:43.000000000 +0200 |
182 |
|
|
+++ sudo-1.8.6p3/src/sudo.c 2012-09-25 16:04:36.687422997 +0200 |
183 |
|
|
@@ -915,6 +915,10 @@ exec_setup(struct command_details *detai |
184 |
|
|
if (selinux_setup(details->selinux_role, details->selinux_type, |
185 |
|
|
ptyname ? ptyname : user_details.tty, ptyfd) == -1) |
186 |
|
|
goto done; |
187 |
|
|
+ if (details->flags & CD_SUDOEDIT_COPY) { |
188 |
|
|
+ rval = true; |
189 |
|
|
+ goto done; |
190 |
|
|
+ } |
191 |
|
|
} |
192 |
|
|
#endif |
193 |
|
|
|
194 |
|
|
@@ -1116,6 +1120,8 @@ run_command(struct command_details *deta |
195 |
|
|
break; |
196 |
|
|
case CMD_WSTATUS: |
197 |
|
|
/* Command ran, exited or was killed. */ |
198 |
|
|
+ if (details->flags & CD_SUDOEDIT_COPY) |
199 |
|
|
+ break; |
200 |
|
|
sudo_debug_printf(SUDO_DEBUG_DEBUG, |
201 |
|
|
"calling policy close with wait status %d", cstat.val); |
202 |
|
|
policy_close(&policy_plugin, cstat.val, 0); |
203 |
|
|
diff -up sudo-1.8.6p3/src/sudo_edit.c.sudoedit-selinux sudo-1.8.6p3/src/sudo_edit.c |
204 |
|
|
--- sudo-1.8.6p3/src/sudo_edit.c.sudoedit-selinux 2012-09-18 15:56:30.000000000 +0200 |
205 |
|
|
+++ sudo-1.8.6p3/src/sudo_edit.c 2012-09-25 16:06:19.108564255 +0200 |
206 |
|
|
@@ -49,11 +49,284 @@ |
207 |
|
|
#if TIME_WITH_SYS_TIME |
208 |
|
|
# include <time.h> |
209 |
|
|
#endif |
210 |
|
|
+#ifdef HAVE_SELINUX |
211 |
|
|
+# include <selinux/selinux.h> |
212 |
|
|
+#endif |
213 |
|
|
|
214 |
|
|
#include "sudo.h" |
215 |
|
|
|
216 |
|
|
#if defined(HAVE_SETRESUID) || defined(HAVE_SETREUID) || defined(HAVE_SETEUID) |
217 |
|
|
|
218 |
|
|
+struct tempfile { |
219 |
|
|
+ char *tfile; |
220 |
|
|
+ char *ofile; |
221 |
|
|
+ struct timeval omtim; |
222 |
|
|
+ off_t osize; |
223 |
|
|
+}; |
224 |
|
|
+ |
225 |
|
|
+static int |
226 |
|
|
+selinux_edit_copy(struct command_details *command_details, struct tempfile *tf, char **files, int nfiles, const char *tmpdir, int tmplen, int tval_isset) |
227 |
|
|
+{ |
228 |
|
|
+ char **sesh_args; |
229 |
|
|
+ int i, sesh_nargs, ret; |
230 |
|
|
+ struct command_details sesh_details; |
231 |
|
|
+ debug_decl(selinux_edit_copy, SUDO_DEBUG_EDIT); |
232 |
|
|
+ |
233 |
|
|
+ /* Prepare selinux stuff (setexeccon) */ |
234 |
|
|
+ if (selinux_setup(command_details->selinux_role, |
235 |
|
|
+ command_details->selinux_type, NULL, -1) != 0) |
236 |
|
|
+ return -1; |
237 |
|
|
+ |
238 |
|
|
+ if (nfiles < 1) |
239 |
|
|
+ return 1; |
240 |
|
|
+ |
241 |
|
|
+ /* Construct common args for sesh */ |
242 |
|
|
+ memcpy(&sesh_details, command_details, sizeof(sesh_details)); |
243 |
|
|
+ sesh_details.command = _PATH_SUDO_SESH; |
244 |
|
|
+ sesh_details.flags |= CD_SUDOEDIT_COPY; |
245 |
|
|
+ |
246 |
|
|
+ sesh_nargs = (nfiles * 2) + 4 + 1; |
247 |
|
|
+ sesh_args = (char **)emalloc2(sesh_nargs, sizeof(char *)); |
248 |
|
|
+ sesh_args++; |
249 |
|
|
+ sesh_args[0] = "sesh"; |
250 |
|
|
+ sesh_args[1] = "-e"; |
251 |
|
|
+ |
252 |
|
|
+ if (files != NULL) { |
253 |
|
|
+ sesh_args[2] = "0"; |
254 |
|
|
+ |
255 |
|
|
+ for (i = 2; i < nfiles+2; ++i) { |
256 |
|
|
+ sesh_args[2*i-1] = files[i-2]; |
257 |
|
|
+ tf[i-2].ofile = files[i-2]; |
258 |
|
|
+ /* |
259 |
|
|
+ * O_CREAT | O_EXCL is used in the sesh helper, so the |
260 |
|
|
+ * usage of the tempnam function here is safe. |
261 |
|
|
+ */ |
262 |
|
|
+ sesh_args[2*i] = tempnam(tmpdir, "sudo."); |
263 |
|
|
+ tf[i-2].tfile = sesh_args[2*i]; |
264 |
|
|
+ //tf[i-2].omtim = 0; |
265 |
|
|
+ tf[i-2].osize = 0; |
266 |
|
|
+ } |
267 |
|
|
+ |
268 |
|
|
+ sesh_args[2*i-1] = NULL; |
269 |
|
|
+ |
270 |
|
|
+ /* Run sesh -e 0 <o1> <t1> ... <on> <tn> */ |
271 |
|
|
+ sesh_details.argv = sesh_args; |
272 |
|
|
+ switch(run_command(&sesh_details)) { |
273 |
|
|
+ case 0: |
274 |
|
|
+ break; |
275 |
|
|
+ case 31: |
276 |
|
|
+ error(1, _("sesh: internal error: odd number of paths")); |
277 |
|
|
+ case 32: |
278 |
|
|
+ error(1, _("sesh: unable to create temporary files")); |
279 |
|
|
+ } |
280 |
|
|
+ |
281 |
|
|
+ /* Chown to user's UID so he can edit the temporary files */ |
282 |
|
|
+ for (i = 2; i < nfiles+2; ++i) { |
283 |
|
|
+ if (chown(tf[i-2].tfile, user_details.uid, user_details.gid) != 0) { |
284 |
|
|
+ warning("Unable to chown(%s) to %d:%d for editing", |
285 |
|
|
+ tf[i-2].tfile, user_details.uid, user_details.gid); |
286 |
|
|
+ } |
287 |
|
|
+ } |
288 |
|
|
+ } else { |
289 |
|
|
+ sesh_args[2] = "1"; |
290 |
|
|
+ |
291 |
|
|
+ /* Construct args for sesh -e 1 */ |
292 |
|
|
+ for (i = 2; i < nfiles+2; ++i) { |
293 |
|
|
+ sesh_args[2*i-1] = tf[i-2].tfile; |
294 |
|
|
+ sesh_args[2*i] = tf[i-2].ofile; |
295 |
|
|
+ |
296 |
|
|
+ if (chown(tf[i-2].tfile, sesh_details.uid, sesh_details.gid) != 0) { |
297 |
|
|
+ warning("Unable to chown(%s) back to %d:%d", |
298 |
|
|
+ tf[i-2].tfile, sesh_details.uid, sesh_details.gid); |
299 |
|
|
+ } |
300 |
|
|
+ } |
301 |
|
|
+ |
302 |
|
|
+ sesh_args[2*i-1] = NULL; |
303 |
|
|
+ |
304 |
|
|
+ /* Run sesh -e 1 <t1> <o1> ... <tn> <on> */ |
305 |
|
|
+ sesh_details.argv = sesh_args; |
306 |
|
|
+ switch(run_command(&sesh_details)) { |
307 |
|
|
+ case 0: |
308 |
|
|
+ break; |
309 |
|
|
+ case 32: |
310 |
|
|
+ warning(_("Copying the temporary files back to its original place failed. The files were left in %s"), tmpdir); |
311 |
|
|
+ break; |
312 |
|
|
+ case 33: |
313 |
|
|
+ warning(_("Copying of some of the temporary files back to its original place failed and they were left in %s"), |
314 |
|
|
+ tmpdir); |
315 |
|
|
+ break; |
316 |
|
|
+ } |
317 |
|
|
+ } |
318 |
|
|
+ |
319 |
|
|
+ return (nfiles); |
320 |
|
|
+} |
321 |
|
|
+ |
322 |
|
|
+static void switch_user(uid_t euid, gid_t egid, int ngroups, GETGROUPS_T *groups); |
323 |
|
|
+ |
324 |
|
|
+static int sudo_edit_copy(struct command_details *command_details, struct tempfile *tf, char **files, int nfiles, const char *tmpdir, int tmplen, int tval_isset) |
325 |
|
|
+{ |
326 |
|
|
+ int i, j, tfd, ofd, rc; |
327 |
|
|
+ char *cp, *suff, buf[BUFSIZ]; |
328 |
|
|
+ ssize_t nwritten, nread; |
329 |
|
|
+ struct stat sb; |
330 |
|
|
+ struct timeval tv; |
331 |
|
|
+ debug_decl(sudo_edit_copy, SUDO_DEBUG_EDIT); |
332 |
|
|
+ |
333 |
|
|
+ if (files != NULL) { |
334 |
|
|
+ /* Create temporary copies */ |
335 |
|
|
+ for (i = 0, j = 0; i < nfiles; i++) { |
336 |
|
|
+ rc = -1; |
337 |
|
|
+ switch_user(command_details->euid, command_details->egid, |
338 |
|
|
+ command_details->ngroups, command_details->groups); |
339 |
|
|
+ if ((ofd = open(files[i], O_RDONLY, 0644)) != -1 || errno == ENOENT) { |
340 |
|
|
+ if (ofd == -1) { |
341 |
|
|
+ zero_bytes(&sb, sizeof(sb)); /* new file */ |
342 |
|
|
+ rc = 0; |
343 |
|
|
+ } else { |
344 |
|
|
+ rc = fstat(ofd, &sb); |
345 |
|
|
+ } |
346 |
|
|
+ } |
347 |
|
|
+ switch_user(ROOT_UID, user_details.egid, |
348 |
|
|
+ user_details.ngroups, user_details.groups); |
349 |
|
|
+ if (rc || (ofd != -1 && !S_ISREG(sb.st_mode))) { |
350 |
|
|
+ if (rc) |
351 |
|
|
+ warning("%s", files[i]); |
352 |
|
|
+ else |
353 |
|
|
+ warningx(_("%s: not a regular file"), files[i]); |
354 |
|
|
+ if (ofd != -1) |
355 |
|
|
+ close(ofd); |
356 |
|
|
+ continue; |
357 |
|
|
+ } |
358 |
|
|
+ tf[j].ofile = files[i]; |
359 |
|
|
+ tf[j].osize = sb.st_size; |
360 |
|
|
+ mtim_get(&sb, &tf[j].omtim); |
361 |
|
|
+ if ((cp = strrchr(tf[j].ofile, '/')) != NULL) |
362 |
|
|
+ cp++; |
363 |
|
|
+ else |
364 |
|
|
+ cp = tf[j].ofile; |
365 |
|
|
+ suff = strrchr(cp, '.'); |
366 |
|
|
+ if (suff != NULL) { |
367 |
|
|
+ easprintf(&tf[j].tfile, "%.*s/%.*sXXXXXXXX%s", tmplen, tmpdir, |
368 |
|
|
+ (int)(size_t)(suff - cp), cp, suff); |
369 |
|
|
+ } else { |
370 |
|
|
+ easprintf(&tf[j].tfile, "%.*s/%s.XXXXXXXX", tmplen, tmpdir, cp); |
371 |
|
|
+ } |
372 |
|
|
+ if (seteuid(user_details.uid) != 0) |
373 |
|
|
+ error(1, "seteuid(%d)", (int)user_details.uid); |
374 |
|
|
+ tfd = mkstemps(tf[j].tfile, suff ? strlen(suff) : 0); |
375 |
|
|
+ if (seteuid(ROOT_UID) != 0) |
376 |
|
|
+ error(1, "seteuid(ROOT_UID)"); |
377 |
|
|
+ if (tfd == -1) { |
378 |
|
|
+ warning("mkstemps"); |
379 |
|
|
+ goto cleanup; |
380 |
|
|
+ } |
381 |
|
|
+ if (ofd != -1) { |
382 |
|
|
+ while ((nread = read(ofd, buf, sizeof(buf))) != 0) { |
383 |
|
|
+ if ((nwritten = write(tfd, buf, nread)) != nread) { |
384 |
|
|
+ if (nwritten == -1) |
385 |
|
|
+ warning("%s", tf[j].tfile); |
386 |
|
|
+ else |
387 |
|
|
+ warningx(_("%s: short write"), tf[j].tfile); |
388 |
|
|
+ goto cleanup; |
389 |
|
|
+ } |
390 |
|
|
+ } |
391 |
|
|
+ close(ofd); |
392 |
|
|
+ } |
393 |
|
|
+ /* |
394 |
|
|
+ * We always update the stashed mtime because the time |
395 |
|
|
+ * resolution of the filesystem the temporary file is on may |
396 |
|
|
+ * not match that of the filesystem where the file to be edited |
397 |
|
|
+ * resides. It is OK if touch() fails since we only use the info |
398 |
|
|
+ * to determine whether or not a file has been modified. |
399 |
|
|
+ */ |
400 |
|
|
+ (void) touch(tfd, NULL, &tf[j].omtim); |
401 |
|
|
+ rc = fstat(tfd, &sb); |
402 |
|
|
+ if (!rc) |
403 |
|
|
+ mtim_get(&sb, &tf[j].omtim); |
404 |
|
|
+ close(tfd); |
405 |
|
|
+ j++; |
406 |
|
|
+ } |
407 |
|
|
+ if ((nfiles = j) == 0) |
408 |
|
|
+ goto cleanup; /* no files readable, you lose */ |
409 |
|
|
+ } else { |
410 |
|
|
+ /* Copy contents of temp files to real ones */ |
411 |
|
|
+ for (i = 0; i < nfiles; i++) { |
412 |
|
|
+ rc = -1; |
413 |
|
|
+ if (seteuid(user_details.uid) != 0) |
414 |
|
|
+ error(1, "seteuid(%d)", (int)user_details.uid); |
415 |
|
|
+ if ((tfd = open(tf[i].tfile, O_RDONLY, 0644)) != -1) { |
416 |
|
|
+ rc = fstat(tfd, &sb); |
417 |
|
|
+ } |
418 |
|
|
+ if (seteuid(ROOT_UID) != 0) |
419 |
|
|
+ error(1, "seteuid(ROOT_UID)"); |
420 |
|
|
+ if (rc || !S_ISREG(sb.st_mode)) { |
421 |
|
|
+ if (rc) |
422 |
|
|
+ warning("%s", tf[i].tfile); |
423 |
|
|
+ else |
424 |
|
|
+ warningx(_("%s: not a regular file"), tf[i].tfile); |
425 |
|
|
+ warningx(_("%s left unmodified"), tf[i].ofile); |
426 |
|
|
+ if (tfd != -1) |
427 |
|
|
+ close(tfd); |
428 |
|
|
+ continue; |
429 |
|
|
+ } |
430 |
|
|
+ mtim_get(&sb, &tv); |
431 |
|
|
+ if (tf[i].osize == sb.st_size && timevalcmp(&tf[i].omtim, &tv, ==)) { |
432 |
|
|
+ /* |
433 |
|
|
+ * If mtime and size match but the user spent no measurable |
434 |
|
|
+ * time in the editor we can't tell if the file was changed. |
435 |
|
|
+ */ |
436 |
|
|
+ if (tval_isset) { |
437 |
|
|
+ warningx(_("%s unchanged"), tf[i].ofile); |
438 |
|
|
+ unlink(tf[i].tfile); |
439 |
|
|
+ close(tfd); |
440 |
|
|
+ continue; |
441 |
|
|
+ } |
442 |
|
|
+ } |
443 |
|
|
+ switch_user(command_details->euid, command_details->egid, |
444 |
|
|
+ command_details->ngroups, command_details->groups); |
445 |
|
|
+ ofd = open(tf[i].ofile, O_WRONLY|O_TRUNC|O_CREAT, 0644); |
446 |
|
|
+ switch_user(ROOT_UID, user_details.egid, |
447 |
|
|
+ user_details.ngroups, user_details.groups); |
448 |
|
|
+ if (ofd == -1) { |
449 |
|
|
+ warning(_("unable to write to %s"), tf[i].ofile); |
450 |
|
|
+ warningx(_("contents of edit session left in %s"), tf[i].tfile); |
451 |
|
|
+ close(tfd); |
452 |
|
|
+ continue; |
453 |
|
|
+ } |
454 |
|
|
+ while ((nread = read(tfd, buf, sizeof(buf))) > 0) { |
455 |
|
|
+ if ((nwritten = write(ofd, buf, nread)) != nread) { |
456 |
|
|
+ if (nwritten == -1) |
457 |
|
|
+ warning("%s", tf[i].ofile); |
458 |
|
|
+ else |
459 |
|
|
+ warningx(_("%s: short write"), tf[i].ofile); |
460 |
|
|
+ break; |
461 |
|
|
+ } |
462 |
|
|
+ } |
463 |
|
|
+ if (nread == 0) { |
464 |
|
|
+ /* success, got EOF */ |
465 |
|
|
+ unlink(tf[i].tfile); |
466 |
|
|
+ } else if (nread < 0) { |
467 |
|
|
+ warning(_("unable to read temporary file")); |
468 |
|
|
+ warningx(_("contents of edit session left in %s"), tf[i].tfile); |
469 |
|
|
+ } else { |
470 |
|
|
+ warning(_("unable to write to %s"), tf[i].ofile); |
471 |
|
|
+ warningx(_("contents of edit session left in %s"), tf[i].tfile); |
472 |
|
|
+ } |
473 |
|
|
+ close(ofd); |
474 |
|
|
+ } |
475 |
|
|
+ j = 0; |
476 |
|
|
+ } |
477 |
|
|
+ |
478 |
|
|
+ debug_return_int(j); |
479 |
|
|
+cleanup: |
480 |
|
|
+ for (i = 0; i < nfiles; i++) { |
481 |
|
|
+ if (tf[i].tfile != NULL) |
482 |
|
|
+ unlink(tf[i].tfile); |
483 |
|
|
+ } |
484 |
|
|
+ |
485 |
|
|
+ debug_return_int(-1); |
486 |
|
|
+} |
487 |
|
|
+ |
488 |
|
|
static void |
489 |
|
|
switch_user(uid_t euid, gid_t egid, int ngroups, GETGROUPS_T *groups) |
490 |
|
|
{ |
491 |
|
|
@@ -87,20 +360,17 @@ int |
492 |
|
|
sudo_edit(struct command_details *command_details) |
493 |
|
|
{ |
494 |
|
|
struct command_details editor_details; |
495 |
|
|
- ssize_t nread, nwritten; |
496 |
|
|
const char *tmpdir; |
497 |
|
|
- char *cp, *suff, **nargv, **ap, **files = NULL; |
498 |
|
|
- char buf[BUFSIZ]; |
499 |
|
|
- int rc, i, j, ac, ofd, tfd, nargc, rval, tmplen; |
500 |
|
|
- int editor_argc = 0, nfiles = 0; |
501 |
|
|
+ char **ap; |
502 |
|
|
+ char **nargv, **files = NULL; |
503 |
|
|
+ int editor_argc = 0; |
504 |
|
|
+ int i, ac, nargc, rval, nfiles = 0, tmplen; |
505 |
|
|
struct stat sb; |
506 |
|
|
- struct timeval tv, tv1, tv2; |
507 |
|
|
- struct tempfile { |
508 |
|
|
- char *tfile; |
509 |
|
|
- char *ofile; |
510 |
|
|
- struct timeval omtim; |
511 |
|
|
- off_t osize; |
512 |
|
|
- } *tf = NULL; |
513 |
|
|
+ struct timeval tv1, tv2; |
514 |
|
|
+ struct tempfile *tf; |
515 |
|
|
+#ifdef HAVE_SELINUX |
516 |
|
|
+ int rbac_enabled; |
517 |
|
|
+#endif |
518 |
|
|
debug_decl(sudo_edit, SUDO_DEBUG_EDIT) |
519 |
|
|
|
520 |
|
|
/* |
521 |
|
|
@@ -109,7 +379,7 @@ sudo_edit(struct command_details *comman |
522 |
|
|
*/ |
523 |
|
|
if (setuid(ROOT_UID) != 0) { |
524 |
|
|
warning(_("unable to change uid to root (%u)"), ROOT_UID); |
525 |
|
|
- goto cleanup; |
526 |
|
|
+ return 1; |
527 |
|
|
} |
528 |
|
|
|
529 |
|
|
/* |
530 |
|
|
@@ -127,6 +397,9 @@ sudo_edit(struct command_details *comman |
531 |
|
|
while (tmplen > 0 && tmpdir[tmplen - 1] == '/') |
532 |
|
|
tmplen--; |
533 |
|
|
|
534 |
|
|
+#ifdef HAVE_SELINUX |
535 |
|
|
+ rbac_enabled = is_selinux_enabled() > 0 && command_details->selinux_role != NULL; |
536 |
|
|
+#endif |
537 |
|
|
/* |
538 |
|
|
* The user's editor must be separated from the files to be |
539 |
|
|
* edited by a "--" option. |
540 |
|
|
@@ -141,7 +414,7 @@ sudo_edit(struct command_details *comman |
541 |
|
|
} |
542 |
|
|
if (nfiles == 0) { |
543 |
|
|
warningx(_("plugin error: missing file list for sudoedit")); |
544 |
|
|
- goto cleanup; |
545 |
|
|
+ return 1; |
546 |
|
|
} |
547 |
|
|
|
548 |
|
|
/* |
549 |
|
|
@@ -150,81 +423,18 @@ sudo_edit(struct command_details *comman |
550 |
|
|
*/ |
551 |
|
|
tf = emalloc2(nfiles, sizeof(*tf)); |
552 |
|
|
zero_bytes(tf, nfiles * sizeof(*tf)); |
553 |
|
|
- for (i = 0, j = 0; i < nfiles; i++) { |
554 |
|
|
- rc = -1; |
555 |
|
|
- switch_user(command_details->euid, command_details->egid, |
556 |
|
|
- command_details->ngroups, command_details->groups); |
557 |
|
|
- if ((ofd = open(files[i], O_RDONLY, 0644)) != -1 || errno == ENOENT) { |
558 |
|
|
- if (ofd == -1) { |
559 |
|
|
- zero_bytes(&sb, sizeof(sb)); /* new file */ |
560 |
|
|
- rc = 0; |
561 |
|
|
- } else { |
562 |
|
|
- rc = fstat(ofd, &sb); |
563 |
|
|
- } |
564 |
|
|
- } |
565 |
|
|
- switch_user(ROOT_UID, user_details.egid, |
566 |
|
|
- user_details.ngroups, user_details.groups); |
567 |
|
|
- if (rc || (ofd != -1 && !S_ISREG(sb.st_mode))) { |
568 |
|
|
- if (rc) |
569 |
|
|
- warning("%s", files[i]); |
570 |
|
|
- else |
571 |
|
|
- warningx(_("%s: not a regular file"), files[i]); |
572 |
|
|
- if (ofd != -1) |
573 |
|
|
- close(ofd); |
574 |
|
|
- continue; |
575 |
|
|
- } |
576 |
|
|
- tf[j].ofile = files[i]; |
577 |
|
|
- tf[j].osize = sb.st_size; |
578 |
|
|
- mtim_get(&sb, &tf[j].omtim); |
579 |
|
|
- if ((cp = strrchr(tf[j].ofile, '/')) != NULL) |
580 |
|
|
- cp++; |
581 |
|
|
- else |
582 |
|
|
- cp = tf[j].ofile; |
583 |
|
|
- suff = strrchr(cp, '.'); |
584 |
|
|
- if (suff != NULL) { |
585 |
|
|
- easprintf(&tf[j].tfile, "%.*s/%.*sXXXXXXXX%s", tmplen, tmpdir, |
586 |
|
|
- (int)(size_t)(suff - cp), cp, suff); |
587 |
|
|
- } else { |
588 |
|
|
- easprintf(&tf[j].tfile, "%.*s/%s.XXXXXXXX", tmplen, tmpdir, cp); |
589 |
|
|
- } |
590 |
|
|
- if (seteuid(user_details.uid) != 0) |
591 |
|
|
- error(1, "seteuid(%d)", (int)user_details.uid); |
592 |
|
|
- tfd = mkstemps(tf[j].tfile, suff ? strlen(suff) : 0); |
593 |
|
|
- if (seteuid(ROOT_UID) != 0) |
594 |
|
|
- error(1, "seteuid(ROOT_UID)"); |
595 |
|
|
- if (tfd == -1) { |
596 |
|
|
- warning("mkstemps"); |
597 |
|
|
- goto cleanup; |
598 |
|
|
- } |
599 |
|
|
- if (ofd != -1) { |
600 |
|
|
- while ((nread = read(ofd, buf, sizeof(buf))) != 0) { |
601 |
|
|
- if ((nwritten = write(tfd, buf, nread)) != nread) { |
602 |
|
|
- if (nwritten == -1) |
603 |
|
|
- warning("%s", tf[j].tfile); |
604 |
|
|
- else |
605 |
|
|
- warningx(_("%s: short write"), tf[j].tfile); |
606 |
|
|
- goto cleanup; |
607 |
|
|
- } |
608 |
|
|
- } |
609 |
|
|
- close(ofd); |
610 |
|
|
- } |
611 |
|
|
- /* |
612 |
|
|
- * We always update the stashed mtime because the time |
613 |
|
|
- * resolution of the filesystem the temporary file is on may |
614 |
|
|
- * not match that of the filesystem where the file to be edited |
615 |
|
|
- * resides. It is OK if touch() fails since we only use the info |
616 |
|
|
- * to determine whether or not a file has been modified. |
617 |
|
|
- */ |
618 |
|
|
- (void) touch(tfd, NULL, &tf[j].omtim); |
619 |
|
|
- rc = fstat(tfd, &sb); |
620 |
|
|
- if (!rc) |
621 |
|
|
- mtim_get(&sb, &tf[j].omtim); |
622 |
|
|
- close(tfd); |
623 |
|
|
- j++; |
624 |
|
|
- } |
625 |
|
|
- if ((nfiles = j) == 0) |
626 |
|
|
- goto cleanup; /* no files readable, you lose */ |
627 |
|
|
+ |
628 |
|
|
+ /* Make temporary copies of the original files */ |
629 |
|
|
+ if (!rbac_enabled) |
630 |
|
|
+ nfiles = sudo_edit_copy(command_details, tf, files, nfiles, tmpdir, tmplen, 0); |
631 |
|
|
+ else |
632 |
|
|
+ nfiles = selinux_edit_copy(command_details, tf, files, nfiles, tmpdir, tmplen, 0); |
633 |
|
|
|
634 |
|
|
+ if (nfiles <= 0) |
635 |
|
|
+ return 1; |
636 |
|
|
+ |
637 |
|
|
+ switch_user(ROOT_UID, user_details.egid, |
638 |
|
|
+ user_details.ngroups, user_details.groups); |
639 |
|
|
/* |
640 |
|
|
* Allocate space for the new argument vector and fill it in. |
641 |
|
|
* We concatenate the editor with its args and the file list |
642 |
|
|
@@ -253,84 +463,18 @@ sudo_edit(struct command_details *comman |
643 |
|
|
editor_details.argv = nargv; |
644 |
|
|
rval = run_command(&editor_details); |
645 |
|
|
gettimeofday(&tv2, NULL); |
646 |
|
|
+ timevalsub(&tv1, &tv2); |
647 |
|
|
|
648 |
|
|
- /* Copy contents of temp files to real ones */ |
649 |
|
|
- for (i = 0; i < nfiles; i++) { |
650 |
|
|
- rc = -1; |
651 |
|
|
- if (seteuid(user_details.uid) != 0) |
652 |
|
|
- error(1, "seteuid(%d)", (int)user_details.uid); |
653 |
|
|
- if ((tfd = open(tf[i].tfile, O_RDONLY, 0644)) != -1) { |
654 |
|
|
- rc = fstat(tfd, &sb); |
655 |
|
|
- } |
656 |
|
|
- if (seteuid(ROOT_UID) != 0) |
657 |
|
|
- error(1, "seteuid(ROOT_UID)"); |
658 |
|
|
- if (rc || !S_ISREG(sb.st_mode)) { |
659 |
|
|
- if (rc) |
660 |
|
|
- warning("%s", tf[i].tfile); |
661 |
|
|
- else |
662 |
|
|
- warningx(_("%s: not a regular file"), tf[i].tfile); |
663 |
|
|
- warningx(_("%s left unmodified"), tf[i].ofile); |
664 |
|
|
- if (tfd != -1) |
665 |
|
|
- close(tfd); |
666 |
|
|
- continue; |
667 |
|
|
- } |
668 |
|
|
- mtim_get(&sb, &tv); |
669 |
|
|
- if (tf[i].osize == sb.st_size && timevalcmp(&tf[i].omtim, &tv, ==)) { |
670 |
|
|
- /* |
671 |
|
|
- * If mtime and size match but the user spent no measurable |
672 |
|
|
- * time in the editor we can't tell if the file was changed. |
673 |
|
|
- */ |
674 |
|
|
- timevalsub(&tv1, &tv2); |
675 |
|
|
- if (timevalisset(&tv2)) { |
676 |
|
|
- warningx(_("%s unchanged"), tf[i].ofile); |
677 |
|
|
- unlink(tf[i].tfile); |
678 |
|
|
- close(tfd); |
679 |
|
|
- continue; |
680 |
|
|
- } |
681 |
|
|
- } |
682 |
|
|
- switch_user(command_details->euid, command_details->egid, |
683 |
|
|
- command_details->ngroups, command_details->groups); |
684 |
|
|
- ofd = open(tf[i].ofile, O_WRONLY|O_TRUNC|O_CREAT, 0644); |
685 |
|
|
- switch_user(ROOT_UID, user_details.egid, |
686 |
|
|
- user_details.ngroups, user_details.groups); |
687 |
|
|
- if (ofd == -1) { |
688 |
|
|
- warning(_("unable to write to %s"), tf[i].ofile); |
689 |
|
|
- warningx(_("contents of edit session left in %s"), tf[i].tfile); |
690 |
|
|
- close(tfd); |
691 |
|
|
- continue; |
692 |
|
|
- } |
693 |
|
|
- while ((nread = read(tfd, buf, sizeof(buf))) > 0) { |
694 |
|
|
- if ((nwritten = write(ofd, buf, nread)) != nread) { |
695 |
|
|
- if (nwritten == -1) |
696 |
|
|
- warning("%s", tf[i].ofile); |
697 |
|
|
- else |
698 |
|
|
- warningx(_("%s: short write"), tf[i].ofile); |
699 |
|
|
- break; |
700 |
|
|
- } |
701 |
|
|
- } |
702 |
|
|
- if (nread == 0) { |
703 |
|
|
- /* success, got EOF */ |
704 |
|
|
- unlink(tf[i].tfile); |
705 |
|
|
- } else if (nread < 0) { |
706 |
|
|
- warning(_("unable to read temporary file")); |
707 |
|
|
- warningx(_("contents of edit session left in %s"), tf[i].tfile); |
708 |
|
|
- } else { |
709 |
|
|
- warning(_("unable to write to %s"), tf[i].ofile); |
710 |
|
|
- warningx(_("contents of edit session left in %s"), tf[i].tfile); |
711 |
|
|
- } |
712 |
|
|
- close(ofd); |
713 |
|
|
- } |
714 |
|
|
+ switch_user(ROOT_UID, user_details.egid, |
715 |
|
|
+ user_details.ngroups, user_details.groups); |
716 |
|
|
+ |
717 |
|
|
+ /* Copy the temporary files back to originals */ |
718 |
|
|
+ if (!rbac_enabled) |
719 |
|
|
+ nfiles = sudo_edit_copy(command_details, tf, NULL, nfiles, NULL, 0, timevalisset(&tv2)); |
720 |
|
|
+ else |
721 |
|
|
+ nfiles = selinux_edit_copy(command_details, tf, NULL, nfiles, NULL, 0, timevalisset(&tv2)); |
722 |
|
|
+ |
723 |
|
|
debug_return_int(rval); |
724 |
|
|
- |
725 |
|
|
-cleanup: |
726 |
|
|
- /* Clean up temp files and return. */ |
727 |
|
|
- if (tf != NULL) { |
728 |
|
|
- for (i = 0; i < nfiles; i++) { |
729 |
|
|
- if (tf[i].tfile != NULL) |
730 |
|
|
- unlink(tf[i].tfile); |
731 |
|
|
- } |
732 |
|
|
- } |
733 |
|
|
- debug_return_int(1); |
734 |
|
|
} |
735 |
|
|
|
736 |
|
|
#else /* HAVE_SETRESUID || HAVE_SETREUID || HAVE_SETEUID */ |
737 |
|
|
diff -up sudo-1.8.6p3/src/sudo.h.sudoedit-selinux sudo-1.8.6p3/src/sudo.h |
738 |
|
|
--- sudo-1.8.6p3/src/sudo.h.sudoedit-selinux 2012-09-18 15:56:30.000000000 +0200 |
739 |
|
|
+++ sudo-1.8.6p3/src/sudo.h 2012-09-25 16:04:36.690423001 +0200 |
740 |
|
|
@@ -130,6 +130,7 @@ struct user_details { |
741 |
|
|
#define CD_RBAC_ENABLED 0x0800 |
742 |
|
|
#define CD_USE_PTY 0x1000 |
743 |
|
|
#define CD_SET_UTMP 0x2000 |
744 |
|
|
+#define CD_SUDOEDIT_COPY 0x4000 |
745 |
|
|
|
746 |
|
|
struct command_details { |
747 |
|
|
uid_t uid; |