/******************************************************************************
 *                                                                            *
 *      Module: PASSWD.C                                                      *
 *          PWDencrypt() takes a textual password and encrypts it using VMS's *
 *              own encryption algorithm.                                     *
 *              Parameters:                                                   *
 *                  1) char *uname  Username of the user                      *
 *                  2) char *pwd    Password to be encrypted                  *
 *                  3) short salt   Password encryption salt                  *
 *                  4) long epwd[2] Quadword holds the encrypted password     *
 *                  5) char algo    Encryption algorithm                      *
 *              Return values:                                                *
 *                  VMS status                                                *
 *          PWDget() retrieves the system authorization file, SYSUAF, for the *
 *              specified user's password, password salt and encryption       *
 *              algorithm. Requires read access to SYSUAF.                    *
 *              Parameters:                                                   *
 *                  1) char *uname  Username of the user                      *
 *                  2) short *salt  Word holds the password salt              *
 *                  3) long epwd[2] Quadword holds the encrypted password     *
 *                  4) char *algo   Encryption algorithm                      *
 *              Return values:                                                *
 *                  VMS status                                                *
 *          PWDcheck() takes a username and a password and compare the given  *
 *              password with the one found in the given user's UAF record.   *
 *              Both the username and the password MUST be in uppercase.      *
 *              Parameters:                                                   *
 *                  1) char *uname  Username of the user                      *
 *                  2) char *pwd    Password to be checked                    *
 *              Return values:                                                *
 *                  0              Invalid password                           *
 *                  1              Valid password                             *
 *                  VMS status     Otherwise                                  *
 *                                                                            *
 *      Author:                                                               *
 *          Terence Lee (DEC/HKO), 13-Dec-1989                                *
 *                                                                            *
 *      Modification history:                                                 *
 *                                                                            *
 ******************************************************************************/

#include <ssdef.h>
#include <stdio.h>
#include <uaidef.h>
#include "descr.h"
#include "itmlst.h"

