File indexing completed on 2024-04-21 03:53:58

0001 /*
0002     kdesu_stub.c: KDE su executes this stub through su or ssh. This stub in turn
0003                   executes the target program. Before that, startup parameters
0004                   are sent through stdin.
0005 
0006     This file is part of the KDE project, module kdesu.
0007     SPDX-FileCopyrightText: 1999, 2000 Geert Jansen <jansen@kde.org>
0008 
0009     SPDX-License-Identifier: LGPL-2.0-or-later
0010 
0011 
0012     Available parameters:
0013 
0014       Parameter       Description         Format (csl = comma separated list)
0015 
0016     - kdesu_stub      Header              "ok" | "stop"
0017     - display         X11 display         string
0018     - display_auth    X11 authentication  "type cookie" pair
0019     - command         Command to run      string
0020     - path            PATH env. var       string
0021     - build_sycoca    Rebuild sycoca?     "yes" | "no"
0022     - user            Target user         string
0023     - priority        Process priority    0 <= int <= 100
0024     - scheduler       Process scheduler   "fifo" | "normal"
0025     - app_startup_id  DESKTOP_STARTUP_ID  string
0026     - environment     Additional envvars  strings, last one is empty
0027 */
0028 
0029 #include <config-kdesu.h>
0030 
0031 #include <errno.h>
0032 #include <pwd.h>
0033 #include <signal.h>
0034 #include <stdio.h>
0035 #include <stdlib.h>
0036 #include <string.h>
0037 #include <termios.h>
0038 #include <unistd.h>
0039 
0040 #if HAVE_INITGROUPS
0041 #include <grp.h>
0042 #endif
0043 
0044 #include <sys/resource.h>
0045 #include <sys/stat.h>
0046 #include <sys/time.h>
0047 #include <sys/types.h>
0048 #include <sys/wait.h>
0049 
0050 #ifdef POSIX1B_SCHEDULING
0051 #include <sched.h>
0052 #endif
0053 
0054 /**
0055  * Params sent by the peer.
0056  */
0057 
0058 struct param_struct {
0059     const char *name;
0060     char *value;
0061 };
0062 
0063 struct param_struct params[] = {{"kdesu_stub", 0L},
0064                                 {"display", 0L},
0065                                 {"display_auth", 0L},
0066                                 {"command", 0L},
0067                                 {"path", 0L},
0068                                 {"xwindows_only", 0L},
0069                                 {"user", 0L},
0070                                 {"priority", 0L},
0071                                 {"scheduler", 0L},
0072                                 /* obsoleted by app_startup_id    { "app_start_pid", 0L } */
0073                                 {"app_startup_id", 0L}};
0074 
0075 #define P_HEADER 0
0076 #define P_DISPLAY 1
0077 #define P_DISPLAY_AUTH 2
0078 #define P_COMMAND 3
0079 #define P_PATH 4
0080 #define P_XWIN_ONLY 5
0081 #define P_USER 6
0082 #define P_PRIORITY 7
0083 #define P_SCHEDULER 8
0084 #define P_APP_STARTUP_ID 9
0085 #define P_LAST 10
0086 
0087 /**
0088  * Safe malloc functions.
0089  */
0090 char *xmalloc(size_t size)
0091 {
0092     char *ptr = malloc(size);
0093     if (ptr) {
0094         return ptr;
0095     }
0096     perror("malloc()");
0097     exit(1);
0098 }
0099 
0100 char **xrealloc(char **ptr, int size)
0101 {
0102     ptr = realloc(ptr, size);
0103     if (ptr) {
0104         return ptr;
0105     }
0106     perror("realloc()");
0107     exit(1);
0108 }
0109 
0110 /**
0111  * Solaris does not have a setenv()...
0112  */
0113 int xsetenv(const char *name, const char *value)
0114 {
0115     char *s = malloc(strlen(name) + strlen(value) + 2);
0116     if (!s) {
0117         return -1;
0118     }
0119     strcpy(s, name);
0120     strcat(s, "=");
0121     strcat(s, value);
0122     return putenv(s); /* yes: no free()! */
0123 }
0124 
0125 /**
0126  * Safe strdup and strip newline
0127  */
0128 char *xstrdup(char *src)
0129 {
0130     int len = strlen(src);
0131     char *dst = xmalloc(len + 1);
0132     strcpy(dst, src);
0133     if (dst[len - 1] == '\n') {
0134         dst[len - 1] = '\000';
0135     }
0136     return dst;
0137 }
0138 
0139 /**
0140  * Split comma separated list.
0141  */
0142 char **xstrsep(char *str)
0143 {
0144     int i = 0;
0145     int size = 10;
0146     char **list = (char **)xmalloc(size * sizeof(char *));
0147     char *ptr = str;
0148     char *nptr;
0149     while ((nptr = strchr(ptr, ',')) != 0L) {
0150         if (i > size - 2) {
0151             list = xrealloc(list, (size *= 2) * sizeof(char *));
0152         }
0153         *nptr = '\000';
0154         list[i++] = ptr;
0155         ptr = nptr + 1;
0156     }
0157     if (*ptr != '\000') {
0158         list[i++] = ptr;
0159     }
0160     list[i] = 0L;
0161     return list;
0162 }
0163 
0164 #define BUFSIZE 8192
0165 
0166 static void dequote(char *buf)
0167 {
0168     char *in;
0169     char *out;
0170     for (in = buf, out = buf; *in; in++, out++) {
0171         char c = *in;
0172         if (c == '\\') {
0173             c = *++in;
0174             if (c == '/') {
0175                 *out = '\\';
0176             } else {
0177                 *out = c - '@';
0178             }
0179         } else {
0180             *out = c;
0181         }
0182     }
0183     *out = 0;
0184 }
0185 
0186 /**
0187  * The main program
0188  */
0189 
0190 int main()
0191 {
0192     char buf[BUFSIZE + 1];
0193     char xauthority[200];
0194     int i;
0195     int prio;
0196     pid_t pid;
0197     FILE *fout;
0198     struct passwd *pw;
0199     const char *kdesu_lc_all;
0200 
0201     xauthority[0] = '\0';
0202 
0203     /* Get startup parameters. */
0204 
0205     for (i = 0; i < P_LAST; i++) {
0206         printf("%s\n", params[i].name);
0207         fflush(stdout);
0208         if (fgets(buf, BUFSIZE, stdin) == 0L) {
0209             printf("end\n");
0210             fflush(stdout);
0211             perror("kdesu_stub: fgets()");
0212             exit(1);
0213         }
0214         params[i].value = xstrdup(buf);
0215         /* Installation check? */
0216         if (i == 0 && !strcmp(params[i].value, "stop")) {
0217             printf("end\n");
0218             exit(0);
0219         }
0220     }
0221     printf("environment\n");
0222     fflush(stdout);
0223     for (;;) {
0224         char *tmp;
0225         if (fgets(buf, BUFSIZE, stdin) == 0L) {
0226             printf("end\n");
0227             fflush(stdout);
0228             perror("kdesu_stub: fgets()");
0229             exit(1);
0230         }
0231         dequote(buf);
0232         tmp = xstrdup(buf);
0233         if (tmp[0] == '\0') { /* terminator */
0234             break;
0235         }
0236         putenv(tmp);
0237     }
0238 
0239     printf("end\n");
0240     fflush(stdout);
0241 
0242     xsetenv("PATH", params[P_PATH].value);
0243     xsetenv("DESKTOP_STARTUP_ID", params[P_APP_STARTUP_ID].value);
0244 
0245     kdesu_lc_all = getenv("KDESU_LC_ALL");
0246     if (kdesu_lc_all != NULL) {
0247         xsetenv("LC_ALL", kdesu_lc_all);
0248     } else {
0249         unsetenv("LC_ALL");
0250     }
0251 
0252     unsetenv("XDG_RUNTIME_DIR");
0253 
0254     /* Do we need to change uid? */
0255 
0256     pw = getpwnam(params[P_USER].value);
0257     if (pw == 0L) {
0258         printf("kdesu_stub: user %s does not exist!\n", params[P_USER].value);
0259         exit(1);
0260     }
0261     xsetenv("HOME", pw->pw_dir);
0262 
0263     /* New user won't be able to connect it anyway. */
0264     unsetenv("DBUS_SESSION_BUS_ADDRESS");
0265 
0266     /* Set scheduling/priority */
0267 
0268     prio = atoi(params[P_PRIORITY].value);
0269     if (!strcmp(params[P_SCHEDULER].value, "realtime")) {
0270 #ifdef POSIX1B_SCHEDULING
0271         struct sched_param sched;
0272         int min = sched_get_priority_min(SCHED_FIFO);
0273         int max = sched_get_priority_max(SCHED_FIFO);
0274         sched.sched_priority = min + (int)(((double)prio) * (max - min) / 100 + 0.5);
0275         sched_setscheduler(0, SCHED_FIFO, &sched);
0276 #else
0277         printf("kdesu_stub: realtime scheduling not supported\n");
0278 #endif
0279     } else {
0280 #if HAVE_SETPRIORITY
0281         int val = 20 - (int)(((double)prio) * 40 / 100 + 0.5);
0282         setpriority(PRIO_PROCESS, getpid(), val);
0283 #endif
0284     }
0285 
0286     /* Drop privileges (this is permanent) */
0287 
0288     if (getuid() != pw->pw_uid) {
0289         if (setgid(pw->pw_gid) == -1) {
0290             perror("kdesu_stub: setgid()");
0291             exit(1);
0292         }
0293 #if HAVE_INITGROUPS
0294         if (initgroups(pw->pw_name, pw->pw_gid) == -1) {
0295             perror("kdesu_stub: initgroups()");
0296             exit(1);
0297         }
0298 #endif
0299         if (setuid(pw->pw_uid) == -1) {
0300             perror("kdesu_stub: setuid()");
0301             exit(1);
0302         }
0303         xsetenv("HOME", pw->pw_dir);
0304     }
0305 
0306     /* Handle display */
0307 
0308     if (strcmp(params[P_DISPLAY].value, "no")) {
0309         xsetenv("DISPLAY", params[P_DISPLAY].value);
0310         if (params[P_DISPLAY_AUTH].value[0]) {
0311             int fd2;
0312             /*
0313              ** save umask and set to 077, so we create those files only
0314              ** readable for root. (if someone else could read them, we
0315              ** are in deep shit).
0316              */
0317             int oldumask = umask(077);
0318             const char *disp = params[P_DISPLAY].value;
0319             if (strncmp(disp, "localhost:", 10) == 0) {
0320                 disp += 9;
0321             }
0322 
0323             strcpy(xauthority, "/tmp/xauth.XXXXXXXXXX");
0324             fd2 = mkstemp(xauthority);
0325             umask(oldumask);
0326 
0327             if (fd2 == -1) {
0328                 perror("kdesu_stub: mkstemp()");
0329                 exit(1);
0330             } else {
0331                 close(fd2);
0332             }
0333             xsetenv("XAUTHORITY", xauthority);
0334 
0335             fout = popen("xauth >/dev/null 2>&1", "w");
0336             if (fout == NULL) {
0337                 perror("kdesu_stub: popen(xauth)");
0338                 exit(1);
0339             }
0340             fprintf(fout, "add %s %s\n", disp, params[P_DISPLAY_AUTH].value);
0341             pclose(fout);
0342         }
0343     }
0344 
0345     /* Rebuild the sycoca and start kdeinit? */
0346 
0347     if (strcmp(params[P_XWIN_ONLY].value, "no")) {
0348         system("kdeinit5 --suicide");
0349     }
0350 
0351     /* Execute the command */
0352 
0353     pid = fork();
0354     if (pid == -1) {
0355         perror("kdesu_stub: fork()");
0356         exit(1);
0357     }
0358     if (pid) {
0359         /* Parent: wait for child, delete tempfiles and return. */
0360         int ret;
0361         int state;
0362         int xit = 1;
0363         while (1) {
0364             ret = waitpid(pid, &state, 0);
0365             if (ret == -1) {
0366                 if (errno == EINTR) {
0367                     continue;
0368                 }
0369                 if (errno != ECHILD) {
0370                     perror("kdesu_stub: waitpid()");
0371                 }
0372                 break;
0373             }
0374             if (WIFEXITED(state)) {
0375                 xit = WEXITSTATUS(state);
0376             }
0377         }
0378 
0379         if (*xauthority) {
0380             unlink(xauthority);
0381         }
0382         exit(xit);
0383     } else {
0384         setsid();
0385         /* Child: exec command. */
0386         sprintf(buf, "%s", params[P_COMMAND].value);
0387         dequote(buf);
0388         execl("/bin/sh", "sh", "-c", buf, (void *)0);
0389         perror("kdesu_stub: exec()");
0390         _exit(1);
0391     }
0392 }