/* poppassd.c v2.1b */
/* Written by Joel Snyder under contract to Schlumberger
   Donated to the Public Domain by Schlumberger (David Sims 'dpsims@slb.com')
   Please give credit in any modifications or re-distributions
   *Not* for intended re-sale */

/* poppassd - uses POP protocols to change passwords */ 
#define VERSION "2.1beta"

/*
 * poppassd.c
 *
 * A Eudora and NUPOP change password server for OpenVMS
 *
 * Joel Snyder
 * Opus One
 * 1404 East Lind Road, Tucson, AZ, 85719
 * +1 602 324 0494 (voice); +1 602 324 0495 (FAX)
 * jms@opus1.com
 * 2/10/95
 *
 * Based on earlier versions by Roy Smith <roy@nyu.edu>, Daniel
 * L. Leavitt <dll.mitre.org>, and John Norstad.
 * 
 * Note that unencrypted passwords are transmitted over the network.  If
 * this bothers you, think hard about whether you want to implement the
 * password changing feature.  On the other hand, it's no worse than what
 * happens when you run /bin/passwd while connected via telnet or rlogin.
 * Well, maybe it is, since the use of a dedicated port makes it slightly
 * easier for a network snooper to snarf passwords off the wire.
 *
 * NOTE: In addition to the security issue outlined in the above paragraph,
 * you should be aware that this program is going to be run with privileges
 * by ordinary users and it mucks around with the password file.  This should
 * set alarms off in your head.  
 *
 * Needs to be installed privileged. 
 *
 * Instructions for installation:
 *
 * 0- READ THROUGH THE SOURCE TO MAKE SURE THAT THIS DOES WHAT YOU 
 *    WANT IT TO DO ! ! !
 * 1- compile and link it properly.  To do that:
 *	$ cc poppassd		(if you have DEC C, cc/vaxc)
 *	$ link poppassd,sys$input:/opt/notrace
 *      MULTINET:MULTINET_SOCKET_LIBRARY/SHARE  
 *      SYS$SHARE:VAXCRTL/SHARE
 *      ^Z
 * 2- put it somewhere.  I suggest you put this in your
 *    MULTINET_COMMON_ROOT:[MULTINET] directories, unless you have some
 *    place else where you keep images like this.  To do that:
 *      $ copy poppassd.exe multinet_common_root:[multinet]
 * 3- make Multinet know about it.  To do that (assuming you stuck the
 *    file in multinet:poppassd.exe:
 * $ multinet config/server
 * SERVER-CONFIG>add poppassd
 * [Adding new configuration entry for service "poppassd"]
 * Protocol: [TCP]
 * TCP Port number: 106
 * Program to run: MULTINET:poppassd.EXE
 * [Added service poppassd to configuration]
 * [Selected service is now poppassd]
 * SERVER-CONFIG>show/full
 * Service "poppassd":
 *         TCP socket (AF_INET,SOCK_STREAM), Port 106
 *         Socket Options = SO_KEEPALIVE
 *         INIT() = TCP_Init
 *         LISTEN() = TCP_Listen
 *         CONNECTED() = TCP_Connected
 *         SERVICE() = Run_Program
 *         Program = "MULTINET:poppassd.EXE"
 * SERVER-CONFIG>restart
 * 
 */

/* Steve Dorner's description of the simple protocol:
 *
 * The server's responses should be like an FTP server's responses; 
 * 1xx for in progress, 2xx for success, 3xx for more information
 * needed, 4xx for temporary failure, and 5xx for permanent failure.  
 * Putting it all together, here's a sample conversation:
 *
 *   S: 200 hello\r\n
 *   E: user yourloginname\r\n
 *   S: 300 please send your password now\r\n
 *   E: pass yourcurrentpassword\r\n
 *   S: 200 My, that was tasty\r\n
 *   E: newpass yournewpassword\r\n
 *   S: 200 Happy to oblige\r\n
 *   E: quit\r\n
 *   S: 200 Bye-bye\r\n
 *   S: <closes connection>
 *   E: <closes connection>
 */

