/****************************************************************************/
/* programma no sērijas - viens sistēmas useris | daudz virtuālo pop3 useru */
/* vpoppasswd formāts:                                                      */
/*   ::vpop:                   */
/*                                                                          */
/* kompilēt ar: gcc -o checkvirtpass checkvirtpass.c -lcrypt                */
/* kompilējot nekādus citus failus nevajag!                   (c) Mr.Goblins*/
/*                        FreeBSD as always ROCK                            */
/****************************************************************************/

#define CONFIG_FILE "/var/qmail/users/vpoppasswd"

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#ifndef LINE_MAX
# define LINE_MAX 2048
#endif

#ifndef LOGIN_MAX
# define LOGIN_MAX 16
#endif

extern int errno;

extern char *crypt();
extern char *malloc();
extern char **environ;

char up[513];
int uplen;

char remoteipz[25]="\0";

char *str1e2(char *name, char *value)
{
  char *nv;
  nv = malloc(strlen(name) + strlen(value) + 2);
  if (!nv) _exit(111);
  strcpy(nv,name);
  strcat(nv,"=");
  strcat(nv,value);
  return nv;
}

struct newpw {
  char *pw_passwd;
  char *pw_dir;   
  char *pw_name;      
  char *pw_shell;         
  uid_t pw_uid;
  gid_t pw_gid;
  char *pw_real;
} *newgetpwnam(char *newlogin)
{
  static char line[LINE_MAX + 1];
  static struct newpw npw;
  struct passwd *pw;
  char *tlogin = NULL;
  FILE *fp;

  if ((fp = fopen(CONFIG_FILE, "rt")) == NULL) _exit(2);
  while (fgets(line, LINE_MAX, fp) != NULL) {
    char *linepnt;
    if ((linepnt = strchr(line, '\n')) != NULL) {
      *linepnt = 0;
    }
    if ((linepnt = strtok(line, ":")) == NULL) _exit(2);
    if (strcmp(linepnt, newlogin) == 0) {
      npw.pw_real = linepnt;
      if ((linepnt = strtok(NULL, ":")) == NULL) _exit(2);
      npw.pw_passwd = linepnt;
      if ((linepnt = strtok(NULL, ":")) == NULL) _exit(2);
      *(linepnt - 1) = 0;
      tlogin = linepnt;
      if ((linepnt = strtok(NULL, ":")) == NULL) _exit(2);
      npw.pw_dir = linepnt;
      break;
    }
  }   
  fclose(fp);

  if (tlogin == NULL) _exit(1);
  pw = getpwnam(tlogin);
  if (!pw) _exit(1);

  npw.pw_name = tlogin;
  npw.pw_shell = pw->pw_shell;
  npw.pw_uid = pw->pw_uid;
  npw.pw_gid = pw->pw_gid;

  return &npw;
}

int main(int argc, char **argv, char *envp[])
{
  struct newpw *pw;
  char *login;
  char *password;
  char *stored;
  char *encrypted;
  int r;
  int i;
  char **newenv;
  int numenv;
  char **me;
  char *constr;
  char *cconv;
  int z;

for(me=envp;*me;me++) {
  long ptr;
  ptr = (long) *(me);
  if (strncmp(*me, "TCPREMOTEIP", 11) == 0) {
    ptr += 12;
    strcpy(remoteipz, (char*)(ptr));
  }
}

if (argc < 2) _exit(2);

uplen = 0;
for (;;) {
  do {
    r = read(3,up + uplen,sizeof(up) - uplen);
  } while ((r == -1) && (errno == EINTR));
  if (r == -1) _exit(111);
  if (r == 0) break;
  uplen += r;
  if (uplen >= sizeof(up)) _exit(1);
}

close(3);
   
i = 0;
login = up + i;
while (up[i++])
if (i == uplen) _exit(2);
password = up + i;
if (i == uplen) _exit(2);
while (up[i++])
if (i == uplen) _exit(2);

cconv = login;
while (*cconv) {
  if ((*cconv <= 90)&&(*cconv >= 65)) *cconv = *cconv + 32;
  cconv++;
}

pw = newgetpwnam(login);
if (!pw) _exit(1); 
stored = pw->pw_passwd;
encrypted = crypt(password,stored);

if (!*stored || strcmp(encrypted,stored)) {
  syslog(LOG_LOCAL2 | LOG_INFO, "password incorrect for user [%s] with password [%s] from [%s]", pw->pw_real, password, remoteipz);
  for (i = 0;i < sizeof(up);++i) up[i] = 0;
  _exit(1);
}
for (i = 0;i < sizeof(up);++i) up[i] = 0;

if (!pw->pw_uid) _exit(1);
if (setgid(pw->pw_gid) == -1) {
  syslog(LOG_LOCAL2 | LOG_INFO, "Unable to setgid() to user [%s] with group ID [%s]", pw->pw_real, pw->pw_gid);
  _exit(1);
}
if (setuid(pw->pw_uid) == -1) {
  syslog(LOG_LOCAL2 | LOG_INFO, "Unable to setuid() to user [%s] with user ID [%s]", pw->pw_real, pw->pw_gid);
  _exit(1);
}
if (chdir(pw->pw_dir) == -1) {
  syslog(LOG_LOCAL2 | LOG_INFO, "Unable to chdir() to user [%s] home directory [%s]", pw->pw_real, pw->pw_dir);
  _exit(111);
}

numenv = 0;
while (environ[numenv]) ++numenv;
newenv = (char **) malloc((numenv + 4) * sizeof(char *));
if (!newenv) _exit(111);
for (i = 0;i < numenv;++i) newenv[i] = environ[i];
newenv[numenv++] = str1e2("USER",pw->pw_name);
newenv[numenv++] = str1e2("HOME",pw->pw_dir);
newenv[numenv++] = str1e2("SHELL",pw->pw_shell);
newenv[numenv] = 0;
environ = newenv;
   
syslog(LOG_LOCAL2 | LOG_INFO, "user [%s] logged in from [%s]", pw->pw_real, remoteipz);
execvp(argv[1],argv + 1);
_exit(111);

return 0;
}