--- 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