#define UAF$C_USER_ID 1
#define UAF$C_VERSION1 1
#define UAF$C_KEYED_PART 52
#define UAF$C_AD_II 0
#define UAF$C_PURDY 1
#define UAF$C_PURDY_V 2
#define UAF$K_FIXED 644
#define UAF$C_FIXED 644
#define UAF$K_LENGTH 1412
#define UAF$C_LENGTH 1412
#define UAF$S_UAFDEF 1412
#define UAF$B_RTYPE 0
#define UAF$B_VERSION 1
#define UAF$W_USRDATOFF 2
#define UAF$S_USERNAME 32
#define UAF$T_USERNAME 4
#define UAF$T_USERNAME_TAG 35
#define UAF$L_UIC 36
#define UAF$W_MEM 36
#define UAF$W_GRP 38
#define UAF$L_SUB_ID 40
#define UAF$S_PARENT_ID 8
#define UAF$Q_PARENT_ID 44
#define UAF$S_ACCOUNT 32
#define UAF$T_ACCOUNT 52
#define UAF$S_OWNER 32
#define UAF$T_OWNER 84
#define UAF$S_DEFDEV 32
#define UAF$T_DEFDEV 116
#define UAF$S_DEFDIR 64
#define UAF$T_DEFDIR 148
#define UAF$S_LGICMD 64
#define UAF$T_LGICMD 212
#define UAF$S_DEFCLI 32
#define UAF$T_DEFCLI 276
#define UAF$S_CLITABLES 32
#define UAF$T_CLITABLES 308
#define UAF$S_PWD 8
#define UAF$Q_PWD 340
#define UAF$L_PWD 340
#define UAF$S_PWD2 8
#define UAF$Q_PWD2 348
#define UAF$W_LOGFAILS 356
#define UAF$W_SALT 358
#define UAF$B_ENCRYPT 360
#define UAF$B_ENCRYPT2 361
#define UAF$B_PWD_LENGTH 362
#define UAF$S_EXPIRATION 8
#define UAF$Q_EXPIRATION 364
#define UAF$S_PWD_LIFETIME 8
#define UAF$Q_PWD_LIFETIME 372
#define UAF$S_PWD_DATE 8
#define UAF$Q_PWD_DATE 380
#define UAF$S_PWD2_DATE 8
#define UAF$Q_PWD2_DATE 388
#define UAF$S_LASTLOGIN_I 8
#define UAF$Q_LASTLOGIN_I 396
#define UAF$S_LASTLOGIN_N 8
#define UAF$Q_LASTLOGIN_N 404
#define UAF$S_PRIV 8
#define UAF$Q_PRIV 412
#define UAF$S_DEF_PRIV 8
#define UAF$Q_DEF_PRIV 420
#define UAF$S_MIN_CLASS 20
#define UAF$R_MIN_CLASS 428
#define UAF$S_MAX_CLASS 20
#define UAF$R_MAX_CLASS 448
#define UAF$L_FLAGS 468
#define UAF$V_DISCTLY 0
#define UAF$V_DEFCLI 1
#define UAF$V_LOCKPWD 2
#define UAF$V_CAPTIVE 3
#define UAF$V_DISACNT 4
#define UAF$V_DISWELCOM 5
#define UAF$V_DISMAIL 6
#define UAF$V_NOMAIL 7
#define UAF$V_GENPWD 8
#define UAF$V_PWD_EXPIRED 9
#define UAF$V_PWD2_EXPIRED 10
#define UAF$V_AUDIT 11
#define UAF$V_DISREPORT 12
#define UAF$V_DISRECONNECT 13
#define UAF$V_AUTOLOGIN 14
#define UAF$V_DISFORCE_PWD_CHANGE 15
#define UAF$S_NETWORK_ACCESS_P 3
#define UAF$B_NETWORK_ACCESS_P 472
#define UAF$S_NETWORK_ACCESS_S 3
#define UAF$B_NETWORK_ACCESS_S 475
#define UAF$S_BATCH_ACCESS_P 3
#define UAF$B_BATCH_ACCESS_P 478
#define UAF$S_BATCH_ACCESS_S 3
#define UAF$B_BATCH_ACCESS_S 481
#define UAF$S_LOCAL_ACCESS_P 3
#define UAF$B_LOCAL_ACCESS_P 484
#define UAF$S_LOCAL_ACCESS_S 3
#define UAF$B_LOCAL_ACCESS_S 487
#define UAF$S_DIALUP_ACCESS_P 3
#define UAF$B_DIALUP_ACCESS_P 490
#define UAF$S_DIALUP_ACCESS_S 3
#define UAF$B_DIALUP_ACCESS_S 493
#define UAF$S_REMOTE_ACCESS_P 3
#define UAF$B_REMOTE_ACCESS_P 496
#define UAF$S_REMOTE_ACCESS_S 3
#define UAF$B_REMOTE_ACCESS_S 499
#define UAF$B_PRIMEDAYS 514
#define UAF$V_MONDAY 0
#define UAF$V_TUESDAY 1
#define UAF$V_WEDNESDAY 2
#define UAF$V_THURSDAY 3
#define UAF$V_FRIDAY 4
#define UAF$V_SATURDAY 5
#define UAF$V_SUNDAY 6
#define UAF$B_PRI 516
#define UAF$B_QUEPRI 517
#define UAF$W_MAXJOBS 518
#define UAF$W_MAXACCTJOBS 520
#define UAF$W_MAXDETACH 522
#define UAF$W_PRCCNT 524
#define UAF$W_BIOLM 526
#define UAF$W_DIOLM 528
#define UAF$W_TQCNT 530
#define UAF$W_ASTLM 532
#define UAF$W_ENQLM 534
#define UAF$W_FILLM 536
#define UAF$W_SHRFILLM 538
#define UAF$L_WSQUOTA 540
#define UAF$L_DFWSCNT 544
#define UAF$L_WSEXTENT 548
#define UAF$L_PGFLQUOTA 552
#define UAF$L_CPUTIM 556
#define UAF$L_BYTLM 560
#define UAF$L_PBYTLM 564
#define UAF$L_JTQUOTA 568
#define UAF$W_PROXY_LIM 572
#define UAF$W_PROXIES 574
#define UAF$W_ACCOUNT_LIM 576
#define UAF$W_ACCOUNTS 578

#define SYSfault(sts)   (sts != SS$_NORMAL)

