aboutsummaryrefslogtreecommitdiff
path: root/libs/libpam/patches/020-fgetpwent_r.patch
blob: cc8042997c47432443ccccf82291ab1d255407f3 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
--- a/modules/pam_faillock/pam_faillock.c
+++ b/modules/pam_faillock/pam_faillock.c
@@ -348,42 +348,81 @@ set_conf_opt(pam_handle_t *pamh, struct options *opts, const char *name, const c
 static int
 check_local_user (pam_handle_t *pamh, const char *user)
 {
-	struct passwd pw, *pwp;
-	char buf[16384];
-	int found = 0;
+	int rc;
+	size_t user_len;
 	FILE *fp;
-	int errn;
+	char line[BUFSIZ];
 
-	fp = fopen(PATH_PASSWD, "r");
-	if (fp == NULL) {
-		pam_syslog(pamh, LOG_ERR, "unable to open %s: %m",
-			   PATH_PASSWD);
-		return -1;
+	/* Validate the user name.  */
+	if ((user_len = strlen(user)) == 0) {
+		pam_syslog(pamh, LOG_NOTICE, "user name is not valid");
+		return PAM_SERVICE_ERR;
+	}
+
+	if (user_len > sizeof(line) - sizeof(":")) {
+		pam_syslog(pamh, LOG_NOTICE, "user name is too long");
+		return PAM_SERVICE_ERR;
+	}
+
+	if (strchr(user, ':') != NULL) {
+		/*
+		 * "root:x" is not a local user name even if the passwd file
+		 * contains a line starting with "root:x:".
+		 */
+		return PAM_PERM_DENIED;
 	}
 
-	for (;;) {
-		errn = fgetpwent_r(fp, &pw, buf, sizeof (buf), &pwp);
-		if (errn == ERANGE) {
-			pam_syslog(pamh, LOG_WARNING, "%s contains very long lines; corrupted?",
-				   PATH_PASSWD);
+	/* Open the passwd file.  */
+	FILE *file_name = "/etc/passwd";
+	if ((fp = fopen(file_name, "r")) == NULL) {
+		pam_syslog(pamh, LOG_ERR, "error opening %s: %m", file_name);
+		return PAM_SERVICE_ERR;
+	}
+
+	/*
+	 * Scan the file using fgets() instead of fgetpwent_r() because
+	 * the latter is not flexible enough in handling long lines
+	 * in passwd files.
+	 */
+	rc = PAM_PERM_DENIED;
+	while (fgets(line, sizeof(line), fp) != NULL) {
+		size_t line_len;
+		const char *str;
+
+		/*
+		 * Does this line start with the user name
+		 * followed by a colon?
+		 */
+		if (strncmp(user, line, user_len) == 0 &&
+		    line[user_len] == ':') {
+			rc = PAM_SUCCESS;
 			break;
 		}
-		if (errn != 0)
-			break;
-		if (strcmp(pwp->pw_name, user) == 0) {
-			found = 1;
+		/* Has a newline been read?  */
+		line_len = strlen(line);
+		if (line_len < sizeof(line) - 1 ||
+		    line[line_len - 1] == '\n') {
+			/* Yes, continue with the next line.  */
+			continue;
+		}
+
+		/* No, read till the end of this line first.  */
+		while ((str = fgets(line, sizeof(line), fp)) != NULL) {
+			line_len = strlen(line);
+			if (line_len == 0 ||
+			    line[line_len - 1] == '\n') {
+				break;
+			}
+		}
+		if (str == NULL) {
+			/* fgets returned NULL, we are done.  */
 			break;
 		}
+		/* Continue with the next line.  */
 	}
 
-	fclose (fp);
-
-	if (errn != 0 && errn != ENOENT) {
-		pam_syslog(pamh, LOG_ERR, "unable to enumerate local accounts: %m");
-		return -1;
-	} else {
-		return found;
-	}
+	fclose(fp);
+	return rc;
 }
 
 static int