File indexing completed on 2025-02-02 14:11:33
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 }