static int last_login_write(pam_handle_t *pamh, int announce, int last_fd, uid_t uid, const char *user) { struct flock last_lock; struct lastlog last_login; time_t ll_time; const void *void_remote_host = NULL; const char *remote_host; const char *terminal_line; int retval = PAM_SUCCESS; /* rewind */ if (lseek(last_fd, sizeof(last_login) * (off_t) uid, SEEK_SET) < 0) { pam_syslog(pamh, LOG_ERR, "failed to lseek %s: %m", _PATH_LASTLOG); return PAM_SERVICE_ERR; } /* set this login date */ D(("set the most recent login time")); (void) time(&ll_time); /* set the time */ last_login.ll_time = ll_time; /* set the remote host */ if (pam_get_item(pamh, PAM_RHOST, &void_remote_host) != PAM_SUCCESS || void_remote_host == NULL) { remote_host = DEFAULT_HOST; } else { remote_host = void_remote_host; } /* copy to last_login */ last_login.ll_host[0] = '\0'; strncat(last_login.ll_host, remote_host, sizeof(last_login.ll_host)-1); /* set the terminal line */ terminal_line = get_tty(pamh); /* copy to last_login */ last_login.ll_line[0] = '\0'; strncat(last_login.ll_line, terminal_line, sizeof(last_login.ll_line)-1); terminal_line = NULL; D(("locking lastlog file")); /* now we try to lock this file-record exclusively; non-blocking */ memset(&last_lock, 0, sizeof(last_lock)); last_lock.l_type = F_WRLCK; last_lock.l_whence = SEEK_SET; last_lock.l_start = sizeof(last_login) * (off_t) uid; last_lock.l_len = sizeof(last_login); if (fcntl(last_fd, F_SETLK, &last_lock) < 0) { D(("locking %s failed..(waiting a little)", _PATH_LASTLOG)); pam_syslog(pamh, LOG_WARNING, "file %s is locked/write", _PATH_LASTLOG); sleep(LASTLOG_IGNORE_LOCK_TIME); } D(("writing to the lastlog file")); if (pam_modutil_write (last_fd, (char *) &last_login, sizeof (last_login)) != sizeof(last_login)) { pam_syslog(pamh, LOG_ERR, "failed to write %s: %m", _PATH_LASTLOG); retval = PAM_SERVICE_ERR; } last_lock.l_type = F_UNLCK; (void) fcntl(last_fd, F_SETLK, &last_lock); /* unlock */ D(("unlocked")); if (announce & LASTLOG_WTMP) { /* write wtmp entry for user */ logwtmp(last_login.ll_line, user, remote_host); } /* cleanup */ memset(&last_login, 0, sizeof(last_login)); return retval; }
static int _unix_run_update_binary(pam_handle_t *pamh, unsigned int ctrl, const char *user, const char *fromwhat, const char *towhat, int remember) { int retval, child, fds[2]; struct sigaction newsa, oldsa; D(("called.")); /* create a pipe for the password */ if (pipe(fds) != 0) { D(("could not make pipe")); return PAM_AUTH_ERR; } if (off(UNIX_NOREAP, ctrl)) { /* * This code arranges that the demise of the child does not cause * the application to receive a signal it is not expecting - which * may kill the application or worse. * * The "noreap" module argument is provided so that the admin can * override this behavior. */ memset(&newsa, '\0', sizeof(newsa)); newsa.sa_handler = SIG_DFL; sigaction(SIGCHLD, &newsa, &oldsa); } /* fork */ child = fork(); if (child == 0) { int i=0; struct rlimit rlim; static char *envp[] = { NULL }; char *args[] = { NULL, NULL, NULL, NULL, NULL, NULL }; char buffer[16]; /* XXX - should really tidy up PAM here too */ /* reopen stdin as pipe */ dup2(fds[0], STDIN_FILENO); if (getrlimit(RLIMIT_NOFILE,&rlim)==0) { if (rlim.rlim_max >= MAX_FD_NO) rlim.rlim_max = MAX_FD_NO; for (i=0; i < (int)rlim.rlim_max; i++) { if (i != STDIN_FILENO) close(i); } } /* exec binary helper */ args[0] = x_strdup(UPDATE_HELPER); args[1] = x_strdup(user); args[2] = x_strdup("update"); if (on(UNIX_SHADOW, ctrl)) args[3] = x_strdup("1"); else args[3] = x_strdup("0"); snprintf(buffer, sizeof(buffer), "%d", remember); args[4] = x_strdup(buffer); execve(UPDATE_HELPER, args, envp); /* should not get here: exit with error */ D(("helper binary is not available")); _exit(PAM_AUTHINFO_UNAVAIL); } else if (child > 0) { /* wait for child */ /* if the stored password is NULL */ int rc=0; if (fromwhat) pam_modutil_write(fds[1], fromwhat, strlen(fromwhat)+1); else pam_modutil_write(fds[1], "", 1); if (towhat) { pam_modutil_write(fds[1], towhat, strlen(towhat)+1); } else pam_modutil_write(fds[1], "", 1); close(fds[0]); /* close here to avoid possible SIGPIPE above */ close(fds[1]); rc=waitpid(child, &retval, 0); /* wait for helper to complete */ if (rc<0) { pam_syslog(pamh, LOG_ERR, "unix_update waitpid failed: %m"); retval = PAM_AUTHTOK_ERR; } else if (!WIFEXITED(retval)) { pam_syslog(pamh, LOG_ERR, "unix_update abnormal exit: %d", retval); retval = PAM_AUTHTOK_ERR; } else { retval = WEXITSTATUS(retval); } } else { D(("fork failed")); close(fds[0]); close(fds[1]); retval = PAM_AUTH_ERR; } if (off(UNIX_NOREAP, ctrl)) { sigaction(SIGCHLD, &oldsa, NULL); /* restore old signal handler */ } return retval; }
/* Do the actual work of creating a home dir */ static int create_homedir(const struct passwd *pwd, const char *source, const char *dest) { char remark[BUFSIZ]; DIR *d; struct dirent *dent; int retval = PAM_SESSION_ERR; /* Create the new directory */ if (rec_mkdir(dest, 0755) != 0) { pam_syslog(NULL, LOG_ERR, "unable to create directory %s: %m", dest); return PAM_PERM_DENIED; } /* See if we need to copy the skel dir over. */ if ((source == NULL) || (strlen(source) == 0)) { retval = PAM_SUCCESS; goto go_out; } /* Scan the directory */ d = opendir(source); if (d == NULL) { pam_syslog(NULL, LOG_DEBUG, "unable to read directory %s: %m", source); retval = PAM_PERM_DENIED; goto go_out; } for (dent = readdir(d); dent != NULL; dent = readdir(d)) { int srcfd; int destfd; int res; struct stat st; #ifndef PATH_MAX char *newsource = NULL, *newdest = NULL; /* track length of buffers */ int nslen = 0, ndlen = 0; int slen = strlen(source), dlen = strlen(dest); #else char newsource[PATH_MAX], newdest[PATH_MAX]; #endif /* Skip some files.. */ if (strcmp(dent->d_name,".") == 0 || strcmp(dent->d_name,"..") == 0) continue; /* Determine what kind of file it is. */ #ifndef PATH_MAX nslen = slen + strlen(dent->d_name) + 2; if (nslen <= 0) { retval = PAM_BUF_ERR; goto go_out; } if ((newsource = malloc(nslen)) == NULL) { retval = PAM_BUF_ERR; goto go_out; } sprintf(newsource, "%s/%s", source, dent->d_name); #else snprintf(newsource, sizeof(newsource), "%s/%s", source, dent->d_name); #endif if (lstat(newsource, &st) != 0) #ifndef PATH_MAX { free(newsource); newsource = NULL; continue; } #else continue; #endif /* We'll need the new file's name. */ #ifndef PATH_MAX ndlen = dlen + strlen(dent->d_name)+2; if (ndlen <= 0) { retval = PAM_BUF_ERR; goto go_out; } if ((newdest = malloc(ndlen)) == NULL) { free (newsource); retval = PAM_BUF_ERR; goto go_out; } sprintf (newdest, "%s/%s", dest, dent->d_name); #else snprintf (newdest, sizeof (newdest), "%s/%s", dest, dent->d_name); #endif /* If it's a directory, recurse. */ if (S_ISDIR(st.st_mode)) { retval = create_homedir(pwd, newsource, newdest); #ifndef PATH_MAX free(newsource); newsource = NULL; free(newdest); newdest = NULL; #endif if (retval != PAM_SUCCESS) { closedir(d); goto go_out; } continue; } /* If it's a symlink, create a new link. */ if (S_ISLNK(st.st_mode)) { int pointedlen = 0; #ifndef PATH_MAX char *pointed = NULL; { int size = 100; while (1) { pointed = malloc(size); if (pointed == NULL) { free(newsource); free(newdest); return PAM_BUF_ERR; } pointedlen = readlink(newsource, pointed, size); if (pointedlen < 0) break; if (pointedlen < size) break; free(pointed); size *= 2; } } if (pointedlen < 0) free(pointed); else pointed[pointedlen] = 0; #else char pointed[PATH_MAX]; memset(pointed, 0, sizeof(pointed)); pointedlen = readlink(newsource, pointed, sizeof(pointed) - 1); #endif if (pointedlen >= 0) { if(symlink(pointed, newdest) == 0) { if (lchown(newdest, pwd->pw_uid, pwd->pw_gid) != 0) { pam_syslog(NULL, LOG_DEBUG, "unable to change perms on link %s: %m", newdest); closedir(d); #ifndef PATH_MAX free(pointed); free(newsource); free(newdest); #endif return PAM_PERM_DENIED; } } #ifndef PATH_MAX free(pointed); #endif } #ifndef PATH_MAX free(newsource); newsource = NULL; free(newdest); newdest = NULL; #endif continue; } /* If it's not a regular file, it's probably not a good idea to create * the new device node, FIFO, or whatever it is. */ if (!S_ISREG(st.st_mode)) { #ifndef PATH_MAX free(newsource); newsource = NULL; free(newdest); newdest = NULL; #endif continue; } /* Open the source file */ if ((srcfd = open(newsource, O_RDONLY)) < 0 || fstat(srcfd, &st) != 0) { pam_syslog(NULL, LOG_DEBUG, "unable to open src file %s: %m", newsource); closedir(d); #ifndef PATH_MAX free(newsource); newsource = NULL; free(newdest); newdest = NULL; #endif return PAM_PERM_DENIED; } if (stat(newsource, &st) != 0) { pam_syslog(NULL, LOG_DEBUG, "unable to stat src file %s: %m", newsource); close(srcfd); closedir(d); #ifndef PATH_MAX free(newsource); newsource = NULL; free(newdest); newdest = NULL; #endif return PAM_PERM_DENIED; } /* Open the dest file */ if ((destfd = open(newdest, O_WRONLY | O_TRUNC | O_CREAT, 0600)) < 0) { pam_syslog(NULL, LOG_DEBUG, "unable to open dest file %s: %m", newdest); close(srcfd); closedir(d); #ifndef PATH_MAX free(newsource); newsource = NULL; free(newdest); newdest = NULL; #endif return PAM_PERM_DENIED; } /* Set the proper ownership and permissions for the module. We make the file a+w and then mask it with the set mask. This preseves execute bits */ if (fchmod(destfd, (st.st_mode | 0222) & (~u_mask)) != 0 || fchown(destfd, pwd->pw_uid, pwd->pw_gid) != 0) { pam_syslog(NULL, LOG_DEBUG, "unable to change perms on copy %s: %m", newdest); close(srcfd); close(destfd); closedir(d); #ifndef PATH_MAX free(newsource); newsource = NULL; free(newdest); newdest = NULL; #endif return PAM_PERM_DENIED; } /* Copy the file */ do { res = pam_modutil_read(srcfd, remark, sizeof(remark)); if (res == 0) continue; if (res > 0) { if (pam_modutil_write(destfd, remark, res) == res) continue; } /* If we get here, pam_modutil_read returned a -1 or pam_modutil_write returned something unexpected. */ pam_syslog(NULL, LOG_DEBUG, "unable to perform IO: %m"); close(srcfd); close(destfd); closedir(d); #ifndef PATH_MAX free(newsource); newsource = NULL; free(newdest); newdest = NULL; #endif return PAM_PERM_DENIED; } while (res != 0); close(srcfd); close(destfd); #ifndef PATH_MAX free(newsource); newsource = NULL; free(newdest); newdest = NULL; #endif } closedir(d); retval = PAM_SUCCESS; go_out: if (chmod(dest, 0777 & (~u_mask)) != 0 || chown(dest, pwd->pw_uid, pwd->pw_gid) != 0) { pam_syslog(NULL, LOG_DEBUG, "unable to change perms on directory %s: %m", dest); return PAM_PERM_DENIED; } return retval; }