/* * Autologin - sort of automatic execution on a terminal. * Author: Nalin Dahyabhai * Copyright (c) 2000,2005 Red Hat, Inc. * * This program is licensed under the terms of the GPL. */ #ident "$Id: autologin.c,v 1.3 1999/12/19 22:37:45 nalin Exp $" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define SLEEPYTIME 5 static int converse(int num_msg, const struct pam_message **msg, struct pam_response **resp, void *appdata_ptr) { int i; resp = malloc(sizeof(struct pam_response*) * (num_msg + 1)); if (resp == NULL) { fprintf(stderr, "out of memory!\n"); sleep(SLEEPYTIME); _exit(1); } memset(resp, 0, sizeof(struct pam_response*) * (num_msg + 1)); for (i = 0; i < num_msg; i++) { resp[i] = malloc(sizeof(struct pam_response)); memset(resp[i], 0, sizeof(struct pam_response)); resp[i]->resp_retcode = PAM_SUCCESS; switch (msg[i]->msg_style) { case PAM_PROMPT_ECHO_OFF: case PAM_PROMPT_ECHO_ON: resp[i]->resp = strdup(appdata_ptr ? appdata_ptr : ""); /* fall through */ case PAM_ERROR_MSG: case PAM_TEXT_INFO: if ((msg[i]->msg != NULL) && (strlen(msg[i]->msg) > 0)) { fprintf(stderr, "%s", msg[i]->msg); } break; default: break; } } return PAM_SUCCESS; } static void init_environment(pam_handle_t *pamh, struct passwd *pwd) { char *tmp, **envlist; const char *path; int i; /* Set the same variables that login(1) would. */ tmp = malloc(5 + strlen(pwd->pw_dir) + 1); if (tmp == NULL) { fprintf(stderr, "out of memory!\n"); sleep(SLEEPYTIME); _exit(1); } sprintf(tmp, "HOME=%s", pwd->pw_dir); putenv(tmp); tmp = malloc(6 + strlen(pwd->pw_shell) + 1); if (tmp == NULL) { fprintf(stderr, "out of memory!\n"); sleep(SLEEPYTIME); _exit(1); } sprintf(tmp, "SHELL=%s", pwd->pw_shell); putenv(tmp); tmp = malloc(8 + strlen(pwd->pw_name) + 1); if (tmp == NULL) { fprintf(stderr, "out of memory!\n"); sleep(SLEEPYTIME); _exit(1); } sprintf(tmp, "LOGNAME=%s", pwd->pw_name); putenv(tmp); tmp = malloc(5 + strlen(pwd->pw_name) + 1); if (tmp == NULL) { fprintf(stderr, "out of memory!\n"); sleep(SLEEPYTIME); _exit(1); } sprintf(tmp, "USER=%s", pwd->pw_name); putenv(tmp); if (pwd->pw_uid == 0) { path = "/sbin:/bin:/usr/sbin:/usr/bin:/usr/X11R6/bin"; } else { path = "/usr/local/bin:" "/bin:/usr/bin:/usr/X11R6/bin:/sbin:/usr/sbin"; } tmp = malloc(5 + strlen(path) + 1); if (tmp == NULL) { fprintf(stderr, "out of memory!\n"); sleep(SLEEPYTIME); _exit(1); } sprintf(tmp, "PATH=%s", path); putenv(tmp); /* Set any environment variables which PAM would like for us to set. */ envlist = pam_getenvlist(pamh); for (i = 0; (envlist != NULL) && (envlist[i] != NULL); i++) { putenv(envlist[i]); } } int main(int argc, char **argv) { int ret = 0; int fd = 0; pid_t child; pam_handle_t *pamh = NULL; char tty[PATH_MAX]; char shell[PATH_MAX]; struct passwd *pwd; struct group *ttygrp; struct stat st; struct pam_conv conversation = { converse, "", }; /* Verify that we were invoked correctly. */ if ((argc < 3) || (argc > 4)) { fprintf(stderr, "Incorrect invocation -- should be \"%s tty " "user [command]\"\n", argv[0]); sleep(SLEEPYTIME); exit(1); } /* Verify that the user exists. */ pwd = getpwnam(argv[2]); if (pwd == NULL) { fprintf(stderr, "Unknown user \"%s\"!\n", argv[2]); sleep(SLEEPYTIME); exit(1); } ttygrp = getgrnam("tty"); /* Verify that the specified tty exists. */ strncpy(tty, argv[1], sizeof(tty)); if (stat(tty, &st) != 0) { strncpy(tty, "/dev/", sizeof(tty)); strncat(tty, argv[1], sizeof(tty)); if (stat(tty, &st) != 0) { fprintf(stderr, "Unknown tty \"%s\"!\n", argv[1]); sleep(SLEEPYTIME); exit(1); } } /* Make the tty our controlling terminal. */ close(STDIN_FILENO); close(STDOUT_FILENO); close(STDERR_FILENO); fd = open(tty, O_RDWR); if (fd == -1) { fprintf(stderr, "Error opening %s: %s.\n", tty, strerror(errno)); sleep(SLEEPYTIME); exit(1); } if (dup2(fd, STDIN_FILENO) != STDIN_FILENO) { fprintf(stderr, "Error dup2()ing to stdin: %s.\n", strerror(errno)); sleep(SLEEPYTIME); exit(1); } if (dup2(fd, STDOUT_FILENO) != STDOUT_FILENO) { fprintf(stderr, "Error dup2()ing to stdout: %s.\n", strerror(errno)); sleep(SLEEPYTIME); exit(1); } if (dup2(fd, STDERR_FILENO) != STDERR_FILENO) { fprintf(stderr, "Error dup2()ing to stderr: %s.\n", strerror(errno)); sleep(SLEEPYTIME); exit(1); } if (dup2(fd, 3) != 3) { fprintf(stderr, "Error dup2()ing to FD3: %s.\n", strerror(errno)); sleep(SLEEPYTIME); exit(1); } if (fcntl(3, F_SETFD, FD_CLOEXEC) != 0) { fprintf(stderr, "Error setting close-on-exec flag: %s.\n", strerror(errno)); sleep(SLEEPYTIME); exit(1); } /* Start PAM up, skip authentication, and open a session. */ ret = pam_start("login", argv[2], &conversation, &pamh); if (ret != PAM_SUCCESS) { fprintf(stderr, "Error initializing libpam: %s\n", pam_strerror(pamh, ret)); sleep(SLEEPYTIME); exit(1); } pam_set_item(pamh, PAM_USER, argv[2]); pam_set_item(pamh, PAM_TTY, argv[1]); pam_set_item(pamh, PAM_RHOST, NULL); pam_set_item(pamh, PAM_RUSER, argv[2]); ret = pam_setcred(pamh, PAM_ESTABLISH_CRED); if (ret != PAM_SUCCESS) { fprintf(stderr, "Error establishing credentials: %s\n", pam_strerror(pamh, ret)); sleep(SLEEPYTIME); exit(1); } ret = pam_open_session(pamh, 0); if (ret != PAM_SUCCESS) { fprintf(stderr, "Error initializing session: %s\n", pam_strerror(pamh, ret)); sleep(SLEEPYTIME); exit(1); } /* Prepare argv[0] for the user's shell. */ memset(shell, 0, sizeof(shell)); if (rindex(pwd->pw_shell, '/') != NULL) { strncpy(shell, rindex(pwd->pw_shell, '/'), sizeof(shell)); } else { strncpy(shell + 1, pwd->pw_shell, sizeof(shell)); } shell[0] = '-'; /* Set permissions on the device. */ chown(tty, pwd->pw_uid, ttygrp ? ttygrp->gr_gid : 0); chmod(tty, 0620); /* Log the login. */ addToUtmp(tty, NULL, STDERR_FILENO); /* Fork. The child will exec the shell. */ child = fork(); if (child < 0) { fprintf(stderr, "Error fork()ing: %s\n", strerror(errno)); /* Don't leave an erroneous login behind. */ removeFromUtmp(); sleep(SLEEPYTIME); exit(1); } if (child == 0) { /* We're in the child. */ fprintf(stderr, ""); initgroups(pwd->pw_name, pwd->pw_gid); if (setuid(pwd->pw_uid) == 0) { init_environment(pamh, pwd); chdir(pwd->pw_dir); /* If we have an argv[3], execute it instead. */ if (argc >= 4) { execlp(argv[3], argv[3], NULL); } else { execlp(pwd->pw_shell, shell, NULL); } } else { fprintf(stderr, "Error becoming %s.\n", pwd->pw_name); } return 1; } if (child > 0) { /* We're in the parent. */ waitpid(child, NULL, 0); kill(child, SIGHUP); } /* Log the logout. */ removeFromUtmp(); /* Reset permissions on the device. */ chown(tty, st.st_uid, st.st_gid); chmod(tty, st.st_mode); /* Clean up. */ ret = pam_close_session(pamh, 0); if (ret != PAM_SUCCESS) { fprintf(stderr, "Error terminating session: %s\n", pam_strerror(pamh, ret)); sleep(SLEEPYTIME); exit(1); } ret = pam_setcred(pamh, PAM_DELETE_CRED); if (ret != PAM_SUCCESS) { fprintf(stderr, "Error deleting credentials: %s\n", pam_strerror(pamh, ret)); sleep(SLEEPYTIME); exit(1); } pam_end(pamh, PAM_SUCCESS); return 0; }