#include <stdio>
#include <rms>
#include <ssdef>
#include <dvidef>
#include <ctype.h>		/* just for isalpha() */
#include <descrip.h>		/* defines struct dsc$descriptor_s */
#include <uaidef.h>		/* defines UAI$_ENCRYPT */

/*
 * Not all versions of the VAX C compiler have all of the
 * values we need.  This is a bad idea, but the best I could come
 * up with.
 */
#ifndef UAI$_PASSWORD
#define UAI$_PASSWORD 73	/* must be less than 117 */
#endif

#ifdef UCX			/* for future UCX compatibility */
#include <socket.h>		/* ignore for now. */
#include <in.h>
#include <netdb.h>
#include <inet.h>
#endif

#define BUFSIZE 512
#define MAXPWDLEN 32		/* maximum length of a password */
#define MAXUSERNAMELEN 32	/* maximum length of a username */

/*
 * This program does a lot of things which a particular site may
 * want to disable or change.  These are all controlled by these
 * compile-time constants.  Normally, you should just leave these
 * be. 
 */

/* Uncomment the following line so we does NOT upper case username/password */
/* #define NOUPCASE 1 */
/* The previous line should BE COMMENTED OUT in standard VMS operations.    */

/* poppassd logs everything it does.  Change this to define where you */
/* want it to send such log messages. */
#define poppassd_LOG_FILE "poppassd.log"

/* poppassd normally runs on port 106.  */
#define poppassd_PORT 106

typedef struct itemlist {		/* VMS style itemlist */
    variant_union {
	variant_struct {
	    unsigned short buffer_length; 
	    unsigned short item_code; 
	} i__v_s;
	unsigned int end_of_itemlist;
    } i__v_u;
    char *buffer_address; 
    int  *return_length_address; 
}
*ITEMLISTPTR,ITEMLIST_STRUCT; 

#define TCPIP_DEVICE_TYPE "INET" /* may have to change for other ip sw */

#ifndef FALSE
#define FALSE 0				/* It is required that */
#define TRUE 1				/* True != False == 0 */
#endif

#ifndef NULL
#define NULL 0			/* Just in case this isn't defined */
#endif

#define bool int			/* for a future, superior language */

#define _toupper(c)     ((c) >= 'a' && (c) <= 'z' ? (c) & 0x5F:(c))

#ifndef UCX
unsigned short lchan;		/* the network channel */
#else
int listenchan, lchan;
#endif

static unsigned char debug;		/* debug flag */
static bool TCPIP;		/* set to TRUE if we're TCPIP */

/*
 * Prototypes 
 */
void WriteToClient( char * );
void ReadFromClient( char * );
void close_files ();
void log_info( char * );
bool chkPass (char *, char *);
bool chgPass (char *, char *, char *);
void do_ucx_open ();
/*
 * Globals for everyone to use
 */
char because[BUFSIZE];
char user[BUFSIZE];


