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