typedef unsigned long ULong;

static long autodin[16] = {     /* autodin-II polynomial table for CRC. */
    000000000000,003555610144,007333420310,004666230254,
    016667040620,015332650764,011554460530,012001270474,
    035556101440,036003711504,032665521750,031330331614,
    023331141260,020664751324,024002561170,027557371034
};
static long c[5][2] = {         /* purdy polynomial coefficient. */
    { -83,-1  },                /* c1. */
    { -179,-1 },                /* c2. */
    { -257,-1 },                /* c3. */
    { -323,-1 },                /* c4. */
    { -363,-1 }                 /* c5. */
};
static ULong sts;

/*
 *      lgi$hpwd() is the C version of the VAX/VMS password encryption
 *          algorithim from facility login module hpwd.
 *          Parameters:
 *              1) char *uname  Username of the user
 *              2) char *pwd    Password to be encrypted
 *              3) short salt   Password encryption salt
 *              4) long epwd[2] Quadword holds the encrypted password
 *              5) char algo    Encryption algorithm
 *          Return values:
 *              SS$_NORMAL     Always success
 */

static int lgi$hpwd(uname,pwd,salt,epwd,algo)
char    *uname,*pwd;
short   salt;
long    epwd[];
char    algo;
{
        short   *ptr1 = (short *)((char *)epwd + 3);
        char    Una[13] = "            ";

        switch (algo) {
            case UAF$C_AD_II:
                epwd[0] = lib$crc(autodin,&-1,descrS(pwd,0));
                epwd[1] = 0;
                break;
            case UAF$C_PURDY:
            case UAF$C_PURDY_V:
                if (algo == UAF$C_PURDY_V) strcpy(Una,uname);
                else strncpy(Una,uname,strlen(uname));
                epwd[0] = epwd[1] = 0;
                collapse(pwd,epwd);
                *ptr1 += salt;
                collapse(Una,epwd);
                purdy(epwd);
                break;
        }
        return(SS$_NORMAL);
}

/*
 *      collapse() takes a string of bytes and collapse them into a quadword.
 *          It does this by cycling around the bytes of the output buffer
 *          adding in the bytes of the input string.
 *          Parameters:
 *              1) char *s      Input string
 *              2) long epwd[2] Quadword holds the encrypted password
 *          Return values:
 *              None
 */

static int collapse(s,epwd)
char    *s;
long    epwd[];
{
        register i;
        long    mask = 0x0007;  /* Select start byte from the quadword. */
        char    *ptr1 = s,*ptr2 = (char *)epwd;

        for (i = strlen(s); i; i--) *(ptr2 + (i & mask)) += *ptr1++;
}

/*
 *      purdy() evaluates purdy polynomial. It computes f(u) = p(u) where
 *          p is a prime of the form p^64-a. The function p is the
 *          following polynomial:
 *          x^n0 + x^n1 * c1 + x^3 * c2 + x^2 * c3 + x * c4 + c5
 *          Parameters:
 *              1) long epwd[2] Quadword holds the encrypted password
 *          Return values:
 *              None
 */

#define quadasgn(d,l,h) (d[0] = l,d[1] = h)
#define quadcopy(s,d)   (d[0] = s[0],d[1] = s[1])

static long a = 59,n0 = (1 << 24) - 3,n1 = (1 << 24) - 63;

static int purdy(epwd)
long    epwd[];
{
        register i = 0;
        long    power,rs1[2],rs2[2],rs3[2];

        pqmod(epwd);

        /*
         *  Get x^n1.
         */

        quadcopy(epwd,rs2);
        power = n1;
        pqexp(rs2,power);

        /*
         *  Get x^(n0 - n1) + c1. Obtain x^n0 + x^n1 * c1 by multiplying the
         *  first two number.
         */

        quadcopy(epwd,rs1);
        power = n0 - n1;
        pqexp(rs1,power);
        pqadd(c[i],rs1);
        pqmul(rs2,rs1,rs1);
        i++;

        /*
         *  Get x * c2 + c3. Obtaining x^2 * c2 + x * c3 by multiplying x to
         *  the result.
         */

        quadcopy(epwd,rs2);
        pqmul(c[i],rs2,rs2);
        i++;
        pqadd(c[i],rs2);
        i++;
        quadcopy(epwd,rs3);
        pqmul(rs3,rs2,rs2);

        /*
         *  Get x^2 * c2 + x * c3 + c4. Obtaining x^3 * c2 + x^2 * c3 + x * c4
         *  by multiply x to the last result.
         */

        pqadd(c[i],rs2);
        i++;
        quadcopy(epwd,rs3);
        pqmul(rs3,rs2,rs2);

        /*
         *  Add c5 and the first number to produce the result.
         */

        pqadd(c[i],rs2);
        pqadd(rs2,rs1);
        quadcopy(rs1,epwd);
}

