1 /*
2 * This file implements the following functions:
3 * pam_modutil_sanitize_helper_fds:
4 * redirects standard descriptors, closes all other descriptors.
5 */
6
7 #include "pam_modutil_private.h"
8 #include <security/pam_ext.h>
9 #include <unistd.h>
10 #include <fcntl.h>
11 #include <syslog.h>
12 #include <sys/resource.h>
13
14 /*
15 * Creates a pipe, closes its write end, redirects fd to its read end.
16 * Returns fd on success, -1 otherwise.
17 */
18 static int
19 redirect_in_pipe(pam_handle_t *pamh, int fd, const char *name)
20 {
21 int in[2];
22
23 if (pipe(in) < 0) {
24 pam_syslog(pamh, LOG_ERR, "Could not create pipe: %m");
25 return -1;
26 }
27
28 close(in[1]);
29
30 if (in[0] == fd)
31 return fd;
32
33 if (dup2(in[0], fd) != fd) {
34 pam_syslog(pamh, LOG_ERR, "dup2 of %s failed: %m", name);
35 fd = -1;
36 }
37
38 close(in[0]);
39 return fd;
40 }
41
42 /*
43 * Opens /dev/null for writing, redirects fd there.
44 * Returns fd on success, -1 otherwise.
45 */
46 static int
47 redirect_out_null(pam_handle_t *pamh, int fd, const char *name)
48 {
49 int null = open("/dev/null", O_WRONLY);
50
51 if (null < 0) {
52 pam_syslog(pamh, LOG_ERR, "open of %s failed: %m", "/dev/null");
53 return -1;
54 }
55
56 if (null == fd)
57 return fd;
58
59 if (dup2(null, fd) != fd) {
60 pam_syslog(pamh, LOG_ERR, "dup2 of %s failed: %m", name);
61 fd = -1;
62 }
63
64 close(null);
65 return fd;
66 }
67
68 static int
69 redirect_out(pam_handle_t *pamh, enum pam_modutil_redirect_fd mode,
70 int fd, const char *name)
71 {
72 switch (mode) {
73 case PAM_MODUTIL_PIPE_FD:
74 if (redirect_in_pipe(pamh, fd, name) < 0)
75 return -1;
76 break;
77 case PAM_MODUTIL_NULL_FD:
78 if (redirect_out_null(pamh, fd, name) < 0)
79 return -1;
80 break;
81 case PAM_MODUTIL_IGNORE_FD:
82 break;
83 }
84 return fd;
85 }
86
87 /* Closes all descriptors after stderr. */
88 static void
89 close_fds(void)
90 {
91 /*
92 * An arbitrary upper limit for the maximum file descriptor number
93 * returned by RLIMIT_NOFILE.
94 */
95 const int MAX_FD_NO = 65535;
96
97 /* The lower limit is the same as for _POSIX_OPEN_MAX. */
98 const int MIN_FD_NO = 20;
99
100 int fd;
101 struct rlimit rlim;
102
103 if (getrlimit(RLIMIT_NOFILE, &rlim) || rlim.rlim_max > (rlim_t)MAX_FD_NO)
104 fd = MAX_FD_NO;
105 else if (rlim.rlim_max < (rlim_t)MIN_FD_NO)
106 fd = MIN_FD_NO;
107 else
108 fd = (int)rlim.rlim_max - 1;
109
110 for (; fd > STDERR_FILENO; --fd)
111 close(fd);
112 }
113
114 int
115 pam_modutil_sanitize_helper_fds(pam_handle_t *pamh,
116 enum pam_modutil_redirect_fd stdin_mode,
117 enum pam_modutil_redirect_fd stdout_mode,
118 enum pam_modutil_redirect_fd stderr_mode)
119 {
120 if (stdin_mode != PAM_MODUTIL_IGNORE_FD &&
121 redirect_in_pipe(pamh, STDIN_FILENO, "stdin") < 0) {
122 return -1;
123 }
124
125 if (redirect_out(pamh, stdout_mode, STDOUT_FILENO, "stdout") < 0)
126 return -1;
127
128 /*
129 * If stderr should not be ignored and
130 * redirect mode for stdout and stderr are the same,
131 * optimize by redirecting stderr to stdout.
132 */
133 if (stderr_mode != PAM_MODUTIL_IGNORE_FD &&
134 stdout_mode == stderr_mode) {
135 if (dup2(STDOUT_FILENO, STDERR_FILENO) != STDERR_FILENO) {
136 pam_syslog(pamh, LOG_ERR,
137 "dup2 of %s failed: %m", "stderr");
138 return -1;
139 }
140 } else {
141 if (redirect_out(pamh, stderr_mode, STDERR_FILENO, "stderr") < 0)
142 return -1;
143 }
144
145 close_fds();
146 return 0;
147 }