/* vi: ts=8 sts=4 sw=4 * * kdesu_stub.c: KDE su executes this stub through su or ssh. This stub in turn * executes the target program. Before that, startup parameters * are sent through stdin. * * This file is part of the KDE project, module kdesu. * Copyright (C) 1999,2000 Geert Jansen * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * * Available parameters: * * Parameter Description Format (csl = comma separated list) * * - kdesu_stub Header "ok" | "stop" * - display X11 display string * - display_auth X11 authentication "type cookie" pair * - command Command to run string * - path PATH env. var string * - build_sycoca Rebuild sycoca? "yes" | "no" * - user Target user string * - priority Process priority 0 <= int <= 100 * - scheduler Process scheduler "fifo" | "normal" * - app_startup_id DESKTOP_STARTUP_ID string * - environment Additional envvars strings, last one is empty */ #include #include #include #include #include #include #include #include #include #include #ifdef HAVE_INITGROUPS #include #endif #include #include #include #include #include #ifdef POSIX1B_SCHEDULING #include #endif /** * Params sent by the peer. */ struct param_struct { const char *name; char *value; }; struct param_struct params[] = { { "kdesu_stub", 0L }, { "display", 0L }, { "display_auth", 0L }, { "command", 0L }, { "path", 0L }, { "xwindows_only", 0L }, { "user", 0L }, { "priority", 0L }, { "scheduler", 0L }, /* obsoleted by app_startup_id { "app_start_pid", 0L } */ { "app_startup_id", 0L } }; #define P_HEADER 0 #define P_DISPLAY 1 #define P_DISPLAY_AUTH 2 #define P_COMMAND 3 #define P_PATH 4 #define P_XWIN_ONLY 5 #define P_USER 6 #define P_PRIORITY 7 #define P_SCHEDULER 8 #define P_APP_STARTUP_ID 9 #define P_LAST 10 /** * Safe malloc functions. */ char *xmalloc(size_t size) { char *ptr = malloc(size); if (ptr) return ptr; perror("malloc()"); exit(1); } char **xrealloc(char **ptr, int size) { ptr = realloc(ptr, size); if (ptr) return ptr; perror("realloc()"); exit(1); } /** * Solaris does not have a setenv()... */ int xsetenv(const char *name, const char *value) { char *s = malloc(strlen(name)+strlen(value)+2); if (!s) return -1; strcpy(s, name); strcat(s, "="); strcat(s, value); return putenv(s); /* yes: no free()! */ } /** * Safe strdup and strip newline */ char *xstrdup(char *src) { int len = strlen(src); char *dst = xmalloc(len+1); strcpy(dst, src); if (dst[len-1] == '\n') dst[len-1] = '\000'; return dst; } /** * Split comma separated list. */ char **xstrsep(char *str) { int i = 0, size = 10; char **list = (char **) xmalloc(size * sizeof(char *)); char *ptr = str, *nptr; while ((nptr = strchr(ptr, ',')) != 0L) { if (i > size-2) list = xrealloc(list, (size *= 2) * sizeof(char *)); *nptr = '\000'; list[i++] = ptr; ptr = nptr+1; } if (*ptr != '\000') list[i++] = ptr; list[i] = 0L; return list; } #define BUFSIZE 8192 static void dequote(char *buf) { char *in, *out; for (in = buf, out = buf; *in; in++, out++) { char c = *in; if (c == '\\') { c = *++in; if (c == '/') *out = '\\'; else *out = c - '@'; } else { *out = c; } } *out = 0; } /** * The main program */ int main() { char buf[BUFSIZE+1]; #ifndef QWS char xauthority[200]; #endif int i/*, res, sycoca*/, prio; pid_t pid; FILE *fout; struct passwd *pw; const char* kdesu_lc_all; xauthority[0] = '\0'; /* Get startup parameters. */ for (i=0; ipw_dir); /* New user won't be able to connect it anyway. */ unsetenv("DBUS_SESSION_BUS_ADDRESS"); /* Set scheduling/priority */ prio = atoi(params[P_PRIORITY].value); if (!strcmp(params[P_SCHEDULER].value, "realtime")) { #ifdef POSIX1B_SCHEDULING struct sched_param sched; int min = sched_get_priority_min(SCHED_FIFO); int max = sched_get_priority_max(SCHED_FIFO); sched.sched_priority = min + (int) (((double) prio) * (max - min) / 100 + 0.5); sched_setscheduler(0, SCHED_FIFO, &sched); #else printf("kdesu_stub: realtime scheduling not supported\n"); #endif } else { #ifdef HAVE_SETPRIORITY int val = 20 - (int) (((double) prio) * 40 / 100 + 0.5); setpriority(PRIO_PROCESS, getpid(), val); #endif } /* Drop privileges (this is permanent) */ if (getuid() != pw->pw_uid) { if (setgid(pw->pw_gid) == -1) { perror("kdesu_stub: setgid()"); exit(1); } #ifdef HAVE_INITGROUPS if (initgroups(pw->pw_name, pw->pw_gid) == -1) { perror("kdesu_stub: initgroups()"); exit(1); } #endif if (setuid(pw->pw_uid) == -1) { perror("kdesu_stub: setuid()"); exit(1); } xsetenv("HOME", pw->pw_dir); } /* Handle display */ if (strcmp(params[P_DISPLAY].value, "no")) { #ifndef QWS xsetenv("DISPLAY", params[P_DISPLAY].value); if (params[P_DISPLAY_AUTH].value[0]) { int fd2; /* ** save umask and set to 077, so we create those files only ** readable for root. (if someone else could read them, we ** are in deep shit). */ int oldumask = umask(077); const char *disp = params[P_DISPLAY].value; if (strncmp(disp, "localhost:", 10) == 0) disp += 9; strcpy(xauthority, "/tmp/xauth.XXXXXXXXXX"); fd2 = mkstemp(xauthority); umask(oldumask); if (fd2 == -1) { perror("kdesu_stub: mkstemp()"); exit(1); } else close(fd2); xsetenv("XAUTHORITY", xauthority); fout = popen("xauth >/dev/null 2>&1","w"); if (fout == NULL) { perror("kdesu_stub: popen(xauth)"); exit(1); } fprintf(fout, "add %s %s\n", disp, params[P_DISPLAY_AUTH].value); pclose(fout); } #else xsetenv("DISPLAY", params[P_DISPLAY].value); #endif } /* Rebuild the sycoca and start kdeinit? */ if (strcmp(params[P_XWIN_ONLY].value, "no")) { system("kdeinit4 --suicide"); } /* Execute the command */ pid = fork(); if (pid == -1) { perror("kdesu_stub: fork()"); exit(1); } if (pid) { /* Parent: wait for child, delete tempfiles and return. */ int ret, state, xit = 1; while (1) { ret = waitpid(pid, &state, 0); if (ret == -1) { if (errno == EINTR) continue; if (errno != ECHILD) perror("kdesu_stub: waitpid()"); break; } if (WIFEXITED(state)) xit = WEXITSTATUS(state); } #ifndef QWS if (*xauthority) unlink(xauthority); #endif exit(xit); } else { setsid(); /* Child: exec command. */ sprintf(buf, "%s", params[P_COMMAND].value); dequote(buf); execl("/bin/sh", "sh", "-c", buf, (void *)0); perror("kdesu_stub: exec()"); _exit(1); } }