int
main()
{
    char line[BUFSIZE];
    char oldpass[BUFSIZE];
    char newpass[BUFSIZE];
    char emess[BUFSIZE];		/* logging message */
    char word1[BUFSIZE];		/* for incoming commands */
    char word2[BUFSIZE];		/* for incoming command objects */
    unsigned int status;

    unsigned int have_user, have_oldpass, have_newpass, prompt_for_more;

    static struct {int Size; char *Ptr;} Descr={9 ,"SYS$INPUT"};
     
/*
 * Initialize variables, including debug mode.
 */

    *user = *oldpass = *newpass = 0;
    prompt_for_more = have_user = have_oldpass = have_newpass = FALSE;
    if (getenv("poppassd_DEBUG") != NULL) 
	debug = TRUE;
    else
	debug = FALSE;

/*
 * Open up the LOCAL poppassword channel.  That's the one coming in to
 * us.  Find out if we're a TCP/IP object or just running at the
 * command line.
 */
#ifndef UCX
    status = SYS$ASSIGN(&Descr, &lchan, 0, 0);
    if (!(status&1)) {
	log_info("Error in assign");
	return(status);		/* error logging ? */
    }
    (void) i_am_tcpobj(lchan);
#else
    (void) do_ucx_open();
#endif

/*
 * Begin the dialog and remember that we know nothing.
 */
    sprintf(emess,"200 poppassd v%s hello, what's your username?",VERSION);
    WriteToClient (emess);

/*
 * Loop until we have what we need
 */
    while ( !have_user || !have_oldpass || !have_newpass) {
	if (prompt_for_more) {
	    WriteToClient ("300 OK. Tell me more.");
	    prompt_for_more = FALSE;	/* put out 300 message next time */
	}
	word1[0] = word2[0] = 0;	/* null everything out */
	ReadFromClient (line);
	if (strlen(line) == 0) continue;	/* no data? try again. */
	sscanf (line, "%s %s", word1, word2);
/*
 * HANDLE USER 
 */
	if ( !strcmp(word1,"user") ) {
	    if (strlen(word2) > MAXUSERNAMELEN) {
		WriteToClient ("500 Your username is too long.");
		log_info("Too long username given after user command.");
		continue;
	    }
	    strncpy(user, word2, MAXUSERNAMELEN);
	    if (strlen (user) == 0) {
		WriteToClient ("500 Username required.");
		log_info("No username given after user command.");
	        continue;
	    }
	    have_user = TRUE;
	    prompt_for_more = TRUE;
	    continue;
	} 
/*
 * HANDLE OLD PASSWORD
 */
	if ( !strcmp(word1,"pass") ) {
	    if (strlen (word2) > MAXPWDLEN) {
		WriteToClient ("500 Your password isn't that long.");
		log_info("Tried to use a too-long password on oldpass.");
		continue;
	    }
	    strncpy(oldpass, word2, MAXPWDLEN);
	    if (strlen (oldpass) == 0) {
		WriteToClient ("500 Password required.");
		log_info("No password given after pass command.");
		continue;
	    }
	    have_oldpass = TRUE;
	    prompt_for_more = TRUE;
	    continue;
	} 
/*
 * HANDLE NEW PASSWORD
 */
	if ( !strcmp(word1, "newpass") ) {
	    if (strlen (word2) > MAXPWDLEN) {
		WriteToClient ("500 Pick a shorter password.");
		log_info("Tried to use a too-long password on newpass.");
		continue;
	    }
	    strncpy(newpass, word2, MAXPWDLEN);
	    if (strlen (newpass) == 0) {
		WriteToClient ("500 New password required.");
		log_info("No password given after newpass command.");
	        continue;
	    }
	    have_newpass = TRUE;
	    prompt_for_more = TRUE;
	    continue;
	}
/*
 * HANDLE OTHER COMMANDS
 */
	if ( !strcmp(word1, "help") ) {
	    WriteToClient ("214- Available commands:");
	    WriteToClient ("214- ");
	    WriteToClient ("214- USER, PASS, NEWPASS");
	    WriteToClient ("214- NOOP, QUIT, HELP, RSET");
	    WriteToClient ("214- ");
	    WriteToClient ("214- Bug reports to jms@opus1.com");
	    WriteToClient ("214 ");
	    continue;
	} 
	if ( !strcmp(word1, "noop") ) {
	    WriteToClient ("200 Ok.");
	    continue;
	} 
	if ( !strcmp(word1, "quit") ) {
	    WriteToClient ("221 Goodbye");
	    exit(1);
	} 
	if ( !strcmp(word1, "rset") ) {
	    have_user = have_newpass = have_oldpass = FALSE;
	    WriteToClient ("250 Ok.");
	    continue;
	} 

	if ( !strcmp(word1, "debug") ) {
	    WriteToClient ("250 Debug mode enabled.");
	    log_info("Some moron is trying to use debug commands...");
	    continue;
	}

	WriteToClient ("500 Unknown command specified. Try HELP.");
	continue;
    }

    if ( !chgPass (user, oldpass, newpass) )
    {
	sprintf(emess,"500 Password not changed because %s.",because);
	if ( (getenv("poppassd_ANAL_MODE")) != NULL) 
	    WriteToClient("500 Password not changed.");
	else
	    WriteToClient (emess);
	log_info(emess);
    } else {
	WriteToClient ("200 Password changed, thank you very much.");
	log_info ("Password changed, thank you very much.");	/* log it */
    }

    close_files();
    return;

}

