/*****************************************************************************
 *                             Listing 3                                     *
 *****************************************************************************/

/*
 * Program to change a user's password
 */

#include        <stdio.h>
#include        <pwd.h>
 
extern char *getlogin ();
extern char *getpass ();
extern char *crypt ();
extern char *malloc ();

static int  put_encrypted    ();

main (argc, argv)
int argc;
char *argv [];
{
	char encrypted [15];
	char password [BUFSIZ];
	char *cp;
	char *oldpwd;
	char *newpwd1;
	char *newpwd2;
	char *user;
	int  isroot;
	struct passwd *pe;

	user = (char *) NULL;
	isroot = (getuid () == 0);

	/*
	 * Find out the name of the user currently logged in
	 */
	if ((user = getlogin ()) == NULL)
	{
		/*
		 * You may or may not want to include this secondary
		 * method of determining the current user
		 */
		if ((pe = getpwuid (getuid ())) != NULL)
			cp = pe->pw_name;
		else
			fprintf (stderr, "%s: Who are you ???\n", argv [0]);
	}

	/*
	 * We must do this to get root privileges on some systems, although
	 * we may be already running with setuid root privileges.
	 */
	setuid (geteuid ());

	/*
	 * Determine the user whose password is to be changed
	 */
	if (argc > 1)
	{
		user = (char *) NULL;

		/*
		 * Take user from command line argument
		 */
		if (argc > 2)
			fprintf (stderr, "Usage: %s [username]\n", argv [0]);
		else
		{
			/*
			 * Only allow the super user to change other user's passwords
			 */
			if (!isroot && (user == NULL || strcmp (user, argv [1]) != 0))
				fprintf (stderr, "%s: Permission denied\n", argv [0]);
			else
				user = argv [1];
		}
	}

	/*
	 * Check for validity of user
	 */
	if (user == (char *) NULL)
		exit (1);

	if (getpwnam (user) == NULL)
	{
		fprintf (stderr, "%s: Unknow user %s\n", argv [0], user);
		exit (1);
	}
	
	/*
	 * Get old (encrypted) password
	 */
	if (get_encrypted (user, password) != 0)
	{
		perror (argv [0]);
		exit (1);
	}

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

	/*
	 * If you are super user or if the user had no password,
	 * then we do not need to verify the old password.
	 */
	if (!isroot && strlen (password) != 0)
	{
		if ((oldpwd = getpass ("Old password: ")) == NULL)
		{
			perror (argv [0]);
			exit (1);
		}

		/*
		 * The first two letters of the current password are the
		 * salt used to encrypt the password. Consequently we
		 * must use it again to encrypt the password that the
		 * user claims is the current one.
		 */
		cp = crypt (oldpwd, password);

		if (strcmp (cp, password) != 0)
		{
			/*
			 * The user was mistaken. The inputed password is not
			 * the same as the current password.
			 */
			fprintf (stderr, "Sorry.\n");
			exit (1);
		}
	}

	/*
	 * So far so good. Now lets get the user to input the new password.
	 * We make the user do it twice to ensure that they did not make
	 * a typing error while entering it.
	 */
	if ((newpwd1 = getpass ("New password: ")) == NULL)
	{
		perror (argv [0]);
		exit (1);
	}

	/*
	 * getpass() returns pointer to static data. Copy it before we
	 * call it again.
	 */
	strcpy (password, newpwd1);

	if ((newpwd2 = getpass ("Retype new password: ")) == NULL)
	{
		perror (argv [0]);
		exit (1);
	}

	/*
	 * Are they the same ???
	 */
	if (strcmp (password, newpwd2) != 0)
	{
		fprintf (stderr, "Mismatch - password unchanged\n");
		exit (1);
	}

	/*
	 * At this time you may want to implement some rules for
	 * the password. Some examples:
	 *
	 *   - Minimum password length
	 *   - Password different from username
	 *   - Password not in dictionary file
	 */ 

	/*
	 * Create a new encrypted password
	 */
	make_passwd (password, encrypted);

	/*
	 * Save the new password
	 */
	put_encrypted (user, encrypted);
}

static int
put_encrypted (user, encrypted)
char *user;
char *encrypted;
{
	fprintf (stderr, "Encrypted password for user %s is %s\n", user, encrypted);
}


