Blob Blame History Raw
--- modules/mod_auth_file.c
+++ modules/mod_auth_file.c
@@ -1,7 +1,7 @@
 /*
  * ProFTPD: mod_auth_file - file-based authentication module that supports
  *                          restrictions on the file contents
- * Copyright (c) 2002-2016 The ProFTPD Project team
+ * Copyright (c) 2002-2021 The ProFTPD Project team
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -40,10 +40,6 @@
 # error "ProFTPD 1.2.7rc2 or later required"
 #endif
 
-#ifndef BUFSIZ
-# define BUFSIZ          PR_TUNABLE_BUFFER_SIZE
-#endif /* !BUFSIZ */
-
 module auth_file_module;
 
 typedef union {
@@ -54,7 +50,7 @@ typedef union {
 
 typedef struct file_rec {
   char *af_path;
-  FILE *af_file;
+  pr_fh_t *af_file_fh;
   unsigned int af_lineno;
 
   unsigned char af_restricted_ids;
@@ -242,18 +238,16 @@ static int af_check_file(pool *p, const
   return 0;
 }
 
-#ifndef HAVE_FGETPWENT
-
 #define NPWDFIELDS      7
 
-static char pwdbuf[BUFSIZ];
+static char pwdbuf[PR_TUNABLE_BUFFER_SIZE];
 static char *pwdfields[NPWDFIELDS];
 static struct passwd pwent;
 
 static struct passwd *af_getpasswd(const char *buf, unsigned int lineno) {
   register unsigned int i;
   register char *cp = NULL;
-  char *ep = NULL, *buffer = NULL;
+  char *ptr = NULL, *buffer = NULL;
   char **fields = NULL;
   struct passwd *pwd = NULL;
 
@@ -261,8 +255,8 @@ static struct passwd *af_getpasswd(const
   buffer = pwdbuf;
   pwd = &pwent;
 
-  sstrncpy(buffer, buf, BUFSIZ-1);
-  buffer[BUFSIZ-1] = '\0';
+  sstrncpy(buffer, buf, PR_TUNABLE_BUFFER_SIZE-1);
+  buffer[PR_TUNABLE_BUFFER_SIZE-1] = '\0';
 
   for (cp = buffer, i = 0; i < NPWDFIELDS && cp; i++) {
     fields[i] = cp;
@@ -279,27 +273,39 @@ static struct passwd *af_getpasswd(const
   }
 
   if (i != NPWDFIELDS) {
-    pr_log_pri(PR_LOG_ERR, "Malformed entry in AuthUserFile file (line %u)",
-      lineno);
+    pr_log_pri(PR_LOG_ERR,
+      "Malformed entry in AuthUserFile file (field count %d != %d, line %u)",
+      i, (int) NPWDFIELDS, lineno);
     return NULL;
   }
 
+  pwd->pw_name = fields[0];
+  pwd->pw_passwd = fields[1];
+
   if (*fields[2] == '\0' ||
       *fields[3] == '\0') {
+    pr_trace_msg(trace_channel, 3,
+      "missing UID/GID fields for user '%.100s' (line %u), skipping",
+      pwd->pw_name, lineno);
     return NULL;
   }
 
-  pwd->pw_name = fields[0];
-  pwd->pw_passwd = fields[1];
-
-  if (fields[2][0] == '\0' ||
-     ((pwd->pw_uid = strtol(fields[2], &ep, 10)) == 0 && *ep)) {
-       return NULL;
+  ptr = NULL;
+  pwd->pw_uid = strtol(fields[2], &ptr, 10);
+  if (*ptr != '\0') {
+    pr_trace_msg(trace_channel, 3,
+      "non-numeric UID field '%.100s' for user '%.100s' (line %u), skipping",
+      fields[2], pwd->pw_name, lineno);
+    return NULL;
   }
 
-  if (fields[3][0] == '\0' ||
-     ((pwd->pw_gid = strtol(fields[3], &ep, 10)) == 0 && *ep)) {
-       return NULL;
+  ptr = NULL;
+  pwd->pw_gid = strtol(fields[3], &ptr, 10);
+  if (*ptr != '\0') {
+    pr_trace_msg(trace_channel, 3,
+      "non-numeric GID field '%.100s' for user '%.100s' (line %u), skipping",
+      fields[3], pwd->pw_name, lineno);
+    return NULL;
   }
 
   pwd->pw_gecos = fields[4];
@@ -308,9 +314,6 @@ static struct passwd *af_getpasswd(const
 
   return pwd;
 }
-#endif /* !HAVE_FGETPWENT */
-
-#ifndef HAVE_FGETGRENT
 
 #define MAXMEMBERS	4096
 #define NGRPFIELDS      4
@@ -320,11 +323,11 @@ static struct group grent;
 static char *grpfields[NGRPFIELDS];
 static char *members[MAXMEMBERS+1];
 
-static char *af_getgrentline(char **buf, int *buflen, FILE *fp,
+static char *af_getgrentline(char **buf, int *buflen, pr_fh_t *fh,
     unsigned int *lineno) {
   char *cp = *buf;
 
-  while (fgets(cp, (*buflen) - (cp - *buf), fp) != NULL) {
+  while (pr_fsio_gets(cp, (*buflen) - (cp - *buf), fh) != NULL) {
     pr_signals_handle();
 
     (*lineno)++;
@@ -435,7 +438,6 @@ static struct group *af_getgrp(const cha
 
   return &grent;
 }
-#endif /* !HAVE_FGETGRENT */
 
 static int af_allow_grent(pool *p, struct group *grp) {
   if (af_group_file == NULL) {
@@ -489,9 +491,9 @@ static int af_allow_grent(pool *p, struc
 
 static void af_endgrent(void) {
   if (af_group_file &&
-      af_group_file->af_file) {
-    fclose(af_group_file->af_file);
-    af_group_file->af_file = NULL;
+      af_group_file->af_file_fh != NULL) {
+    pr_fsio_close(af_group_file->af_file_fh);
+    af_group_file->af_file_fh = NULL;
     af_group_file->af_lineno = 0;
   }
 
@@ -502,29 +504,25 @@ static struct group *af_getgrent(pool *p
   struct group *grp = NULL, *res = NULL;
 
   if (!af_group_file ||
-      !af_group_file->af_file) {
+      af_group_file->af_file_fh == NULL) {
     errno = EINVAL;
     return NULL;
   }
 
   while (TRUE) {
-#ifdef HAVE_FGETGRENT
-    pr_signals_handle();
-    grp = fgetgrent(af_group_file->af_file);
-#else
     char *cp = NULL, *buf = NULL;
-    int buflen = BUFSIZ;
+    int buflen = PR_TUNABLE_BUFFER_SIZE;
 
     pr_signals_handle();
 
-    buf = malloc(BUFSIZ);
+    buf = malloc(buflen);
     if (buf == NULL) {
       pr_log_pri(PR_LOG_ALERT, "Out of memory!");
       _exit(1);
     }
     grp = NULL;
 
-    while (af_getgrentline(&buf, &buflen, af_group_file->af_file,
+    while (af_getgrentline(&buf, &buflen, af_group_file->af_file_fh,
         &(af_group_file->af_lineno)) != NULL) {
 
       pr_signals_handle();
@@ -541,11 +539,16 @@ static struct group *af_getgrent(pool *p
       }
 
       grp = af_getgrp(buf, af_group_file->af_lineno);
+
+      /* If grp is NULL here, it's a malformed entry; keep looking. */
+      if (grp == NULL) {
+        continue;
+      }
+
       free(buf);
 
       break;
     }
-#endif /* !HAVE_FGETGRENT */
 
     /* If grp is NULL now, the file is empty - nothing more to be read. */
     if (grp == NULL) {
@@ -604,22 +607,33 @@ static struct group *af_getgrgid(pool *p
 static int af_setgrent(pool *p) {
 
   if (af_group_file != NULL) {
-    if (af_group_file->af_file != NULL) {
+
+    if (af_group_file->af_file_fh != NULL) {
+      pr_buffer_t *pbuf;
+
       /* If already opened, rewind */
-      rewind(af_group_file->af_file);
+      (void) pr_fsio_lseek(af_group_file->af_file_fh, 0, SEEK_SET);
+
+      /* Make sure to clear any buffers as well. */
+      pbuf = af_group_file->af_file_fh->fh_buf;
+      if (pbuf != NULL) {
+        memset(pbuf->buf, '\0', pbuf->buflen);
+        pbuf->current = pbuf->buf;
+        pbuf->remaining = pbuf->buflen;
+      }
+
       return 0;
 
     } else {
       int xerrno;
+      struct stat st;
 
       PRIVS_ROOT
-      af_group_file->af_file = fopen(af_group_file->af_path, "r");
+      af_group_file->af_file_fh = pr_fsio_open(af_group_file->af_path, O_RDONLY);
       xerrno = errno;
       PRIVS_RELINQUISH
 
-      if (af_group_file->af_file == NULL) {
-        struct stat st;
-
+      if (af_group_file->af_file_fh == NULL) {
         if (pr_fsio_stat(af_group_file->af_path, &st) == 0) {
           pr_log_pri(PR_LOG_WARNING,
             "error: unable to open AuthGroupFile file '%s' (file owned by "
@@ -639,15 +653,15 @@ static int af_setgrent(pool *p) {
         return -1;
       }
 
-      /* As the file may contain sensitive data, we do not want it lingering
-       * around in stdio buffers.
-       */
-      (void) setvbuf(af_group_file->af_file, NULL, _IONBF, 0);
+      /* Set the optimum buffer/block size for this filehandle. */
+      if (pr_fsio_fstat(af_group_file->af_file_fh, &st) == 0) {
+        af_group_file->af_file_fh->fh_iosz = st.st_blksize;
+      }
 
-      if (fcntl(fileno(af_group_file->af_file), F_SETFD, FD_CLOEXEC) < 0) {
+      if (fcntl(PR_FH_FD(af_group_file->af_file_fh), F_SETFD, FD_CLOEXEC) < 0) {
         pr_log_pri(PR_LOG_WARNING, MOD_AUTH_FILE_VERSION
           ": unable to set CLOEXEC on AuthGroupFile %s (fd %d): %s",
-          af_group_file->af_path, fileno(af_group_file->af_file),
+          af_group_file->af_path, PR_FH_FD(af_group_file->af_file_fh),
           strerror(errno));
       }
 
@@ -731,9 +745,9 @@ static int af_allow_pwent(pool *p, struc
 
 static void af_endpwent(void) {
   if (af_user_file &&
-      af_user_file->af_file) {
-    fclose(af_user_file->af_file);
-    af_user_file->af_file = NULL;
+      af_user_file->af_file_fh != NULL) {
+    pr_fsio_close(af_user_file->af_file_fh);
+    af_user_file->af_file_fh = NULL;
     af_user_file->af_lineno = 0;
   }
 
@@ -744,24 +758,20 @@ static struct passwd *af_getpwent(pool *
   struct passwd *pwd = NULL, *res = NULL;
 
   if (af_user_file == NULL ||
-      af_user_file->af_file == NULL) {
+      af_user_file->af_file_fh == NULL) {
     errno = EINVAL;
     return NULL;
   }
 
   while (TRUE) {
-#ifdef HAVE_FGETPWENT
-    pr_signals_handle();
-    pwd = fgetpwent(af_user_file->af_file);
-#else
-    char buf[BUFSIZ+1] = {'\0'};
+    char buf[PR_TUNABLE_BUFFER_SIZE+1] = {'\0'};
 
     pr_signals_handle();
 
     memset(buf, '\0', sizeof(buf));
     pwd = NULL;
 
-    while (fgets(buf, sizeof(buf)-1, af_user_file->af_file) != NULL) {
+    while (pr_fsio_gets(buf, sizeof(buf)-1, af_user_file->af_file_fh) != NULL) {
       pr_signals_handle();
 
       af_user_file->af_lineno++;
@@ -775,9 +785,15 @@ static struct passwd *af_getpwent(pool *
 
       buf[strlen(buf)-1] = '\0';
       pwd = af_getpasswd(buf, af_user_file->af_lineno);
+
+      /* If pwd is NULL here, it's a malformed entry; keep looking. */
+      if (pwd == NULL) {
+        memset(buf, '\0', sizeof(buf));
+        continue;
+      }
+
       break;
     }
-#endif /* !HAVE_FGETPWENT */
 
     /* If pwd is NULL now, the file is empty - nothing more to be read. */
     if (pwd == NULL) {
@@ -785,9 +801,7 @@ static struct passwd *af_getpwent(pool *
     }
 
     if (af_allow_pwent(p, pwd) < 0) {
-#ifndef HAVE_FGETPWENT
       memset(buf, '\0', sizeof(buf));
-#endif
       continue;
     }
 
@@ -844,22 +858,33 @@ static struct passwd *af_getpwuid(pool *
 static int af_setpwent(pool *p) {
 
   if (af_user_file != NULL) {
-    if (af_user_file->af_file != NULL) {
+
+    if (af_user_file->af_file_fh != NULL) {
+      pr_buffer_t *pbuf;
+
       /* If already opened, rewind */
-      rewind(af_user_file->af_file);
+      (void) pr_fsio_lseek(af_user_file->af_file_fh, 0, SEEK_SET);
+
+      /* Make sure to clear any buffers as well. */
+      pbuf = af_user_file->af_file_fh->fh_buf;
+      if (pbuf != NULL) {
+        memset(pbuf->buf, '\0', pbuf->buflen);
+        pbuf->current = pbuf->buf;
+        pbuf->remaining = pbuf->buflen;
+      }
+
       return 0;
 
     } else {
       int xerrno;
+      struct stat st;
 
       PRIVS_ROOT
-      af_user_file->af_file = fopen(af_user_file->af_path, "r");
+      af_user_file->af_file_fh = pr_fsio_open(af_user_file->af_path, O_RDONLY);
       xerrno = errno;
       PRIVS_RELINQUISH
 
-      if (af_user_file->af_file == NULL) {
-        struct stat st;
-
+      if (af_user_file->af_file_fh == NULL) {
         if (pr_fsio_stat(af_user_file->af_path, &st) == 0) {
           pr_log_pri(PR_LOG_WARNING,
             "error: unable to open AuthUserFile file '%s' (file owned by "
@@ -879,15 +904,15 @@ static int af_setpwent(pool *p) {
         return -1;
       }
 
-      /* As the file may contain sensitive data, we do not want it lingering
-       * around in stdio buffers.
-       */
-      (void) setvbuf(af_user_file->af_file, NULL, _IONBF, 0);
+      /* Set the optimum buffer/block size for this filehandle. */
+      if (pr_fsio_fstat(af_user_file->af_file_fh, &st) == 0) {
+        af_user_file->af_file_fh->fh_iosz = st.st_blksize;
+      }
 
-      if (fcntl(fileno(af_user_file->af_file), F_SETFD, FD_CLOEXEC) < 0) {
+      if (fcntl(PR_FH_FD(af_user_file->af_file_fh), F_SETFD, FD_CLOEXEC) < 0) {
         pr_log_pri(PR_LOG_WARNING, MOD_AUTH_FILE_VERSION
           ": unable to set CLOEXEC on AuthUserFile %s (fd %d): %s",
-          af_user_file->af_path, fileno(af_user_file->af_file),
+          af_user_file->af_path, PR_FH_FD(af_user_file->af_file_fh),
           strerror(errno));
       }
 
@@ -1745,4 +1770,3 @@ module auth_file_module = {
   /* Module version */
   MOD_AUTH_FILE_VERSION
 };
-