bool
i_am_tcpobj(chan)
unsigned short chan;
/*
 * Return TRUE if this is a TCP object, 
 *        FALSE otherwise
 * This subroutine also sets the global variable TCPIP so that 
 * other things can use it. 
 */
{
    
    ITEMLIST_STRUCT itemlist[4];
    unsigned int devchar, devclass, devnam_length;
    unsigned int status;
    char devnam[64];
    void WriteToClient();

    TCPIP = FALSE;				/* not TCPIP by default */

    itemlist[0].item_code = DVI$_DEVCHAR;
    itemlist[0].buffer_length = 4;
    itemlist[0].buffer_address = &devchar;
    itemlist[0].return_length_address = 0;

    itemlist[1].item_code = DVI$_DEVNAM;	/* inet is tcpip */
    itemlist[1].buffer_length = 64;
    itemlist[1].buffer_address = &devnam;
    itemlist[1].return_length_address = &devnam_length;

    itemlist[2].item_code = DVI$_DEVCLASS;
    itemlist[2].buffer_length = 4;
    itemlist[2].buffer_address = &devclass;
    itemlist[2].return_length_address = 0;

    itemlist[3].end_of_itemlist = 0;

    status = sys$getdviw( 0 ,  /* efn */
			  chan ,
			  0 ,  /* devnam */
			  &itemlist ,
			  0 , /* iosb */
			  0 , /* astadr */
			  0 , /* astprm */
			  0 ); /* nullarg */

    if (status != SS$_NORMAL) {
	WriteToClient("500 Sorry, an internal error has occurred.");
	return(TCPIP);		/* I don't know.  Maybe false? */
    }

    devnam[(devnam_length>=64 ? 63 : devnam_length)] = '\000';
    if (index(devnam,TCPIP_DEVICE_TYPE) != -1) 
	TCPIP = TRUE;

    return(TCPIP);
}

void
close_files()
/*
 * Close all files.
 */
{
    if (TCPIP) {
	close(lchan);
    } 
}

void
log_info(arg)
char *arg;
/*
 * Log a message to the log file. 
 */
{
    long bintim;		/* the time of day it is */
    FILE *logfile;
    char buf[BUFSIZE];		/* an internal buffer used for scratch space */

    if (getenv("poppassd_DONT_LOG") != NULL) return;

    logfile = fopen(poppassd_LOG_FILE, "a");
    if (logfile <= NULL) return;

    time(&bintim);

    fprintf(logfile, "poppassd: user %s at %s", user, ctime(&bintim));
    fprintf(logfile, "\t%s\n", arg);

    fclose(logfile);

    return;
}    

int
index(string,substring)			
char* string;
char* substring;
/*
 * This subroutine searches for substring in string. If substring
 * is found, then the return value is the offset into string.
 * If not found, a -1 is returned. 
 * INDEX is case-insensitive and not particularily efficient.
 */
{
    int len_string = 0;			/* length of search string */
    int len_substring = 0;		/* length of substring */
    int offst = 0;			/* current point in string */
    int i = 0;				/* normal index tmp var */

    if ((len_string=strlen(string))<(len_substring=strlen(substring))) {
	return(-1);
    }

    for (offst=0;offst<=len_string-len_substring; offst++) {
	if (_toupper((string[offst]))!=_toupper((substring[0])))
	    continue;
	for (i=1;i<len_substring;i++) {
	    if (_toupper((string[offst+i]))!=_toupper((substring[i])))
		break;
	}
	if (i==len_substring) {
	    return(offst);
	}
    }

    return(-1);
}


