1/* vi: ts=8 sts=4 sw=4
2 *
3 * kdesu_stub.c: KDE su executes this stub through su or ssh. This stub in turn
4 * executes the target program. Before that, startup parameters
5 * are sent through stdin.
6 *
7 * This file is part of the KDE project, module kdesu.
8 * Copyright (C) 1999,2000 Geert Jansen <jansen@kde.org>
9 *
10 * This library is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU Lesser General Public
12 * License as published by the Free Software Foundation; either
13 * version 2 of the License, or (at your option) any later version.
14 *
15 * This library is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * Lesser General Public License for more details.
19 *
20 * You should have received a copy of the GNU Lesser General Public
21 * License along with this library; if not, write to the Free Software
22 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
23 *
24 *
25 * Available parameters:
26 *
27 * Parameter Description Format (csl = comma separated list)
28 *
29 * - kdesu_stub Header "ok" | "stop"
30 * - display X11 display string
31 * - display_auth X11 authentication "type cookie" pair
32 * - command Command to run string
33 * - path PATH env. var string
34 * - build_sycoca Rebuild sycoca? "yes" | "no"
35 * - user Target user string
36 * - priority Process priority 0 <= int <= 100
37 * - scheduler Process scheduler "fifo" | "normal"
38 * - app_startup_id DESKTOP_STARTUP_ID string
39 * - environment Additional envvars strings, last one is empty
40 */
41
42#include <config.h>
43#include <config-kdesu.h>
44
45#include <stdio.h>
46#include <stdlib.h>
47#include <unistd.h>
48#include <string.h>
49#include <errno.h>
50#include <pwd.h>
51#include <termios.h>
52#include <signal.h>
53
54#ifdef HAVE_INITGROUPS
55#include <grp.h>
56#endif
57
58#include <sys/types.h>
59#include <sys/stat.h>
60#include <sys/wait.h>
61#include <sys/time.h>
62#include <sys/resource.h>
63
64#ifdef POSIX1B_SCHEDULING
65#include <sched.h>
66#endif
67
68/**
69 * Params sent by the peer.
70 */
71
72struct param_struct
73{
74 const char *name;
75 char *value;
76};
77
78struct param_struct params[] =
79{
80 { "kdesu_stub", 0L },
81 { "display", 0L },
82 { "display_auth", 0L },
83 { "command", 0L },
84 { "path", 0L },
85 { "xwindows_only", 0L },
86 { "user", 0L },
87 { "priority", 0L },
88 { "scheduler", 0L },
89/* obsoleted by app_startup_id { "app_start_pid", 0L } */
90 { "app_startup_id", 0L }
91};
92
93#define P_HEADER 0
94#define P_DISPLAY 1
95#define P_DISPLAY_AUTH 2
96#define P_COMMAND 3
97#define P_PATH 4
98#define P_XWIN_ONLY 5
99#define P_USER 6
100#define P_PRIORITY 7
101#define P_SCHEDULER 8
102#define P_APP_STARTUP_ID 9
103#define P_LAST 10
104
105/**
106 * Safe malloc functions.
107 */
108char *xmalloc(size_t size)
109{
110 char *ptr = malloc(size);
111 if (ptr) return ptr;
112 perror("malloc()");
113 exit(1);
114}
115
116
117char **xrealloc(char **ptr, int size)
118{
119 ptr = realloc(ptr, size);
120 if (ptr) return ptr;
121 perror("realloc()");
122 exit(1);
123}
124
125
126/**
127 * Solaris does not have a setenv()...
128 */
129int xsetenv(const char *name, const char *value)
130{
131 char *s = malloc(strlen(name)+strlen(value)+2);
132 if (!s) return -1;
133 strcpy(s, name);
134 strcat(s, "=");
135 strcat(s, value);
136 return putenv(s); /* yes: no free()! */
137}
138
139/**
140 * Safe strdup and strip newline
141 */
142char *xstrdup(char *src)
143{
144 int len = strlen(src);
145 char *dst = xmalloc(len+1);
146 strcpy(dst, src);
147 if (dst[len-1] == '\n')
148 dst[len-1] = '\000';
149 return dst;
150}
151
152/**
153 * Split comma separated list.
154 */
155char **xstrsep(char *str)
156{
157 int i = 0, size = 10;
158 char **list = (char **) xmalloc(size * sizeof(char *));
159 char *ptr = str, *nptr;
160 while ((nptr = strchr(ptr, ',')) != 0L)
161 {
162 if (i > size-2)
163 list = xrealloc(list, (size *= 2) * sizeof(char *));
164 *nptr = '\000';
165 list[i++] = ptr;
166 ptr = nptr+1;
167 }
168 if (*ptr != '\000')
169 list[i++] = ptr;
170 list[i] = 0L;
171 return list;
172}
173
174#define BUFSIZE 8192
175
176static void dequote(char *buf)
177{
178 char *in, *out;
179 for (in = buf, out = buf; *in; in++, out++) {
180 char c = *in;
181 if (c == '\\') {
182 c = *++in;
183 if (c == '/')
184 *out = '\\';
185 else
186 *out = c - '@';
187 } else {
188 *out = c;
189 }
190 }
191 *out = 0;
192}
193
194/**
195 * The main program
196 */
197
198int main()
199{
200 char buf[BUFSIZE+1];
201#ifndef QWS
202 char xauthority[200];
203#endif
204 int i/*, res, sycoca*/, prio;
205 pid_t pid;
206 FILE *fout;
207 struct passwd *pw;
208 const char* kdesu_lc_all;
209
210 xauthority[0] = '\0';
211
212 /* Get startup parameters. */
213
214 for (i=0; i<P_LAST; i++)
215 {
216 printf("%s\n", params[i].name);
217 fflush(stdout);
218 if (fgets(buf, BUFSIZE, stdin) == 0L)
219 {
220 printf("end\n"); fflush(stdout);
221 perror("kdesu_stub: fgets()");
222 exit(1);
223 }
224 params[i].value = xstrdup(buf);
225 /* Installation check? */
226 if ((i == 0) && !strcmp(params[i].value, "stop"))
227 {
228 printf("end\n");
229 exit(0);
230 }
231 }
232 printf("environment\n");
233 fflush(stdout);
234 for(;;)
235 {
236 char* tmp;
237 if (fgets(buf, BUFSIZE, stdin) == 0L)
238 {
239 printf("end\n"); fflush(stdout);
240 perror("kdesu_stub: fgets()");
241 exit(1);
242 }
243 dequote(buf);
244 tmp = xstrdup( buf );
245 if( tmp[ 0 ] == '\0' ) /* terminator */
246 break;
247 putenv(tmp);
248 }
249
250 printf("end\n");
251 fflush(stdout);
252
253 xsetenv("PATH", params[P_PATH].value);
254 xsetenv("DESKTOP_STARTUP_ID", params[P_APP_STARTUP_ID].value);
255
256 kdesu_lc_all = getenv( "KDESU_LC_ALL" );
257 if( kdesu_lc_all != NULL )
258 xsetenv("LC_ALL",kdesu_lc_all);
259 else
260 unsetenv("LC_ALL");
261
262 /* Do we need to change uid? */
263
264 pw = getpwnam(params[P_USER].value);
265 if (pw == 0L)
266 {
267 printf("kdesu_stub: user %s does not exist!\n", params[P_USER].value);
268 exit(1);
269 }
270 xsetenv("HOME", pw->pw_dir);
271
272 /* New user won't be able to connect it anyway. */
273 unsetenv("DBUS_SESSION_BUS_ADDRESS");
274
275 /* Set scheduling/priority */
276
277 prio = atoi(params[P_PRIORITY].value);
278 if (!strcmp(params[P_SCHEDULER].value, "realtime"))
279 {
280#ifdef POSIX1B_SCHEDULING
281 struct sched_param sched;
282 int min = sched_get_priority_min(SCHED_FIFO);
283 int max = sched_get_priority_max(SCHED_FIFO);
284 sched.sched_priority = min + (int) (((double) prio) * (max - min) / 100 + 0.5);
285 sched_setscheduler(0, SCHED_FIFO, &sched);
286#else
287 printf("kdesu_stub: realtime scheduling not supported\n");
288#endif
289 } else
290 {
291#ifdef HAVE_SETPRIORITY
292 int val = 20 - (int) (((double) prio) * 40 / 100 + 0.5);
293 setpriority(PRIO_PROCESS, getpid(), val);
294#endif
295 }
296
297 /* Drop privileges (this is permanent) */
298
299 if (getuid() != pw->pw_uid)
300 {
301 if (setgid(pw->pw_gid) == -1)
302 {
303 perror("kdesu_stub: setgid()");
304 exit(1);
305 }
306#ifdef HAVE_INITGROUPS
307 if (initgroups(pw->pw_name, pw->pw_gid) == -1)
308 {
309 perror("kdesu_stub: initgroups()");
310 exit(1);
311 }
312#endif
313 if (setuid(pw->pw_uid) == -1)
314 {
315 perror("kdesu_stub: setuid()");
316 exit(1);
317 }
318 xsetenv("HOME", pw->pw_dir);
319 }
320
321 /* Handle display */
322
323 if (strcmp(params[P_DISPLAY].value, "no"))
324 {
325#ifndef QWS
326 xsetenv("DISPLAY", params[P_DISPLAY].value);
327 if (params[P_DISPLAY_AUTH].value[0])
328 {
329 int fd2;
330 /*
331 ** save umask and set to 077, so we create those files only
332 ** readable for root. (if someone else could read them, we
333 ** are in deep shit).
334 */
335 int oldumask = umask(077);
336 const char *disp = params[P_DISPLAY].value;
337 if (strncmp(disp, "localhost:", 10) == 0)
338 disp += 9;
339
340 strcpy(xauthority, "/tmp/xauth.XXXXXXXXXX");
341 fd2 = mkstemp(xauthority);
342 umask(oldumask);
343
344 if (fd2 == -1) {
345 perror("kdesu_stub: mkstemp()");
346 exit(1);
347 } else
348 close(fd2);
349 xsetenv("XAUTHORITY", xauthority);
350
351 fout = popen("xauth >/dev/null 2>&1","w");
352 if (fout == NULL)
353 {
354 perror("kdesu_stub: popen(xauth)");
355 exit(1);
356 }
357 fprintf(fout, "add %s %s\n", disp,
358 params[P_DISPLAY_AUTH].value);
359 pclose(fout);
360 }
361#else
362 xsetenv("DISPLAY", params[P_DISPLAY].value);
363#endif
364 }
365
366 /* Rebuild the sycoca and start kdeinit? */
367
368 if (strcmp(params[P_XWIN_ONLY].value, "no"))
369 {
370 system("kdeinit4 --suicide");
371 }
372
373 /* Execute the command */
374
375 pid = fork();
376 if (pid == -1)
377 {
378 perror("kdesu_stub: fork()");
379 exit(1);
380 }
381 if (pid)
382 {
383 /* Parent: wait for child, delete tempfiles and return. */
384 int ret, state, xit = 1;
385 while (1)
386 {
387 ret = waitpid(pid, &state, 0);
388 if (ret == -1)
389 {
390 if (errno == EINTR)
391 continue;
392 if (errno != ECHILD)
393 perror("kdesu_stub: waitpid()");
394 break;
395 }
396 if (WIFEXITED(state))
397 xit = WEXITSTATUS(state);
398 }
399
400#ifndef QWS
401 if (*xauthority)
402 unlink(xauthority);
403#endif
404 exit(xit);
405 } else
406 {
407 setsid();
408 /* Child: exec command. */
409 sprintf(buf, "%s", params[P_COMMAND].value);
410 dequote(buf);
411 execl("/bin/sh", "sh", "-c", buf, (void *)0);
412 perror("kdesu_stub: exec()");
413 _exit(1);
414 }
415}
416