Task: launch from the main program on Qt 4, which is run under any ordinary user, a graphical application under the root. You cannot use sudo, expect.

In general, you need to run the command su -c "запуск программы" with auto-password. Those. echo "пароль" | su -c "запуск программы" echo "пароль" | su -c "запуск программы" How to get around the error "su must run from the terminal"? As a result, the desired program should start under su, with automatic password substitution.

When using QProcess requires running through a terminal. So now I'm trying to execute su through a pseudo-terminal. Stuck on password substitution problem, i.e. emulation of user input (if I understand correctly, standard stdin will not help, since the password for su is read directly from tty and cannot be entered in any way except manually)

  • for this, it is better to use sudo without a password (with a restriction on the program being started, of course). - aleksandr barakin
  • sudo does not fit) - Emm
  • Yes, it often happens: the most appropriate means is rejected for (usually) non-objective reasons. - aleksandr barakin
  • 2
    First read the 'Password:' prompt from su, then write () password. Then you need to determine that everything is OK. - avp
  • one
    @avp thank you so much, everything worked out. - Emm

4 answers 4

Great, that you did. I think that using forkpty and an example of calling su might be interesting for quite a lot of C / C ++ programmers.

Therefore ventured to bring a small code.

 // avp 2015 link with -lutil #include <stdio.h> #include <stdlib.h> #include <err.h> #include <sysexits.h> #include <pty.h> #include <unistd.h> #include <string.h> #include <sys/types.h> #include <sys/wait.h> #include <limits.h> // returns master pty fd or -1 on error int pty_execvp (char *argv[], pid_t *child) { int master; if (!(*child = forkpty(&master, 0, 0, 0))) { execvp(argv[0], argv); /* Вот это незадача... Здесь мы в child process, а stderr и stdout (да и управляющий /dev/tty) уже перенаправлены на master. Ни о каких log-файлах нам неизвестно, так что похоже ничего не остается, как отправить error message вызвавшей нас функции через master. Ведь все равно она должна быть готова к обработке сообщений об ошибках в программе argv[0]. */ err(EX_UNAVAILABLE, "PID %ld exec %s", (long)getpid(), argv[0]); // NOT REACHABLE } else if (*child == -1) { warn("PID %ld forkpty %s", (long)getpid, argv[0]); return -1; } struct termios t; tcgetattr(master, &t); cfmakeraw(&t); tcsetattr(master, TCSAFLUSH, &t); return master; } /* Run su -l USER -c COMMAND in child process Returns pty (COMMAND's stdin, stdout, stderr) as FILE * (or NULL on error) */ FILE * run_su (const char *cmd, const char *user, const char *passwd, pid_t *child) { /* Возможно эту функцию стоит дополнить установкой таймаута... (особенно если ее модифицировать для запуска scp/ssh) */ char *ecmd, /* Для упрощения нашей задачи по определению успешности запуска /bin/su добавим к выводу CMD "уникальную" строку, которую сформируем в ok_reply[]. Для этого вместо "su -c CMD" запустим "su -c echo OK_REPLY; CMD" и если первая прочитанная от su строка (после передачи пароля) не равна ожидаемой, то будем считать, что это ошибка аутентификации. */ ok_reply[20], buf[LINE_MAX]; char *su[] = { (char *)"/bin/su", (char *)"-l", (char *)user, (char *)"-c", 0, //"echo xaxa ; ls -l /tmp", 0 }; FILE *pty; int bsize = sizeof(buf) - 1, l, master; sprintf(ok_reply, "OK %ld", (long)getpid()); if (asprintf(&ecmd, "echo %s; %s", ok_reply, cmd) < 0) { perror("run_su() asprintf"); return 0; } su[4] = ecmd; master = pty_execvp(su, child); free(ecmd); if (master < 0) return 0; /* Теоретически эти 10 байт (слово "Password: ") надо бы читать порциями в цикле и с таймаутом, проверяя не закрыл ли child свой tty */ if ((l = read(master, buf, bsize)) < 0) { perror("run_su() read Password: "); goto Err; } buf[l] = 0; if (strncmp("Password: ", buf, 10)) { fputs("Unexpected su message: ", stderr); fputs(buf, stderr); goto Err; } if (write(master, passwd, strlen(passwd)) < 0 || write(master, "\n", 1) < 0 || read(master, buf, 1) < 0) { perror("run_su() handshake su error"); goto Err; } if (!(pty = fdopen(master, "r+"))) { perror("fun_su() fdopen pty"); Err:; close(master); return 0; } if (!fgets(buf, bsize, pty) || strncmp(ok_reply, buf, strlen(ok_reply))) { fprintf(stderr, "Auth failure. Message: %s\n", buf); fclose(pty); return 0; } return pty; } int main (int ac, char *av[]) { pid_t child; FILE *pty = run_su("ls -al /tmp", "testuser", "toor", &child); int c; puts("Gooo"); while (pty && (c = fgetc(pty)) != EOF) putchar(c); if (wait(&c) != child) err(1, "unexpected child"); if (WIFEXITED(c)) printf("result: %d\n", WEXITSTATUS(c)); else printf("terminated %d\n", WTERMSIG(c)); return puts("End") == EOF; } 
  • one
    @Emm, improved error handling and added code comments - avp

Maybe you just need to put the script SUID bit? In this case, it will be executed on behalf of the owner, who could be anyone, even root.

The idea that the program "should work for any ordinary user, without adding it to sudoers and other preliminary operations" contradicts all the principles of security. Either it is available to all users and then su is not needed, or someone with root rights permits its execution - sudo rules or sets the SUID bit to it.

  • Are you sure that the s bits for root in Linux for scripts (bash, etc.) work? Can you demonstrate ??? - avp
  • @avp pkexec works this way, it has the suid bit set and the root owner is Emm
  • @Emm, so this is not a script !!! / file /usr/bin/pkexec / ... /usr/bin/pkexec: setuid ELF 32-bit LSB executable, Intel 80386,..... - avp
  • Yes. Really. Was wrong. It will be correct to use sudo after all. - Sergey Rusakov
 "echo \"пароль\" | sudo -S -запуск программы" 

but this is a bad idea :)

  • if executed directly in the console, with su gives the same error (su: should be launched from the terminal), with sudo also swears that the wrong parameters. - Emm
  • tested and corrected. So echo "test" | sudo -S '/ usr / bin / cat / proc / meminfo' does not work. but without quotes - it works echo "test" | sudo -S / usr / bin / cat / proc / meminfo - KoVadim
  • @ KoVadim sudo does not fit, unfortunately, because The main program (in which this launch takes place) should work for any ordinary user, without adding it to sudoers and other preliminary operations. similar to gksu, fly-su, kdesu, pkexec, etc. Those. su is needed with password substitution. - Emm
  • So you looked at the source? Although gksu, though fly-su, does not matter. It also works there. - user6550
  • @Emm why do we need this launch? can you solve a banal problem in ways unknown to science? - KoVadim

Use expect in linux. Line by line you can describe all the actions.

  • It is not desirable to expect requires installation - Emm