/*
 *      pqexp() replaces the quadword num with num^p where p is of the
 *          form p = 2^64 - a.
 *          Parameters:
 *              1) long num[2] Quadword to be raised to the given power
 *              2) long power  Raised to this power
 *          Return values:
 *              None
 */

static int pqexp(num,power)
long    num[],power;
{
        long    multi[2],save_num[2];


        if (power <= 0) return;
        quadasgn(multi,1,0);
        quadcopy(num,save_num);
        for ( ; ; ) {
            if (power & 0x00000001) {
                pqmul(num,multi,num);
                quadcopy(num,multi);
                if ((power & 0xfffffffe) == 0) break;
            }
            pqmul(save_num,save_num,num);
            quadcopy(num,save_num);
            power >>= 1;
        }
}

/*
 *      pqmod() replaces the quadword num with num mod p where p is of the
 *          form p = 2^64 - a.
 *          Parameters:
 *              1) long num[2] Quadword to be mod by p
 *          Return values:
 *              None
 */

static int pqmod(num)
long    num[];
{
        if ((ULong)num[0] < (ULong)-a) return;
        if ((ULong)num[1] < (ULong)-1) return;
        num[0] += a;
        num[1] += 1;
}

/*
 *      pqmul() computes the product mulr * muld mod p where p is of the
 *          form p = 2^64 - a.
 *          The product may be form as the sum of four longword multiplications
 *          which are scaled by powers of  2^32 by evaluating:
 *          2^64 * v * z + 2^32 * (v * y + u * z) + u * y
 *          where u = muld[0]
 *                v = muld[1]
 *                y = mulr[0]
 *                z = mulr[1]
 *          Parameters:
 *              1) long mulr[2] Quadword multiplier
 *              2) long muld[2] Quadword multiplicand
 *              3) long prod[2] Quadword product
 *          Return values:
 *              None
 */

static int pqmul(mulr,muld,prod)
long    mulr[],muld[],prod[];
{
        long    tmp[2],rs1[2],rs2[2];

        /*
         *  Get 2^32 * v * z.
         */

        emulq(mulr[1],muld[1],tmp);
        pqmod(tmp);
        pqlsh(tmp);
        quadcopy(tmp,rs2);

        /*
         *  Get v * y and add in the above sum.
         */

        emulq(mulr[0],muld[1],tmp);
        pqmod(tmp);
        quadcopy(tmp,rs1);

        /*
         *  Get u * z and add in the above sum. Obtain the first two
         *  items by pqlsh() the sum.
         */

        emulq(mulr[1],muld[0],tmp);
        pqmod(tmp);
        pqadd(tmp,rs1);
        pqadd(rs2,rs1);
        pqlsh(rs1);

        /*
         *  Get u * y and add in the above sum.
         */

        emulq(mulr[0],muld[0],tmp);
        pqmod(tmp);
        pqadd(tmp,rs1);
        quadcopy(rs1,prod);
}

/*
 *      emulq() knows how to multiply two unsigned longwords, producing an
 *          unsigned quadword product.
 *          Parameters:
 *              1) long mulr    Longword multiplier
 *              2) long muld    Longword multiplicand
 *              3) long prod[2] Quadword product
 *          Return values:
 *              None
 */

static int emulq(mulr,muld,prod)
long    mulr,muld,prod[];
{
        long    compensate = 0;

        lib$emul(&mulr,&muld,&0,prod);
        if (mulr < 0) compensate += muld;
        if (muld < 0) compensate += mulr;
        prod[1] += compensate;
}

