/* vi: ts=8 sts=4 sw=4 * * $Id$ * * This file is part of the KDE project, module kdesu. * Copyright (C) 1999,2000 Geert Jansen * * 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. * * * 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 * - dcopserver KDE dcopserver csl of netids * - dcop_auth DCOP authentication csl of "type cookie" pairs for DCOP * - ice_auth ICE authentication csl of "type cookie" pairs for ICE * - 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 #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 }, { "dcopserver", 0L }, { "dcop_auth", 0L }, { "ice_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_DCOPSERVER 3 #define P_DCOP_AUTH 4 #define P_ICE_AUTH 5 #define P_COMMAND 6 #define P_PATH 7 #define P_XWIN_ONLY 8 #define P_USER 9 #define P_PRIORITY 10 #define P_SCHEDULER 11 #define P_APP_STARTUP_ID 12 #define P_LAST 13 /* Prototypes */ char *xmalloc(size_t); char *xrealloc(char *ptr, int size); int xsetenv(const char *name, const char *value); char *xstrdup(char *src); char **xstrsep(char *str); /** * 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 = realloc(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 /** * The main program */ int main() { char buf[BUFSIZE+1]; #ifndef QWS char xauthority[200]; #endif char iceauthority[200]; char **host, **auth; int i/*, res, sycoca*/, prio; pid_t pid; FILE *fout; struct passwd *pw; const char* kdesu_lc_all; /* Get startup parameters. */ for (i=0; ipw_dir); /* 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 } /* Handle DCOP */ if (strcmp(params[P_DCOPSERVER].value, "no")) { xsetenv("DCOPSERVER", params[P_DCOPSERVER].value); host = xstrsep(params[P_DCOPSERVER].value); auth = xstrsep(params[P_ICE_AUTH].value); if (host[0]) { int fd; int oldumask = umask(077); strcpy(iceauthority, "/tmp/iceauth.XXXXXXXXXX"); fd = mkstemp(iceauthority); umask(oldumask); if (fd == -1) { perror("kdesu_stub: mkstemp()"); exit(1); } else close(fd); xsetenv("ICEAUTHORITY", iceauthority); fout = popen(ICEAUTH_COMMAND " >/dev/null 2>&1", "w"); if (!fout) { perror("kdesu_stub: popen " ICEAUTH_COMMAND); exit(1); } for (i=0; host[i]; i++) fprintf(fout, "add ICE \"\" %s %s\n", host[i], auth[i]); auth = xstrsep(params[P_DCOP_AUTH].value); for (i=0; host[i]; i++) fprintf(fout, "add DCOP \"\" %s %s\n", host[i], auth[i]); pclose(fout); } } /* Rebuild the sycoca and start kdeinit? */ if (strcmp(params[P_XWIN_ONLY].value, "no")) { system("kdeinit --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 unlink(xauthority); #endif unlink(iceauthority); exit(xit); } else { setsid(); /* Child: exec command. */ sprintf(buf, "%s", params[P_COMMAND].value); execl("/bin/sh", "sh", "-c", buf, (void *)0); perror("kdesu_stub: exec()"); _exit(1); } }