1 #include "pam_modutil_private.h"
2 #include <security/pam_ext.h>
3
4 #include <stdio.h>
5 #include <string.h>
6 #include <syslog.h>
7
8 int
9 pam_modutil_check_user_in_passwd(pam_handle_t *pamh,
10 const char *user_name,
11 const char *file_name)
12 {
13 int rc;
14 size_t user_len;
15 FILE *fp;
16 char line[BUFSIZ];
17
18 /* Validate the user name. */
19 if ((user_len = strlen(user_name)) == 0) {
20 pam_syslog(pamh, LOG_NOTICE, "user name is not valid");
21 return PAM_SERVICE_ERR;
22 }
23
24 if (user_len > sizeof(line) - sizeof(":")) {
25 pam_syslog(pamh, LOG_NOTICE, "user name is too long");
26 return PAM_SERVICE_ERR;
27 }
28
29 if (strchr(user_name, ':') != NULL) {
30 /*
31 * "root:x" is not a local user name even if the passwd file
32 * contains a line starting with "root:x:".
33 */
34 return PAM_PERM_DENIED;
35 }
36
37 /* Open the passwd file. */
38 if (file_name == NULL) {
39 file_name = "/etc/passwd";
40 }
41 if ((fp = fopen(file_name, "r")) == NULL) {
42 pam_syslog(pamh, LOG_ERR, "error opening %s: %m", file_name);
43 return PAM_SERVICE_ERR;
44 }
45
46 /*
47 * Scan the file using fgets() instead of fgetpwent_r() because
48 * the latter is not flexible enough in handling long lines
49 * in passwd files.
50 */
51 rc = PAM_PERM_DENIED;
52 while (fgets(line, sizeof(line), fp) != NULL) {
53 size_t line_len;
54 const char *str;
55
56 /*
57 * Does this line start with the user name
58 * followed by a colon?
59 */
60 if (strncmp(user_name, line, user_len) == 0 &&
61 line[user_len] == ':') {
62 rc = PAM_SUCCESS;
63 /*
64 * Continue reading the file to avoid timing attacks.
65 */
66 }
67 /* Has a newline been read? */
68 line_len = strlen(line);
69 if (line_len < sizeof(line) - 1 ||
70 line[line_len - 1] == '\n') {
71 /* Yes, continue with the next line. */
72 continue;
73 }
74
75 /* No, read till the end of this line first. */
76 while ((str = fgets(line, sizeof(line), fp)) != NULL) {
77 line_len = strlen(line);
78 if (line_len == 0 ||
79 line[line_len - 1] == '\n') {
80 break;
81 }
82 }
83 if (str == NULL) {
84 /* fgets returned NULL, we are done. */
85 break;
86 }
87 /* Continue with the next line. */
88 }
89
90 fclose(fp);
91 return rc;
92 }