/*
 * ncsa_auth.c
 *
 * AUTHOR: Arjan de Vet <Arjan.deVet@adv.iae.nl>
 *
 * Example authentication program for Squid, based on the original
 * proxy_auth code from client_side.c, written by
 * Jon Thackray <jrmt@uk.gdscorp.com>.
 *
 * Uses a NCSA httpd style password file for authentication with the
 * following improvements suggested by various people:
 *
 * - comment lines are possible and should start with a '#';
 * - empty or blank lines are possible;
 * - extra fields in the password file are ignored; this makes it
 *   possible to use a Unix password file but I do not recommend that.
 *
 */
#define POKA 0
#define INCL_DOS
#include <os2.h>


#include <stdio.h>
#include <io.h>
#include <stdlib.h>
//#if HAVE_UNISTD_H
//#include <unistd.h>
//#endif
#include <string.h>
//#if HAVE_SYS_TYPES_H
#include <sys/types.h>
//#endif
//#if HAVE_SYS_STAT_H
#include <sys/stat.h>
//#endif
//#if HAVE_CRYPT_H
//#include <crypt.h>
//#endif

#include <time.h>

typedef struct _user_data {
    /* first two items must be same as hash_link */
    char *user;
//    struct _user_data *next;
    char *passwd;
} user_data;

int iscrypt(char *passwd, char *cryptPasswd);
int MoveReadableToBin(char *buf,char *outbuf, int inlen, int &outlen);
int MoveBinToReadable(char *buf,char *outbuf, int inlen, int &outlen);
int evgEncript(char *inbuf, char *outbuf, int inlen, int &outlen,int ltime);
int evgDecript(char *inbuf, char *outbuf, int inlen, int &outlen, char *keyword);
int FileDeCript(char *fname, char *keyword);
int FileCript(char *fname, char *keyword);


struct usersinfo
{  int n; /* number of users */
   int lu,lp;
   user_data *pUserData;
   char *pUsersBuff;
   char *pPasswsBuff;
};

struct usersinfo UsersInf = { 0,0,0, NULL, NULL,NULL};
char *MasterKeyWord = "os2rulez";


static void
read_passwd_file(const char *passwdfile)
{
    FILE *fp;
    char buf[8192];
    user_data *u;
    char *user;
    char *passwd;
    int lusers,lpasswds,lu,lp,n;

    fp = fopen(passwdfile, "r");
    UsersInf.lu = UsersInf.lp = 0;
    UsersInf.n = 0;
    lpasswds = lusers = 0;
    while (fgets(buf, 8192, fp) != NULL) {
       if ((buf[0] == '#') || (buf[0] == ' ') || (buf[0] == '\t') ||
           (buf[0] == '\n'))
           continue;
       user = strtok(buf, ":\n");
       passwd = strtok(NULL, ":\n");
       lu = strlen(user);
       if ((lu > 0) && passwd) {
            lp = strlen(passwd);
           lusers += (lu+1);
           lpasswds += (lp+1);
           UsersInf.n++;
       }
    }
/* memory allocation */
    if(UsersInf.pUsersBuff)  free(UsersInf.pUsersBuff);
    if(UsersInf.pPasswsBuff) free(UsersInf.pPasswsBuff);
    if(UsersInf.pUserData)   free(UsersInf.pUserData);
    UsersInf.pUsersBuff = (char *)calloc(lusers+1,1);
    UsersInf.pPasswsBuff = (char *)calloc(lpasswds+1,1);
    UsersInf.pUserData = (user_data *)calloc(UsersInf.n+1,sizeof(user_data));
    UsersInf.lu = lusers;
    UsersInf.lp = lpasswds;

   fseek(fp,0,SEEK_SET);

    lpasswds = lusers = n = 0;
    while (fgets(buf, 8192, fp) != NULL) {
       if ((buf[0] == '#') || (buf[0] == ' ') || (buf[0] == '\t') ||
           (buf[0] == '\n'))
           continue;
       user = strtok(buf, ":\n");
       passwd = strtok(NULL, ":\n");
       if ((strlen(user) > 0) && passwd) {
           lu = strlen(user);
           lp = strlen(passwd);
           strcpy(&UsersInf.pUsersBuff[lusers],user);
           strcpy(&UsersInf.pPasswsBuff[lpasswds],passwd);
           UsersInf.pUserData[n].user = &UsersInf.pUsersBuff[lusers];
           UsersInf.pUserData[n].passwd = &UsersInf.pPasswsBuff[lpasswds];
           n++;
           lusers   += lu+1;
           lpasswds += lp+1;
       }
    }
    fclose(fp);
}