/*
 *      pqlsh() computes the product 2^32 * u mod p where p is of the
 *          form p = 2^64 - a.
 *          Parameters:
 *              1) long num[2] Quadword to be mod by p
 *          Return values:
 *              None
 */

#define ASH             32

static int pqlsh(num)
long    num[];
{
        long    tmp[2],mask = 0x80000000;

        emulq(num[1],a,tmp);
        num[1] <<= ASH;
        num[1] |= (ULong)((mask >>= ASH - 1) & num[0]) >> (32 - ASH);
        num[0] <<= ASH;
        pqadd(tmp,num);
}

/*
 *      pqadd() computes the sum add + sum mod p where p is of the
 *          form p = 2^64 - a.
 *          Parameters:
 *              1) long add[2] Quadword addend
 *              2) long sum[2] Quadword sum
 *          Return values:
 *              None
 */

static int pqadd(add,sum)
long    add[],sum[];
{
        register c0,c1;

        if ((ULong)sum[0] > (ULong)-1 - (ULong)add[0])
            c0 = 1;
        else
            c0 = 0;
        sum[0] += add[0];
        if ((ULong)(sum[1] + c0) > (ULong)-1 - (ULong)add[1])
            c1 = 1;
        else
            c1 = 0;
        sum[1] += add[1] + c0;
        if (!c1 && (ULong)sum[1] < (ULong)-1) return;
        if ((ULong)sum[0] > (ULong)-1 - (ULong)a)
            c0 = 1;
        else
            c0 = 0;
        sum[0] += a;
        sum[1] += c0;
}

/*
 *      PWDencrypt() takes a textual password and encrypts it using VMS's
 *          own encryption algorithm.
 *          Parameters:
 *              1) char *uname  Username of the user
 *              2) char *pwd    Password to be encrypted
 *              3) short salt   Password encryption salt
 *              4) long epwd[2] Quadword holds the encrypted password
 *              5) char algo    Encryption algorithm
 *          Return values:
 *              VMS status
 */

int PWDencrypt(uname,pwd,salt,epwd,algo)
char    *uname,*pwd;
short   salt;
long    epwd[];
char    algo;
{
        if (strlen(uname) > 12 || strlen(pwd) > 31) return(SS$_BADPARAM);
        sts = lgi$hpwd(uname,pwd,salt,epwd,algo);
        return(sts);
}

/*
 *      PWDget() retrieves the system authorization file, SYSUAF, for the
 *          specified user's password, password salt and encryption
 *          algorithm. Requires read access to SYSUAF.
 *          Parameters:
 *              1) char *uname  Username of the user
 *              2) short *salt  Word holds the password salt
 *              3) long epwd[2] Quadword holds the encrypted password
 *              4) char *algo   Encryption algorithm
 *          Return values:
 *              VMS status
 */

int PWDget(uname,salt,epwd,algo)
char    *uname;
short   *salt;
long    epwd[];
char    *algo;
{
        itmlst_3 UAIitm[] = {
            2,UAI$_SALT,salt,0,8,UAI$_PWD,epwd,0,
            1,UAI$_ENCRYPT,algo,0,0,0,0,0
        };

        sts = sys$getuai(0,0,descrS(uname,0),UAIitm,0,0,0);
        return(sts);
}

/*
 *      PWDcheck() takes a username and a password and compare the given
 *          password with the one found in the given user's UAF record.
 *          Both the username and the password MUST be in uppercase.
 *          Parameters:
 *              1) char *uname  Username of the user
 *              2) char *pwd    Password to be checked
 *          Return values:
 *              0              Invalid password
 *              1              Valid password
 *              VMS status     Otherwise
 */

int PWDcheck(uname,pwd)
char    *uname,*pwd;
{
        short   salt;
        long    epwd_1[2],epwd_2[2];
        char    algo;

        sts = PWDget(uname,&salt,epwd_1,&algo);
        if (SYSfault(sts)) return(sts);
        sts = PWDencrypt(uname,pwd,salt,epwd_2,algo);
        if (SYSfault(sts)) return(sts);
        return(epwd_1[0] == epwd_2[0] && epwd_1[1] == epwd_2[1]);
}