void
send_file(arg)
char *arg;
/*
 * Write the given file name to the remote user 
 */
{
    FILE *hostfile;
    char buf[BUFSIZE];		/* an internal buffer used for scratch space */

    hostfile = fopen(arg, "r");
    if (hostfile <= NULL) return;	/* rms$_fnf */

    while (fgets(buf,BUFSIZE,hostfile) != NULL) {
	if (TCPIP) {
	    send(lchan, buf, strlen(buf), 0);
	} else {
	    puts(buf);
	}
    }

    fclose(hostfile);

    return;
}

void
WriteToClient(arg)
char *arg;
/*
 * Write the single line to the remote user 
 * 
 * Hey, isn't it cool that in the C RTL, fprintf has the file
 * first, and in fputs the file is last?  I sure know how much I
 * love that.  I call it "job security."
 */
{
char emess[BUFSIZE];

    if (TCPIP) {
	strcpy(emess,arg);
	strcat(emess,"\r\n");
	send(lchan, emess, strlen(emess), 0);
    } else {
	printf("%s\n",arg);
    }

    return;
}

void
ReadFromClient (arg)
char *arg;
/*
 * Read a line from the remote user.
 *
 * Some users may attempt a Morris-like attack on us, trying a very
 * long string in some attempt to over-write our buffers.  This wouldn't
 * work in VMS, but you never know what kind of idiots are out there.
 * In any case, we only read in as much as we have room to store,
 * so anything longer than that is a waste of time anyway.  
 */
{
int length;
char *sp;		/* for converting to lower case */

    if (TCPIP) 
        length = recv(lchan, arg, BUFSIZE-1, 0);
    else
	length = gets(arg);

    if (length == 0) {		/* end of file */
	exit(1);
    }

    if (length < 0) {		/* error condition */
	log_info("Error in ReadFromClient");
	/*socket_perror("poppassd: read");*/
    }

    for (sp = arg; isalpha(*sp); sp++) *sp = tolower(*sp);

    return;		
}

/* 
 * chgPass - check username and password against SYSUAF for validity,
 * 	if OK, then change password.
 *
 * Canonical call: 	
 *	int status = chkPass(char *username, char *password, char *newpassword);
 *
 * Returns:
 *	TRUE		- Password is valid and changed
 *	FALSE		- Password is invalid or couldn't be changed
 *
 * Platforms:
 *	This code will run as is on VAX and AXP systems running VMS.
 *	Linking with the VAX C run time library (VAXCRTL) is not necessary
 *	nor encouraged.
 *
 */ 

