/* * [bushi@rose ~]$ gcc -o wrapcmd_pty wrapcmd_pty.c -Wall -lutil -lpthread * [bushi@rose ~]$ * [bushi@rose ~]$ ./wrapcmd_pty * ptm_fd:5 * [child] pts: /dev/pts/2 * [child] Password: * [child] [root@rose bushi]# whoami * [child] root * [child] [root@rose bushi]# exit * [child] exit * (thread died) * (parent died) * [bushi@rose ~]$ */ #define _GNU_SOURCE #define _XOPEN_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include static char * const su_user = "root"; static const char * const su_password = "babo"; static const char * const shell_cmds[] = { "whoami", }; static int control[2] = { -1, -1}; static volatile int met_password = 0; static void thread_died(void *arg) { free(arg); printf("(thread died)\n"); } static void parent_died(void) { printf("(parent died)\n"); } static void child_died(void) { printf("(child died)\n"); } static void *print_from_child(void *arg) { const int ptm_fd = *(const int*)arg; char *buf = malloc(256); char *cs, *s, *e = buf + 256 - 1; pthread_cleanup_push(thread_died, buf); #define CHILD "[child] " memcpy(buf, CHILD, sizeof(CHILD)); cs = s = buf + sizeof(CHILD); int ret; do { char c; ret = read(ptm_fd, &c, 1); if (ret > 0) { *cs++ = c; if (!met_password && !strncasecmp(s, "password:", 9)) { met_password = 1; } if (!(c=='\r' || c=='\n') || (cs>=e)) continue; write(STDOUT_FILENO, buf, cs - buf); cs = s; } } while (ret >= 0); pthread_cleanup_pop(1); return (void*)NULL; } static void print_to_child(FILE *ptm_fp) { int i; setbuf(ptm_fp, NULL); fprintf(ptm_fp, "%s\n", su_password); for (i = 0; i < sizeof(shell_cmds)/sizeof(shell_cmds[0]); i++) { fprintf(ptm_fp, "%s\n", shell_cmds[i]); } fputs("exit\n", ptm_fp); } static int parent(const int ptm_fd, const pid_t cid) { char c; int cstatus, ret; pid_t wid; pthread_t tid; close(control[1]); control[1] = -1; atexit(parent_died); printf("ptm_fd:%d\n", ptm_fd); ret = pthread_create(&tid, NULL, print_from_child, (void*)&ptm_fd); if (ret) { fprintf(stderr, "pthread_create: %s\n", strerror(ret)); return -ret; } pthread_detach(tid); FILE *ptm_fp = fdopen(ptm_fd, "w"); if (!ptm_fp) { perror("fdopen"); kill(cid, SIGTERM); goto out; } /* wait event */ if ((ret = read(control[0], &c, 1)) < 1) { /* endpoint died. */ goto out; } /* FIXME: wait first token */ while (met_password == 0) { usleep(1); } print_to_child(ptm_fp); out: wid = waitpid(cid, &cstatus, 0); if (wid < 0) { perror("wait(cid)"); } else { sleep(1); } pthread_cancel(tid); fclose(ptm_fp); /* FIXME */ return cstatus; } static void child(const int ptm_fd, const char *pts) { close(control[0]); control[0] = -1; /* won't be called except exceptions */ atexit(child_died); printf("pts: %s\n", pts); /* wakeup parent */ char c = 0; if (write(control[1], &c, 1) < 0) exit(-1); setbuf(stdout, NULL); setbuf(stderr, NULL); char *const envp[] = { "LANG=C", NULL}; int ret = execle("/bin/su", "/bin/su", su_user, NULL, envp); /* never reached except exceptions */ perror("exec*"); exit(ret); } int main(int argc, char **argv) { int ret = 0; if (pipe(control)) { perror("pipe"); return -1; } int ptm_fd = -1; char pts[128] = {0,}; /* libutil */ int cid = forkpty(&ptm_fd, pts, NULL, NULL); switch (cid) { case -1: perror("fork()"); return -3; case 0: child(ptm_fd, pts); break; default: ret = parent(ptm_fd, cid); break; } close(ptm_fd); if (control[0] > 0) close(control[0]); if (control[1] > 0) close(control[1]); return -ret; }