int addUser(char *fname,char *user, char *passw)
{  FILE *fp;
   int i,isuser;
   char pswdbuff[6192];
   char pswdRbuff[8192];
   int inlen, outlen,crtime;
   time_t ltime;
   time(&ltime);
   crtime = (int) ltime;
//printf("Passw=%s (%i)\n", passw,strlen(passw));
   fp = fopen(fname,"w");
   isuser = -1;
/* del spaces */
   inlen = strlen(user);
   for(i=inlen-1;i;i--)
   {  if((int)user[i] > (int)' ') break;
      user[i] = 0;
   }
   inlen = strlen(passw);
   for(i=inlen-1;i;i--)
   {  if((int)passw[i] > (int)' ') break;
      passw[i] = 0;
   }
   for(i=0;i<UsersInf.n;i++)
   {  if(!strcmp(UsersInf.pUserData[i].user,user) )
      {  isuser = i;
         break;
      }
   }
   for(i=0;i<UsersInf.n;i++)
   {   if(i == isuser) continue;
       fprintf(fp,"%s:%s\n",UsersInf.pUserData[i].user,UsersInf.pUserData[i].passwd);
   }
   inlen = strlen(passw);
printf("%s:%s (%i)\n",user, passw,inlen);

   evgEncript(passw, pswdbuff, inlen, outlen,crtime);
   MoveBinToReadable(pswdbuff,pswdRbuff, outlen, inlen);

   fprintf(fp,"%s:%s\n",user, pswdRbuff);
   fclose(fp);
   return 0;
}

int qUserInfo(char *fname,char *user, char *keyword)
{ int i, is=0,inlen,outlen;
  char pswdbuff[8192];
  char inbuf[8096];
  for(i=0;i<UsersInf.n;i++)
  { if(!strcmp(UsersInf.pUserData[i].user,user) )
    { is = 1;
      inlen = strlen(UsersInf.pUserData[i].passwd);
      MoveReadableToBin(UsersInf.pUserData[i].passwd,inbuf, inlen, outlen);
      inlen = outlen;
      evgDecript(inbuf, pswdbuff, inlen, outlen, keyword);
      pswdbuff[outlen] = 0;
      printf("User %s : %s (%i)\n",UsersInf.pUserData[i].user, pswdbuff,outlen);

      break;
    }
  }
  if(!is)
     printf("user %s not found\n",user);
  return 0;
}

int FileCript(char *fname, char *keyword)
{  FILE *fp;
   int i,isuser;
   char *outbuff;
   char *inbuf;
   int inlen, outlen,crtime;
   time_t ltime;
   time(&ltime);
   crtime = (int) ltime;

   MasterKeyWord = keyword;
   fp = fopen(fname,"rb");
   inlen = _filelength(fileno(fp));
   inbuf = (char *) calloc(inlen+1,1);
   outlen = inlen+1+sizeof(int)*3;
   outbuff = (char *) calloc(outlen,1);
   fread(inbuf,inlen,1,fp);
   fclose(fp);
   evgEncript(inbuf, outbuff, inlen, outlen,crtime);
   fp = fopen(fname,"wb");
   fwrite(outbuff,outlen,1,fp);
   fclose(fp);
   printf("FileCript: %s\n", fname);
   return 0;
}

int FileDeCript(char *fname, char *keyword)
{  FILE *fp;
   int i,isuser;
   char *outbuff;
   char *inbuf;
   int inlen, outlen,crtime;
   time_t ltime;
   time(&ltime);
   crtime = (int) ltime;

   MasterKeyWord = keyword;
   fp = fopen(fname,"rb");
   inlen = _filelength(fileno(fp));
   inbuf = (char *) calloc(inlen+1,1);
   outlen = inlen+1+sizeof(int)*3;
   outbuff = (char *) calloc(outlen,1);
   fread(inbuf,inlen,1,fp);
   fclose(fp);
   evgDecript(inbuf, outbuff, inlen, outlen,keyword);
   if(outlen > 0 && abs(outlen-inlen) < 16)
   { printf("FileDeCript: %s\n", fname);
       fp = fopen(fname,"wb");
       fwrite(outbuff,outlen,1,fp);
       fclose(fp);
   }
   return 0;
}


