Download | Plain Text | Line Numbers


#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <pwd.h>
#include <mntent.h>
#include <time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/quota.h>
 
#include <sys/sysmacros.h>
#define CMP_DEV_T(a, b) (major(a) == major(b) && minor(a) == minor(b))
 
#define LOGPREFIX "QUOTA: "
 
int check_parent(const char *username)
{
  pid_t parent;
  int num;
  char buf[256], buf2[256];
  const char *smtpbin = "/var/qmail/bin/qmail-smtpd";
 
  // check parent
  parent = getppid();
  num = snprintf(buf, sizeof(buf), "/proc/%d/exe", parent);
  if (num < 0 || num > sizeof(buf))
  {
    fprintf(stderr, LOGPREFIX "Unable to copy string to buffer for user \"%s\".\n", username);
    return 0;
  }
 
  num = readlink(buf, buf2, sizeof(buf2));
  if (num < 0)
  {
    fprintf(stderr, LOGPREFIX "Unable to read parent from proc-fs for user \"%s\": %s\n", username, strerror(errno));
    return 0;
  }
  buf2[num] = '\0';
 
  if (strcmp(smtpbin, buf2) != 0)
  {
    fprintf(stderr, LOGPREFIX "Error: Parent \"%s\" doesn't match qmail-smtp for user \"%s\".\n", buf2, username);
    return 0;
  }
 
  return 1;
}
 
int main()
{
  const char *username = NULL, *device_path = NULL;
  struct passwd *pw;
  struct stat st, st2;
  FILE *fp;
  struct mntent *ent;
  struct dqblk dq;
  time_t now;
  pid_t parent;
 
  parent = getppid();
  username = getenv("DTUSER");
  if (!username)
    return 0;
 
  // lookup user in passwd
  pw = getpwnam(username);
  if (pw == NULL)
  {
    fprintf(stderr, LOGPREFIX "Unable to get user \"%s\" from passwd!: %s\n", username, strerror(errno));
    return 0;
  }
 
  // seteuid
  if (seteuid(pw->pw_uid))
  {
    fprintf(stderr, LOGPREFIX "Unable to call seteuid for user \"%s\": %s\n", username, strerror(errno));
    return 0;
  }
 
  // check parent
  if (!check_parent(username))
    return 0;
 
  if (pw->pw_dir == NULL)
  {
    fprintf(stderr, LOGPREFIX "User \"%s\" has no homedir?\n", username);
    return 0;
  }
 
  // stat homedir to fetch blockdevice id
  if (stat(pw->pw_dir, &st) < 0)
  {
    fprintf(stderr, LOGPREFIX "Stat of \"%s\" of user \"%s\" failed: %s\n", pw->pw_dir, username, strerror(errno));
    return 0;
  }
 
  // iterate over mounted devices and check blockdevice id
  fp = setmntent(_PATH_MOUNTED, "r");
  if (fp == NULL)
  {
    fprintf(stderr, LOGPREFIX "Unable to open %s!\n", _PATH_MOUNTED);
    return 0;
  }
  while ((ent = getmntent(fp)) != NULL)
  {
    if (strcmp(ent->mnt_type, MNTTYPE_SWAP) == 0 ||
        strcmp(ent->mnt_type, MNTTYPE_IGNORE) == 0)
      continue;
 
    if (stat(ent->mnt_dir, &st2) == 0 &&
        CMP_DEV_T(st.st_dev, st2.st_dev))
    {
      device_path = ent->mnt_fsname;
      break;
    }
  }
  endmntent(fp);
 
  // nothing found...
  if (device_path == NULL)
  {
    fprintf(stderr, LOGPREFIX "Unable to get mountpoint of user \"%s\".\n", username);
    return 0;
  }
 
  // fetch quota
  if (quotactl(QCMD(Q_GETQUOTA, USRQUOTA), device_path, pw->pw_uid, (caddr_t) &dq))
  {
    fprintf(stderr, LOGPREFIX "Unable to get quota of user \"%s\": %s.\n", username, strerror(errno));
    return 0;
  }
 
  now = time(NULL);
  if ((dq.dqb_bhardlimit && btodb(dq.dqb_curspace) >= dq.dqb_bhardlimit)
      || (dq.dqb_ihardlimit && dq.dqb_curinodes >= dq.dqb_ihardlimit)
      || (dq.dqb_bsoftlimit && btodb(dq.dqb_curspace) >= dq.dqb_bsoftlimit && now >= dq.dqb_btime)
      || (dq.dqb_isoftlimit && dq.dqb_curinodes >= dq.dqb_isoftlimit && now >= dq.dqb_itime))
  {
    fprintf(stderr, LOGPREFIX "User \"%s\" is over quota.\n", username);
    printf("E553 User over quota. (#5.1.1)\r\n");
  }
 
  return 0;
}