int chgPass(char *username, char *password, char *newpassword)
{

   /* Stuff for SYS$HASH_PASSWORD */
   struct dsc$descriptor_s pwd;			/* password text descriptor */
   struct dsc$descriptor_s newpwd;		/* new password  descriptor */
   unsigned char alg;				/* algorithm type	    */
   unsigned short salt;				/* hash salt value	    */
   struct dsc$descriptor_s usrnam;		/* username text descriptor */
   unsigned long hash[2];			/* hash result		    */
 
   /* Storage areas for same */
   char username_buf[MAXUSERNAMELEN + 1];	/* username text buffer     */
   char pwd_buf[MAXPWDLEN + 1];			/* password text buffer     */

   /* Stuff for SYS$GETUAI for salt and algorithm */
   ITEMLIST_STRUCT itmlst[10];

   /* GETUAI validation information */
   unsigned long user_flags;
   unsigned char min_pwd_length;

   /* Local variables */
   int ss_stat;					/* System service ret. stat */
   int sav_stat;				/* Saved ss_stat across call*/
   int stored_password[2];			/* Hash value from SYSUAF   */

   /* External Routines */
   int strcpy();		/* Included below to avoid VAXCRTL */
   int strlen();		/* Included below to avoid VAXCRTL */
   int str$upcase();
   int sys$exit();
   int sys$getuai();
   int sys$hash_password();

   /* Initialize SYS$GETUAI itemlist */
   usrnam.dsc$a_pointer = username;		/* Given this username*/
   usrnam.dsc$w_length  = strlen(username);
   usrnam.dsc$b_class   = DSC$K_CLASS_S;
   usrnam.dsc$b_dtype   = DSC$K_DTYPE_T;

   pwd.dsc$a_pointer     = password;     	/* And this password */
   pwd.dsc$w_length      = strlen(password);
   pwd.dsc$b_class       = DSC$K_CLASS_S;
   pwd.dsc$b_dtype       = DSC$K_DTYPE_T;

   newpwd.dsc$a_pointer  = newpassword;     	/* And this password */
   newpwd.dsc$w_length   = strlen(newpassword);
   newpwd.dsc$b_class    = DSC$K_CLASS_S;
   newpwd.dsc$b_dtype    = DSC$K_DTYPE_T;

   itmlst[0].item_code             = UAI$_ENCRYPT;    /* Get the encryption */
   itmlst[0].buffer_address        = (char *) &alg;   /* algorithm value    */
   itmlst[0].buffer_length         = sizeof(alg);
   itmlst[0].return_length_address = 0;

   itmlst[1].item_code	           = UAI$_SALT;	      /* Get the salt value */
   itmlst[1].buffer_address        = (char *) &salt;
   itmlst[1].buffer_length         = sizeof(salt);
   itmlst[1].return_length_address = 0;

   itmlst[2].item_code             = UAI$_PWD;                /* Get the stored    */
   itmlst[2].buffer_address        = (char *)&stored_password;/* hashed password   */
   itmlst[2].buffer_length         = sizeof(stored_password);
   itmlst[2].return_length_address = 0;

   itmlst[3].item_code             = UAI$_FLAGS;     /* user flags */
   itmlst[3].buffer_address        = &user_flags;
   itmlst[3].buffer_length         = 4;	             /* 4 bytes */
   itmlst[3].return_length_address = 0;

   itmlst[4].item_code             = UAI$_PWD_LENGTH;/* password min length */
   itmlst[4].buffer_address        = &min_pwd_length;
   itmlst[4].buffer_length         = 1;		     /* one byte */
   itmlst[4].return_length_address = 0;

   itmlst[5].end_of_itemlist       = 0;

/*
 * Assume that any error will be a system error
 */
    strcpy (because,"a system error occurred");

   /* Canonicalize username and password into upper case, just like
      LOGINOUT does.  This restriction may be lifted in a future version
      of VMS.  If that is the case, define NOUPCASE above */

#ifndef NOUPCASE
    ss_stat = str$upcase(&usrnam,&usrnam);
    if (!(ss_stat & 1)) return(FALSE);

    ss_stat = str$upcase(&pwd,&pwd);
    if (!(ss_stat & 1)) return(FALSE);

    ss_stat = str$upcase(&newpwd,&newpwd);
    if (!(ss_stat & 1)) return(FALSE);
#endif /* NOUPCASE */

    /* Get start values from UAF record */
    ss_stat = sys$getuai(0,			/* nullarg */
			0,			/* nullarg */
			&usrnam,		/* usrnam  */
			&itmlst,		/* itmlst  */
			0,			/* nullarg */
			0,			/* nullarg */
			0);			/* nullarg */
    if (!(ss_stat & 1)) return(FALSE);

    ss_stat = sys$hash_password(&pwd,alg,salt,&usrnam,&hash);
    if (!(ss_stat & 1)) return(FALSE);

    /* Gee... let's see if he lied!  */
    if (  (hash[0] == stored_password[0]) &&
          (hash[1] == stored_password[1])) {
	;
    } else {
	strcpy(because,"old password did not validate");
	return(FALSE);		/* password validation error */
    }

/*
 * We know that the old password was good.  Now make sure that
 * the new password is also good.  We have to check a bunch of
 * things.
 */
    if ( (getenv("poppassd_IGNORE_LENGTH")) == NULL) {
	if ( (strlen(newpassword) > MAXPWDLEN) ||
             (strlen(newpassword) < min_pwd_length) ) {
	    strcpy(because,"password too long or too short");
	    return(FALSE);
	}
    }

    if ( !strcmp(password,newpassword) ) {	/* are they the same ? */
	strcpy(because,"new password must be different from old");
	return(FALSE);
    }


    if ( (getenv("poppassd_IGNORE_CAPTIVE")) == NULL) {
	if ( user_flags & UAI$M_CAPTIVE ) {
	    strcpy(because,"captive accounts cannot change their passwords");
	    return(FALSE);
	}
    }

    if ( (getenv("poppassd_IGNORE_DISUSER")) == NULL) {
	if ( user_flags & UAI$M_DISACNT ) {
	    strcpy(because,"DISUSERed accounts cannot change their passwords");
	    return(FALSE);
	}
    }

    if ( (getenv("poppassd_IGNORE_GENPWD")) == NULL) {
	if ( user_flags & UAI$M_GENPWD ) {
	    strcpy(because,"password must be system generated (GENPWD flag)");
	    return(FALSE);
	}
    }

    if ( (getenv("poppassd_IGNORE_LOCKPWD")) == NULL) {
	if ( user_flags & UAI$M_LOCKPWD ) {
	    strcpy(because,"password is locked from change");
	    return(FALSE);
	}
    }

/* The following does not work under all versions of 5.0 of VMS */
#ifdef UAI$M_RESTRICTED		
    if ( (getenv("poppassd_IGNORE_RESTRICTED")) == NULL) {
	if ( user_flags & UAI$M_RESTRICTED ) {
	    strcpy(because,"restricted accounts cannot change their passwords");
	    return(FALSE);
	}
    }
#endif /* uai$m_restricted */

/*
 * Everything seems to be cool.  Set the password.
 */
   itmlst[0].item_code             = UAI$_PASSWORD;   /* set the password */
   itmlst[0].buffer_address        = newpassword;    /* password text */
   itmlst[0].buffer_length         = strlen(newpassword);
   itmlst[0].return_length_address = 0;

   itmlst[1].end_of_itemlist       = 0;

   ss_stat = sys$setuai(0,			/* nullarg */
			0,			/* nullarg */
			&usrnam,		/* usrnam  */
			&itmlst,		/* itmlst  */
			0,			/* nullarg */
			0,			/* nullarg */
			0);			/* nullarg */
   if (!(ss_stat & 1)) return(FALSE);

   return(TRUE);    
}