int
main(int argc, char **argv)
{
    struct stat sb;
    time_t change_time = 0;
    char buf[256];
    char *user, *passwd, *p;
    user_data *u;
    int i,rc;

/*
  { char buf0[256],buf1[256];
    int inlen,outlen;
    strcpy(buf0,"01234567890ABCDEFGHIJKLMNO");
    inlen = strlen(buf0);
    MoveReadableToBin(buf0,buf1, inlen, outlen);
    MoveBinToReadable(buf1,buf0, outlen,inlen);
    printf("\n%s\n",buf0);
    return 0;
  }
*/


    setbuf(stdout, NULL);
    if (argc < 2 || (argc == 2 && !strcmp(argv[1],"-?") ) ) {
       fprintf(stderr, "Usage: ncsa_auth <passwordfile> [par]\n");
       fprintf(stderr, "add user [par]: -a username password\n");
       fprintf(stderr, "q   user [par]: -q username keyword\n");
       DosBeep(1000,50);
       exit(1);
    }

    read_passwd_file(argv[1]);
    if(argc > 2 && !stricmp(argv[2],"-a") )
    {  if(argc == 5)
       addUser(argv[1],argv[3],argv[4]);
        return 0;
    }

    if (stat(argv[1], &sb) != 0) {
       fprintf(stderr, "cannot stat %s\n", argv[1]);
       DosBeep(1000,500);
       DosBeep(2000,500);
       DosBeep(1000,500);
       exit(1);
    }

    if(argc > 2 && !stricmp(argv[2],"-q") )
    {  if(argc == 5)
       qUserInfo(argv[1],argv[3],argv[4]);
        return 0;
    }
    if(argc > 2 && !stricmp(argv[2],"-f") )
    {  if(argc == 4)
          FileCript(argv[1],argv[3]);
        return 0;
    }

    if(argc > 2 && !stricmp(argv[2],"-d") )
    {  if(argc == 4)
          FileDeCript(argv[1],argv[3]);
        return 0;
    }


    while (fgets(buf, 256, stdin) != NULL) {
       if ((p = strchr(buf, '\n')) != NULL)
           *p = '\0';          /* strip \n */
       if (stat(argv[1], &sb) == 0) {
           if (sb.st_mtime != change_time) {
               read_passwd_file(argv[1]);
               change_time = sb.st_mtime;
           }
       }

       if ((user = strtok(buf, " ")) == NULL) {
               printf("ERR\n");
               continue;
       }
       if ((passwd = strtok(NULL, "")) == NULL) {
               printf("ERR\n");
               continue;
       }
       rc = 1;
       for(i=0;i<UsersInf.n;i++)
       { if(!strcmp(UsersInf.pUserData[i].user,user) )
         {  if (!iscrypt(passwd,UsersInf.pUserData[i].passwd))
            {  rc = 0;
            }
            break;
         }
       }
       if(rc)
           printf("ERR\n");
       else
           printf("OK\n");
    }
    exit(0);
}

/******************************/
int my_rand(int i);

int iscrypt(char *passwd, char *cryptPasswdTxt)
{  char pswdbuff[8192];
   char cryptPasswd[8192];
   int inlen,outlen,l;
   int inlenCr,outlenCr,rn,r,t0,t;

   inlen = strlen(passwd);
   inlenCr = strlen(cryptPasswdTxt);
   MoveReadableToBin(cryptPasswdTxt,cryptPasswd, inlenCr, outlenCr);
/* Decode time*/
   rn = my_rand(-1);
   r = rn%32;
   memcpy(&t0,&cryptPasswd[0],sizeof(int));
   t = _rotr(t0,r);

   evgEncript(passwd, pswdbuff, inlen, outlen,t);
   if(outlenCr != outlen)
      return 1;
   return memcmp(pswdbuff,cryptPasswd,outlen);
}

