
/*
    passwd for LinuxWare

    Copyright (C) 1994, 1995  Ales Dryak <e-mail: A.Dryak@sh.cvut.cz>

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

*/

/* passwd.c - change password on an account
 * Initially written for Linux by Peter Orbaek <poe@daimi.aau.dk>
 * Currently maintained at ftp://ftp.daimi.aau.dk/pub/linux/poe/
 */

/* Hacked by Alvaro Martinez Echevarria, alvaro@enano.etsit.upm.es,
   to allow peaceful coexistence with yp. Nov 94. */
   
/* August 1995: Rewritten by Ales Dryak (A.Dryak@sh.cvut.cz) for LinuxWare */ 

#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <unistd.h>
#include <termios.h>
#include <pwd.h>
#include <ctype.h>
#include <time.h>
#include <nwcrypt.h>
#include "config.h"
#include "lwpwd.h"

#define PASSFILE PASSWD_FILENAME
#define TMPFILE PASSWD_FILENAME".tmp"
#define OLDFILE PASSWD_FILENAME".OLD"

char* strup(char* p)
{
	for(;*p;p++)
	{
		*p=toupper(*p);
	}
	return p;
}

#define ascii_to_bin(c) ((c)>='a'?(c-59):(c)>='A'?((c)-53):(c)-'.')
#define bin_to_ascii(c) ((c)>=38?((c)-38+'a'):(c)>=12?((c)-12+'A'):(c)+'.')

#define MAX_LENGTH	1024

int
main(argc, argv)
	int argc;
	char *argv[];
{
	struct passwd *pe;
	uid_t gotuid = getuid();
	char *pwdstr;
	int other;
	char *p, *q, *user;
	FILE *fd_in, *fd_out;
	int error=0;
	int r;
	
	lwpwent olde; /* old entry from file */
	lwpwent newe; /* new entry */
	byte oid[4]; /* obj id */
	FILE* lwf; /* lw pwd file */

	umask(022);

	if(argc > 2) {
		puts("Too many arguments");
		exit(1);
	} else if(argc == 2) {
		if(gotuid) {
			puts("Only root can change the password for others");
			exit(1);
		}
		user = argv[1];
	} else {
		if (!(user = getlogin())) {
		   if (!(pe = getpwuid( getuid() ))) {
		      puts("Cannot find login name");
		      exit(1);
		   } else
			 user = pe->pw_name;
                }
	}

	if(!(pe = getpwnam(user))) {
		puts("Can't find username anywhere. Are you really a user?");
		exit(1);
	}

	/* if somebody got into changing utmp... */
	if(gotuid && gotuid != pe->pw_uid) {
	    puts("UID and username does not match, imposter!");
	    exit(1);
	}

	oid[0]=(pe->pw_uid>>24)&0xFF;
	oid[1]=(pe->pw_uid>>16)&0xFF;
	oid[2]=(pe->pw_uid>>8)&0xFF;
	oid[3]=pe->pw_uid&0xFF;

	if ((lwf=fopen(PASSFILE,"r"))==NULL) {perror(PASSFILE);return 1;}
	strup(user);
	if (!getlwpwnam(lwf,&olde,user)) {
	    puts("Sorry, no LinuxWare access.");
	    exit(1);
	}
	newe=olde;
	fclose(lwf);


	printf( "Changing password for %s\n", user );

	if(gotuid && !olde.expired) {
		lwpwent ine; /* old password from user */
		
		pwdstr = getpass("Enter old password: ");
		strup(pwdstr);
 		p362(ine.passwd.passwd,pwdstr,strlen(pwdstr),oid);
		if (memcmp(ine.passwd.passwd,olde.passwd.passwd,sizeof(ine.passwd))!=0){
			puts("Illegal password, imposter.");
			exit(1);
		}

	}
	
redo_it:
	{
		lwpwent tmp;
		pwdstr = getpass("Enter new password: ");
		strup(pwdstr);
 		p362(tmp.passwd.passwd,pwdstr,strlen(pwdstr),oid);
		pwdstr = getpass("Re-type new password: ");
		strup(pwdstr);
 		p362(newe.passwd.passwd,pwdstr,strlen(pwdstr),oid);
		if(memcmp(tmp.passwd.passwd, newe.passwd.passwd, sizeof(tmp.passwd))) {
			puts("You misspelled it. Password not changed.");
			exit(0);
		}
	}

	if((strlen(pwdstr) < 6) && gotuid) {
		puts("The password must have at least 6 characters, try again.");
		goto redo_it;
	}
	
	other = 0;
	for(p = pwdstr; *p; p++) {
		other = other || !isalpha(*p);
	}
	
	if(!other && gotuid) {
		puts("The password must have non-letters; try again.");
		goto redo_it;
	}
	
	r = 0;
	for(p = pwdstr, q = pe->pw_name; *q && *p; q++, p++) {
	  if(tolower(*p) != tolower(*q)) {
	    r = 1;
	    break;
	  }
	}

	for(p = pwdstr + strlen(pwdstr)-1, q = pe->pw_name;
	    *q && p >= pwdstr; q++, p--) {
	  if(tolower(*p) != tolower(*q)) {
	    r += 2;
	    break;
	  }
	}

	if(gotuid && r != 3) {
	  puts("Please don't use something like your username as password!");
	  goto redo_it;
	}

	newe.expired=0;

	/* do various other checks for stupid passwords here... */

	if(access(TMPFILE, F_OK) == 0) {
		puts(TMPFILE" exists, can't change password");
		exit(1);
	}
	
	if(!(fd_out = fopen(TMPFILE, "w"))) {
		puts("Can't open "TMPFILE", can't update password");
		exit(1);
	}

	if(!(fd_in = fopen(PASSFILE, "r"))) {
		puts("Can't read "PASSFILE", can't update password");
		exit(1);
	}
	while(1) {
		lwpwent tmp;
		if (!getlwpwent(fd_in,&tmp)) break;
		if(strcmp(olde.name.name,tmp.name.name)==0) {
			if(!putlwpwent(fd_out,&newe)) {
				error = 1;
			}
		} else {
			if(!putlwpwent(fd_out,&tmp)) {
				error = 1;
			}
		}
		if(error) {
			puts("Error while writing new password file, password not changed.");
			fclose(fd_out);
			unlink(TMPFILE);
			exit(1);
		}
	}
	fclose(fd_in);
	fclose(fd_out);

	unlink(OLDFILE);
	link(PASSFILE, OLDFILE);
	unlink(PASSFILE);
	link(TMPFILE, PASSFILE);
	unlink(TMPFILE);
	chmod(PASSFILE, 0600);
	chown(PASSFILE, 0, 0);

	puts("Password changed.");	
	exit(0);
}