#ifdef UCX
void
do_ucx_open()
{
static struct sockaddr_in listenchan_name;
struct hostent hostentstruct;
struct hostent *hostentptr;
static char hostname[256];
int flag;
int retval;
int namelength;

/* Do a long and verbose UCX open, ick. */
/* Doesn't work under Multinet anyway. */
    if ( (listenchan = socket (AF_INET, SOCK_STREAM, 0) ) == -1) {
	perror("socket");
	exit(1);
    }
/*
 * Get the local host name 
 */
    retval = gethostname(hostname, sizeof hostname);
    if (retval) {
	perror("gethostname");
	exit(1);
    }
/*
 * Get pointer to network data structure
 */
    if ( (hostentptr = gethostbyname(hostname)) == NULL) {
	perror("gethostbyname");
	exit(1);
    }
/*
 * Copy hostent data to safe storage
 */
    hostentstruct = *hostentptr;
/*
 * Fill in the name & address for the listen socket 
 */
    listenchan_name.sin_family = hostentstruct.h_addrtype;
    listenchan_name.sin_port = htons(poppassd_PORT);
    listenchan_name.sin_addr = * ((struct in_addr *) hostentstruct.h_addr);
/*
 * Bind name to socket
 */
    retval = bind (listenchan, &listenchan_name, sizeof listenchan_name);
    if (retval) {
	perror("bind");
	exit(1);
    }
/*
 * Listen on socket for incoming connections 
 */
    retval = listen (listenchan, 2);
    if (retval) {
	perror("listen");
	exit(1);
    }
/*
 * Accept connection from the socket
 */
    namelength = sizeof (listenchan_name);
    lchan = accept (listenchan, &listenchan_name, &namelength);
    if (lchan == -1) {
	perror("accept");
	exit (1);
    }

    TCPIP = TRUE;

    return;
}
#endif /* UCX */