/* */
int MoveBinToReadable(char *buf,char *outbuf, int inlen, int &outlen)
{ int i;

   for(i=0;i<inlen;i+=2)
   { outbuf[i/2*3  ] = (48+(buf[i] & 0x1f)) & 0xff;   /* 5bit */
                                          /* 1110 0000  0xe0 */
     outbuf[i/2*3+1] = (48+((buf[i] >>5) | ((buf[i+1] & 0xe0)>>2) )) &0xff; /* 6bit */
     outbuf[i/2*3+2] = (48+(buf[i+1] & 0x1f) ) &0xff; /* 5bit */
   }
   outlen = inlen/2*3;
   outbuf[outlen] = 0;

   for(i=0;i<outlen;i++)
           if((int)outbuf[i] > (int)'9')
                               outbuf[i] += 7;
//printf("BinToReadable %i %i\n",inlen,outlen);
   return 0;
}
/* */
int MoveReadableToBin(char *bufTxt,char *outbuf, int inlen, int &outlen)
{ int i;
   char buf[8096];

   outlen = inlen/3*2;
   for(i=0;i<inlen;i++)
   {     buf[i] = bufTxt[i];
         if((int)buf[i] > (int)'9')
                           buf[i] -= 7;
   }
   for(i=0;i<outlen;i+=2)
   {
     outbuf[i]   =  ((buf[i/2*3  ] - 48)&0xff)| (((buf[i/2*3+1]-48)&0x7)<<5);
                                                     /*  111000    */
     outbuf[i+1] =  ((buf[i/2*3+2] - 48)&0xff)| (((buf[i/2*3+1]-48)&0x38)<<2);
   }
//printf("\nReadableToBin %i %i\n",inlen,outlen);

   return 0;
}

/* зашифровать inlen байт из inbuf в outlen байт outbuf */
int evgEncript(char *inbuf, char *outbuf, int inlen, int &outlen, int t)
{  int i,l, t0, outn=0,rn,r,lkey;
   unsigned int d;
   char buf[8096];

//printf("e0 inlen=%x \n",inlen);
   l = inlen/4*4;
   if(l != inlen)
   {  for(i=inlen;i<l+4;i++) inbuf[i] = 0;
      l+=4;
   }
   memcpy(&outbuf[outn],&t,sizeof(int));
   outn+= sizeof(int);
   memcpy(&outbuf[outn],&inlen,sizeof(int));
   outn+= sizeof(int);
   lkey=strlen(MasterKeyWord);
   for(i=0;i<l;i++)
   { outbuf[outn+i] = inbuf[i] ^ MasterKeyWord[i%lkey];
   }
//   memcpy(&outbuf[outn],inbuf,l);
   outn+= l;
   outlen = outn;
//printf("e t=%x \n",t);
   rn = my_rand(-1);
   r = rn%32;
   t0 = _rotl(t,r);
   memcpy(&outbuf[0],&t0,sizeof(int));
   rn = my_rand(rn);
   r = (rn+t)%32;
//printf("e inlen=%x \n",inlen);
   t0 = _rotl(inlen,r)+t;
   outn= sizeof(int);
   memcpy(&outbuf[outn],&t0,sizeof(int));
   outn+= sizeof(int);
   for(i=0;i<l/4;i++)
   { d = *((unsigned int *)&outbuf[outn+i*4]);
     rn = my_rand(rn);
     r = (rn+t)%32;
     t0 = _rotl(d,r)+t;
     memcpy(&outbuf[outn+i*4],&t0,sizeof(int));
//printf("%8x ",d);
   }

   return 0;
}


/* расшифровать inlen байт из inbuf в outlen байт outbuf */
int evgDecript(char *inbuf, char *outbuf, int inlen, int &outlen, char *keyword)
{  int i,l,t, t0, outn=0,rn,r,lkey;
   unsigned int d;

   rn = my_rand(-1);
   r = rn%32;
   memcpy(&t0,&inbuf[0],sizeof(int));
   t = _rotr(t0,r);
//printf("\nd t=%x \n",t);
   outn= sizeof(int);
   memcpy(&t0,&inbuf[outn],sizeof(int));
   rn = my_rand(rn);
   r = (rn+t)%32;
   t0 = _rotr(t0 -t,r);
//printf("d inlen=%x \n",t0);
   outn+= sizeof(int);
   outlen = t0;
   l = outlen/4*4;
   if(l != outlen)
   {  l+=4;
   }

   for(i=0;i<l/4;i++)
   {
     d = *((unsigned int *)&inbuf[outn+i*4]);
     rn = my_rand(rn);
     r = (rn+t)%32;
     t0 = _rotr(d-t,r);
     memcpy(&outbuf[i*4],&t0,sizeof(int));

//     printf("%8x ",t0);
   }

   lkey=strlen(keyword);
   for(i=0;i<l;i++)
   { outbuf[i] = outbuf[i] ^ keyword[i%lkey];
   }

   return 0;
}


/* компиляторо-независимый генератор случайных чисел от 0 до 255 */
/* на входе: i =-1 - переустановка, >= 0 - ~порядковый номер */
int my_rand(int i)
{   static unsigned long int old=0x12398765;
    int iret;
    if(i < 0) old=0x12398765;
    old = (i + old + 0x65439867l);
    iret =(int) (old%255);
    return iret;
}


