Package: pam
Version: 1.1.8-3.6
Severity: important
Tags: patch, upstream
I noticed that sometimes PAM authentication takes longer than expected
since I upgraded from jessie to stretch. Stracing a binary that calls
pam_authenticate showed that it was trying to close one million files.
Turns out that since stretch, the standard ulimit for number of open
files has changed. Soft limit is still 1024, hard limit went from 65536
to 1048576.
$ ulimit -n
1024
$ ulimit -H -n
1048576
In modules/pam_unix/ there are several loops like this:
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);
}
}
This is present in pam_unix_acct.c, pam_unix_passwd.c and support.c
Fortunately the loop is limited by MAX_FD_NO, which surely has a sane value:
#define MAX_FD_NO 2000000
Hmm.
This is overkill. I would change "rlim_max" to "rlim_cur" at least, and
perhaps lower MAX_FD_NO to something more reasonable.
If it really is important to close *all* open files, it would be better
to add something like the below, and replace the loops with
pam_close_all_files(<<lowest fileno e.g. STDIN_FILENO>> + 1).
#include <sys/types.h>
#include <sys/resource.h>
#include <dirent.h>
#include <stdlib.h>
#include <unistd.h>
#define MAX_FD_NO 100000
#ifdef __linux__
int pam_close_all_files_procfs(int start_at_fd)
{
DIR *dir = opendir("/proc/self/fd");
if (dir == NULL)
return -1;
int dfd = dirfd(dir);
struct dirent de, *de_p;
while (readdir_r(dir, &de, &de_p) == 0 && de_p != NULL) {
char *endp;
int fd = (int)strtol(de.d_name, &endp, 10);
if (endp != de.d_name && fd >= start_at_fd && fd != dfd) {
close(fd);
}
}
closedir(dir);
return 0;
}
#endif
void pam_close_all_files(int start_at_fd)
{
#ifdef __linux__
if (pam_close_all_files_procfs(start_at_fd) == 0)
return;
#endif
struct rlimit rlim;
if (getrlimit(RLIMIT_NOFILE,&rlim)==0) {
if (rlim.rlim_cur >= MAX_FD_NO)
rlim.rlim_cur = MAX_FD_NO;
for (int i = 0; i < (int)rlim.rlim_cur; i++) {
if (i >= start_at_fd)
close(i);
}
}
}
#ifdef TEST
int main()
{
pam_close_all_files(0);
}
#endif
Thanks,
Mike.