1 /*
2 * pam_securetty module
3 *
4 * by Elliot Lee <sopwith@redhat.com>, Red Hat Software.
5 * July 25, 1996.
6 * This code shamelessly ripped from the pam_rootok module.
7 * Slight modifications AGM. 1996/12/3
8 */
9
10 #include "config.h"
11
12 #include <stdio.h>
13 #include <stdlib.h>
14 #include <sys/types.h>
15 #include <sys/stat.h>
16 #include <unistd.h>
17 #include <syslog.h>
18 #include <stdarg.h>
19 #include <pwd.h>
20 #include <string.h>
21 #include <ctype.h>
22 #include <limits.h>
23 #include <errno.h>
24
25 #include <security/pam_modules.h>
26 #include <security/pam_modutil.h>
27 #include <security/pam_ext.h>
28 #include "pam_inline.h"
29
30 #define PAM_DEBUG_ARG 0x0001
31 #define PAM_NOCONSOLE_ARG 0x0002
32
33 #define SECURETTY_FILE "/etc/securetty"
34 #ifdef VENDORDIR
35 #define SECURETTY2_FILE VENDORDIR"/securetty"
36 #endif
37 #define TTY_PREFIX "/dev/"
38 #define CMDLINE_FILE "/proc/cmdline"
39 #define CONSOLEACTIVE_FILE "/sys/class/tty/console/active"
40
41 static int
42 _pam_parse (const pam_handle_t *pamh, int argc, const char **argv)
43 {
44 int ctrl=0;
45
46 /* step through arguments */
47 for (ctrl=0; argc-- > 0; ++argv) {
48
49 /* generic options */
50
51 if (!strcmp(*argv,"debug"))
52 ctrl |= PAM_DEBUG_ARG;
53 else if (!strcmp(*argv, "noconsole"))
54 ctrl |= PAM_NOCONSOLE_ARG;
55 else {
56 pam_syslog(pamh, LOG_ERR, "unknown option: %s", *argv);
57 }
58 }
59
60 return ctrl;
61 }
62
63 static int
64 securetty_perform_check (pam_handle_t *pamh, int ctrl,
65 const char *function_name)
66 {
67 int retval = PAM_AUTH_ERR;
68 const char *securettyfile;
69 const char *username;
70 const char *uttyname;
71 const char *str;
72 const void *void_uttyname;
73 char ttyfileline[256];
74 char ptname[256];
75 struct stat ttyfileinfo;
76 struct passwd *user_pwd;
77 FILE *ttyfile;
78
79 /* log a trail for debugging */
80 if (ctrl & PAM_DEBUG_ARG) {
81 pam_syslog(pamh, LOG_DEBUG, "pam_securetty called via %s function",
82 function_name);
83 }
84
85 retval = pam_get_user(pamh, &username, NULL);
86 if (retval != PAM_SUCCESS) {
87 pam_syslog(pamh, LOG_NOTICE, "cannot determine user name: %s",
88 pam_strerror(pamh, retval));
89 return (retval == PAM_CONV_AGAIN ? PAM_INCOMPLETE : retval);
90 }
91
92 user_pwd = pam_modutil_getpwnam(pamh, username);
93 if (user_pwd != NULL && user_pwd->pw_uid != 0) {
94 /* If the user is not root, securetty's does not apply to them */
95 return PAM_SUCCESS;
96 }
97 /* The user is now either root or an invalid / mistyped username */
98
99 retval = pam_get_item(pamh, PAM_TTY, &void_uttyname);
100 uttyname = void_uttyname;
101 if (retval != PAM_SUCCESS || uttyname == NULL) {
102 pam_syslog (pamh, LOG_ERR, "cannot determine user's tty");
103 return PAM_SERVICE_ERR;
104 }
105
106 /* The PAM_TTY item may be prefixed with "/dev/" - skip that */
107 if ((str = pam_str_skip_prefix(uttyname, TTY_PREFIX)) != NULL)
108 uttyname = str;
109
110 if (stat(SECURETTY_FILE, &ttyfileinfo)) {
111 #ifdef VENDORDIR
112 if (errno == ENOENT) {
113 if (stat(SECURETTY2_FILE, &ttyfileinfo)) {
114 if (ctrl & PAM_DEBUG_ARG)
115 pam_syslog(pamh, LOG_DEBUG,
116 "Couldn't open %s: %m", SECURETTY2_FILE);
117 return PAM_SUCCESS; /* for compatibility with old securetty handling,
118 this needs to succeed. But we still log the
119 error. */
120 }
121 securettyfile = SECURETTY2_FILE;
122 } else {
123 #endif
124 if (ctrl & PAM_DEBUG_ARG)
125 pam_syslog(pamh, LOG_DEBUG, "Couldn't open %s: %m", SECURETTY_FILE);
126 return PAM_SUCCESS; /* for compatibility with old securetty handling,
127 this needs to succeed. But we still log the
128 error. */
129 #ifdef VENDORDIR
130 }
131 #endif
132 } else {
133 securettyfile = SECURETTY_FILE;
134 }
135
136 if ((ttyfileinfo.st_mode & S_IWOTH) || !S_ISREG(ttyfileinfo.st_mode)) {
137 /* If the file is world writable or is not a
138 normal file, return error */
139 pam_syslog(pamh, LOG_ERR,
140 "%s is either world writable or not a normal file",
141 securettyfile);
142 return PAM_AUTH_ERR;
143 }
144
145 ttyfile = fopen(securettyfile,"r");
146 if (ttyfile == NULL) { /* Check that we opened it successfully */
147 pam_syslog(pamh, LOG_ERR, "Error opening %s: %m", securettyfile);
148 return PAM_SERVICE_ERR;
149 }
150
151 if (isdigit(uttyname[0])) {
152 snprintf(ptname, sizeof(ptname), "pts/%s", uttyname);
153 } else {
154 ptname[0] = '\0';
155 }
156
157 retval = 1;
158
159 while ((fgets(ttyfileline, sizeof(ttyfileline)-1, ttyfile) != NULL)
160 && retval) {
161 if (ttyfileline[strlen(ttyfileline) - 1] == '\n')
162 ttyfileline[strlen(ttyfileline) - 1] = '\0';
163
164 retval = ( strcmp(ttyfileline, uttyname)
165 && (!ptname[0] || strcmp(ptname, uttyname)) );
166 }
167 fclose(ttyfile);
168
169 if (retval && !(ctrl & PAM_NOCONSOLE_ARG)) {
170 FILE *cmdlinefile;
171
172 /* Allow access from the kernel console, if enabled */
173 cmdlinefile = fopen(CMDLINE_FILE, "r");
174
175 if (cmdlinefile != NULL) {
176 char line[LINE_MAX], *p;
177
178 p = fgets(line, sizeof(line), cmdlinefile);
179 fclose(cmdlinefile);
180
181 for (; p; p = strstr(p+1, "console=")) {
182 const char *e;
183
184 /* Test whether this is a beginning of a word? */
185 if (p > line && p[-1] != ' ')
186 continue;
187
188 /* Is this our console? */
189 if ((e = pam_str_skip_prefix_len(p + 8, uttyname, strlen(uttyname))) == NULL)
190 continue;
191
192 /* Is there any garbage after the TTY name? */
193 if (*e == ',' || *e == ' ' || *e == '\n' || *e == 0) {
194 retval = 0;
195 break;
196 }
197 }
198 }
199 }
200 if (retval && !(ctrl & PAM_NOCONSOLE_ARG)) {
201 FILE *consoleactivefile;
202
203 /* Allow access from the active console */
204 consoleactivefile = fopen(CONSOLEACTIVE_FILE, "r");
205
206 if (consoleactivefile != NULL) {
207 char line[LINE_MAX], *p, *n;
208
209 line[0] = 0;
210 p = fgets(line, sizeof(line), consoleactivefile);
211 fclose(consoleactivefile);
212
213 if (p) {
214 /* remove the newline character at end */
215 if (line[strlen(line)-1] == '\n')
216 line[strlen(line)-1] = 0;
217
218 for (n = p; n != NULL; p = n+1) {
219 if ((n = strchr(p, ' ')) != NULL)
220 *n = '\0';
221
222 if (strcmp(p, uttyname) == 0) {
223 retval = 0;
224 break;
225 }
226 }
227 }
228 }
229 }
230
231 if (retval) {
232 pam_syslog(pamh, LOG_NOTICE, "access denied: tty '%s' is not secure !",
233 uttyname);
234
235 retval = PAM_AUTH_ERR;
236 if (user_pwd == NULL) {
237 retval = PAM_USER_UNKNOWN;
238 }
239 } else {
240 if (ctrl & PAM_DEBUG_ARG) {
241 pam_syslog(pamh, LOG_DEBUG, "access allowed for '%s' on '%s'",
242 username, uttyname);
243 }
244 retval = PAM_SUCCESS;
245
246 }
247
248 return retval;
249 }
250
251 /* --- authentication management functions --- */
252
253 int pam_sm_authenticate(pam_handle_t *pamh, int flags UNUSED, int argc,
254 const char **argv)
255 {
256 int ctrl;
257
258 /* parse the arguments */
259 ctrl = _pam_parse (pamh, argc, argv);
260
261 return securetty_perform_check(pamh, ctrl, __FUNCTION__);
262 }
263
264 int
265 pam_sm_setcred (pam_handle_t *pamh UNUSED, int flags UNUSED,
266 int argc UNUSED, const char **argv UNUSED)
267 {
268 return PAM_SUCCESS;
269 }
270
271 /* --- account management functions --- */
272
273 int
274 pam_sm_acct_mgmt (pam_handle_t *pamh, int flags UNUSED,
275 int argc, const char **argv)
276 {
277 int ctrl;
278
279 /* parse the arguments */
280 ctrl = _pam_parse (pamh, argc, argv);
281
282 /* take the easy route */
283 return securetty_perform_check(pamh, ctrl, __FUNCTION__);
284 }
285
286 /* end of module definition */