Example #1
0
/*
 * passwd - change a user's password file information
 *
 *	This command controls the password file and commands which are used
 * 	to modify it.
 *
 *	The valid options are
 *
 *	-d	delete the password for the named account (*)
 *	-e	expire the password for the named account (*)
 *	-f	execute chfn command to interpret flags
 *	-g	execute gpasswd command to interpret flags
 *	-i #	set sp_inact to # days (*)
 *	-k	change password only if expired
 *	-l	lock the password of the named account (*)
 *	-n #	set sp_min to # days (*)
 *	-r #	change password in # repository
 *	-s	execute chsh command to interpret flags
 *	-S	show password status of named account
 *	-u	unlock the password of the named account (*)
 *	-w #	set sp_warn to # days (*)
 *	-x #	set sp_max to # days (*)
 *
 *	(*) requires root permission to execute.
 *
 *	All of the time fields are entered in days and converted to the
 * 	appropriate internal format. For finer resolute the chage
 *	command must be used.
 */
int main (int argc, char **argv)
{
	const struct passwd *pw;	/* Password file entry for user      */

#ifndef USE_PAM
	char *cp;		/* Miscellaneous character pointing  */

	const struct spwd *sp;	/* Shadow file entry for user   */
#endif				/* !USE_PAM */

	(void) setlocale (LC_ALL, "");
	(void) bindtextdomain (PACKAGE, LOCALEDIR);
	(void) textdomain (PACKAGE);

	/*
	 * The program behaves differently when executed by root than when
	 * executed by a normal user.
	 */
	amroot = (getuid () == 0);

	/*
	 * Get the program name. The program name is used as a prefix to
	 * most error messages.
	 */
	Prog = Basename (argv[0]);

	sanitize_env ();

	OPENLOG ("passwd");

	{
		/*
		 * Parse the command line options.
		 */
		int option_index = 0;
		int c;
		static struct option long_options[] = {
			{"all", no_argument, NULL, 'a'},
			{"delete", no_argument, NULL, 'd'},
			{"expire", no_argument, NULL, 'e'},
			{"help", no_argument, NULL, 'h'},
			{"inactive", required_argument, NULL, 'i'},
			{"keep-tokens", no_argument, NULL, 'k'},
			{"lock", no_argument, NULL, 'l'},
			{"mindays", required_argument, NULL, 'n'},
			{"quiet", no_argument, NULL, 'q'},
			{"root", required_argument, NULL, 'R'},
			{"repository", required_argument, NULL, 'r'},
			{"status", no_argument, NULL, 'S'},
			{"unlock", no_argument, NULL, 'u'},
			{"warndays", required_argument, NULL, 'w'},
			{"maxdays", required_argument, NULL, 'x'},
			{NULL, 0, NULL, '\0'}
		};

		while ((c = getopt_long (argc, argv, "adei:kln:qR:r:Suw:x:",
		                         long_options, &option_index)) != -1) {
			switch (c) {
			case 'a':
				aflg = true;
				break;
			case 'd':
				dflg = true;
				anyflag = true;
				break;
			case 'e':
				eflg = true;
				anyflag = true;
				break;
			case 'i':
				if (   (getlong (optarg, &inact) == 0)
				    || (inact < -1)) {
					fprintf (stderr,
					         _("%s: invalid numeric argument '%s'\n"),
					         Prog, optarg);
					usage (E_BAD_ARG);
				}
				iflg = true;
				anyflag = true;
				break;
			case 'k':
				/* change only if expired, like Linux-PAM passwd -k. */
				kflg = true;	/* ok for users */
				break;
			case 'l':
				lflg = true;
				anyflag = true;
				break;
			case 'n':
				if (   (getlong (optarg, &age_min) == 0)
				    || (age_min < -1)) {
					fprintf (stderr,
					         _("%s: invalid numeric argument '%s'\n"),
					         Prog, optarg);
					usage (E_BAD_ARG);
				}
				nflg = true;
				anyflag = true;
				break;
			case 'q':
				qflg = true;	/* ok for users */
				break;
			case 'R':
				if ('/' != optarg[0]) {
					fprintf (stderr,
					         _("%s: invalid chroot path '%s'\n"),
					         Prog, optarg);
					exit (E_BAD_ARG);
				}
				newroot = optarg;

				if (access (newroot, F_OK) != 0) {
					fprintf(stderr,
					        _("%s: chroot directory %s does not exist\n"),
					        Prog, newroot);
					exit (E_BAD_ARG);
				}
				if ( chroot(newroot) != 0 ) {
					fprintf(stderr,
				            _("%s: unable to chroot to directory %s\n"),
					        Prog, newroot);
					exit (E_BAD_ARG);
				}
				break;
			case 'r':
				/* -r repository (files|nis|nisplus) */
				/* only "files" supported for now */
				if (strcmp (optarg, "files") != 0) {
					fprintf (stderr,
					         _("%s: repository %s not supported\n"),
						 Prog, optarg);
					exit (E_BAD_ARG);
				}
				break;
			case 'S':
				Sflg = true;	/* ok for users */
				break;
			case 'u':
				uflg = true;
				anyflag = true;
				break;
			case 'w':
				if (   (getlong (optarg, &warn) == 0)
				    || (warn < -1)) {
					fprintf (stderr,
					         _("%s: invalid numeric argument '%s'\n"),
					         Prog, optarg);
					usage (E_BAD_ARG);
				}
				wflg = true;
				anyflag = true;
				break;
			case 'x':
				if (   (getlong (optarg, &age_max) == 0)
				    || (age_max < -1)) {
					fprintf (stderr,
					         _("%s: invalid numeric argument '%s'\n"),
					         Prog, optarg);
					usage (E_BAD_ARG);
				}
				xflg = true;
				anyflag = true;
				break;
			default:
				usage (E_BAD_ARG);
			}
		}
	}

	/*
	 * Now I have to get the user name. The name will be gotten from the
	 * command line if possible. Otherwise it is figured out from the
	 * environment.
	 */
	pw = get_my_pwent ();
	if (NULL == pw) {
		fprintf (stderr,
		         _("%s: Cannot determine your user name.\n"), Prog);
		SYSLOG ((LOG_WARN, "Cannot determine the user name of the caller (UID %lu)",
		         (unsigned long) getuid ()));
		exit (E_NOPERM);
	}
	myname = xstrdup (pw->pw_name);
	if (optind < argc) {
		name = argv[optind];
	} else {
		name = myname;
	}

	/*
	 * Make sure that at most one username was specified.
	 */
	if (argc > (optind+1)) {
		usage (E_USAGE);
	}

	/*
	 * The -a flag requires -S, no other flags, no username, and
	 * you must be root.  --marekm
	 */
	if (aflg) {
		if (anyflag || !Sflg || (optind < argc)) {
			usage (E_USAGE);
		}
		if (!amroot) {
			fprintf (stderr, _("%s: Permission denied.\n"), Prog);
			exit (E_NOPERM);
		}
		setpwent ();
		while ( (pw = getpwent ()) != NULL ) {
			print_status (pw);
		}
		endpwent ();
		exit (E_SUCCESS);
	}
#if 0
	/*
	 * Allow certain users (administrators) to change passwords of
	 * certain users. Not implemented yet. --marekm
	 */
	if (may_change_passwd (myname, name))
		amroot = 1;
#endif

	/*
	 * If any of the flags were given, a user name must be supplied on
	 * the command line. Only an unadorned command line doesn't require
	 * the user's name be given. Also, -x, -n, -w, -i, -e, -d,
	 * -l, -u may appear with each other. -S, -k must appear alone.
	 */

	/*
	 * -S now ok for normal users (check status of my own account), and
	 * doesn't require username.  --marekm
	 */
	if (anyflag && optind >= argc) {
		usage (E_USAGE);
	}

	if (   (Sflg && kflg)
	    || (anyflag && (Sflg || kflg))) {
		usage (E_USAGE);
	}

	if (anyflag && !amroot) {
		fprintf (stderr, _("%s: Permission denied.\n"), Prog);
		exit (E_NOPERM);
	}

	pw = xgetpwnam (name);
	if (NULL == pw) {
		fprintf (stderr, _("%s: user '%s' does not exist\n"), Prog, name);
		exit (E_NOPERM);
	}
#ifdef WITH_SELINUX
	/* only do this check when getuid()==0 because it's a pre-condition for
	   changing a password without entering the old one */
	if ((is_selinux_enabled() > 0) && (getuid() == 0) &&
	    (check_selinux_access (name, pw->pw_uid, PASSWD__PASSWD) != 0)) {
		security_context_t user_context = NULL;
		const char *user = "******";
		if (getprevcon (&user_context) == 0) {
			user = user_context;
		}
		SYSLOG ((LOG_ALERT,
		         "%s is not authorized to change the password of %s",
		         user, name));
		fprintf(stderr,
		        _("%s: %s is not authorized to change the password of %s\n"),
		        Prog, user, name);
		if (NULL != user_context) {
			freecon (user_context);
		}
		exit (E_NOPERM);
	}
#endif				/* WITH_SELINUX */

	/*
	 * If the UID of the user does not match the current real UID,
	 * check if I'm root.
	 */
	if (!amroot && (pw->pw_uid != getuid ())) {
		fprintf (stderr,
		         _("%s: You may not view or modify password information for %s.\n"),
		         Prog, name);
		SYSLOG ((LOG_WARN,
			 "%s: can't view or modify password information for %s",
			 Prog, name));
		closelog ();
		exit (E_NOPERM);
	}

	if (Sflg) {
		print_status (pw);
		exit (E_SUCCESS);
	}
#ifndef USE_PAM
	/*
	 * The user name is valid, so let's get the shadow file entry.
	 */
	sp = getspnam (name); /* !USE_PAM, no need for xgetspnam */
	if (NULL == sp) {
		sp = pwd_to_spwd (pw);
	}

	cp = sp->sp_pwdp;

	/*
	 * If there are no other flags, just change the password.
	 */
	if (!anyflag) {
		STRFCPY (crypt_passwd, cp);

		/*
		 * See if the user is permitted to change the password. 
		 * Otherwise, go ahead and set a new password.
		 */
		check_password (pw, sp);

		/*
		 * Let the user know whose password is being changed.
		 */
		if (!qflg) {
			printf (_("Changing password for %s\n"), name);
		}

		if (new_password (pw)) {
			fprintf (stderr,
				 _("The password for %s is unchanged.\n"),
				 name);
			closelog ();
			exit (E_NOPERM);
		}
		do_update_pwd = true;
		do_update_age = true;
	}
#endif				/* !USE_PAM */
	/*
	 * Before going any further, raise the ulimit to prevent colliding
	 * into a lowered ulimit, and set the real UID to root to protect
	 * against unexpected signals. Any keyboard signals are set to be
	 * ignored.
	 */
	pwd_init ();

#ifdef USE_PAM
	/*
	 * Don't set the real UID for PAM...
	 */
	if (!anyflag) {
		do_pam_passwd (name, qflg, kflg);
		exit (E_SUCCESS);
	}
#endif				/* USE_PAM */
	if (setuid (0) != 0) {
		fputs (_("Cannot change ID to root.\n"), stderr);
		SYSLOG ((LOG_ERR, "can't setuid(0)"));
		closelog ();
		exit (E_NOPERM);
	}
	if (spw_file_present ()) {
		update_shadow ();
	} else {
		update_noshadow ();
	}

	nscd_flush_cache ("passwd");
	nscd_flush_cache ("group");

	SYSLOG ((LOG_INFO, "password for '%s' changed by '%s'", name, myname));
	closelog ();
	if (!qflg) {
		if (!anyflag) {
#ifndef USE_PAM
			printf (_("%s: password changed.\n"), Prog);
#endif				/* USE_PAM */
		} else {
			printf (_("%s: password expiry information changed.\n"), Prog);
		}
	}

	return E_SUCCESS;
}
Example #2
0
int passwd_main(int argc UNUSED_PARAM, char **argv)
{
	enum {
		OPT_algo   = (1 << 0), /* -a - password algorithm */
		OPT_lock   = (1 << 1), /* -l - lock account */
		OPT_unlock = (1 << 2), /* -u - unlock account */
		OPT_delete = (1 << 3), /* -d - delete password */
		OPT_lud    = OPT_lock | OPT_unlock | OPT_delete,
	};
	unsigned opt;
	int rc;
	const char *opt_a = CONFIG_FEATURE_DEFAULT_PASSWD_ALGO;
	const char *filename;
	char *myname;
	char *name;
	char *newp;
	struct passwd *pw;
	uid_t myuid;
	struct rlimit rlimit_fsize;
	char c;
#if ENABLE_FEATURE_SHADOWPASSWDS
	/* Using _r function to avoid pulling in static buffers */
	struct spwd spw;
	char buffer[256];
#endif

	logmode = LOGMODE_BOTH;
	openlog(applet_name, 0, LOG_AUTH);
	opt = getopt32(argv, "a:lud", &opt_a);
	//argc -= optind;
	argv += optind;

	myuid = getuid();
	/* -l, -u, -d require root priv and username argument */
	if ((opt & OPT_lud) && (myuid != 0 || !argv[0]))
		bb_show_usage();

	/* Will complain and die if username not found */
	myname = xstrdup(xuid2uname(myuid));
	name = argv[0] ? argv[0] : myname;

	pw = xgetpwnam(name);
	if (myuid != 0 && pw->pw_uid != myuid) {
		/* LOGMODE_BOTH */
		bb_error_msg_and_die("%s can't change password for %s", myname, name);
	}

#if ENABLE_FEATURE_SHADOWPASSWDS
	{
		/* getspnam_r may return 0 yet set result to NULL.
		 * At least glibc 2.4 does this. Be extra paranoid here. */
		struct spwd *result = NULL;
		errno = 0;
		if (getspnam_r(pw->pw_name, &spw, buffer, sizeof(buffer), &result) != 0
		 || !result /* no error, but no record found either */
		 || strcmp(result->sp_namp, pw->pw_name) != 0 /* paranoia */
		) {
			if (errno != ENOENT) {
				/* LOGMODE_BOTH */
				bb_perror_msg("no record of %s in %s, using %s",
					name, bb_path_shadow_file,
					bb_path_passwd_file);
			}
			/* else: /etc/shadow does not exist,
			 * apparently we are on a shadow-less system,
			 * no surprise there */
		} else {
			pw->pw_passwd = result->sp_pwdp;
		}
	}
#endif

	/* Decide what the new password will be */
	newp = NULL;
	c = pw->pw_passwd[0] - '!';
	if (!(opt & OPT_lud)) {
		if (myuid != 0 && !c) { /* passwd starts with '!' */
			/* LOGMODE_BOTH */
			bb_error_msg_and_die("can't change "
					"locked password for %s", name);
		}
		printf("Changing password for %s\n", name);
		newp = new_password(pw, myuid, opt_a);
		if (!newp) {
			logmode = LOGMODE_STDIO;
			bb_error_msg_and_die("password for %s is unchanged", name);
		}
	} else if (opt & OPT_lock) {
		if (!c)
			goto skip; /* passwd starts with '!' */
		newp = xasprintf("!%s", pw->pw_passwd);
	} else if (opt & OPT_unlock) {
		if (c)
			goto skip; /* not '!' */
		/* pw->pw_passwd points to static storage,
		 * strdup'ing to avoid nasty surprizes */
		newp = xstrdup(&pw->pw_passwd[1]);
	} else if (opt & OPT_delete) {
		newp = (char*)"";
	}

	rlimit_fsize.rlim_cur = rlimit_fsize.rlim_max = 512L * 30000;
	setrlimit(RLIMIT_FSIZE, &rlimit_fsize);
	bb_signals(0
		+ (1 << SIGHUP)
		+ (1 << SIGINT)
		+ (1 << SIGQUIT)
		, SIG_IGN);
	umask(077);
	xsetuid(0);

#if ENABLE_FEATURE_SHADOWPASSWDS
	filename = bb_path_shadow_file;
	rc = update_passwd(bb_path_shadow_file, name, newp, NULL);
	if (rc > 0)
		/* password in /etc/shadow was updated */
		newp = (char*) "x";
	if (rc >= 0)
		/* 0 = /etc/shadow missing (not an error), >0 = passwd changed in /etc/shadow */
#endif
	{
		filename = bb_path_passwd_file;
		rc = update_passwd(bb_path_passwd_file, name, newp, NULL);
	}
	/* LOGMODE_BOTH */
	if (rc < 0)
		bb_error_msg_and_die("can't update password file %s", filename);
	bb_error_msg("password for %s changed by %s", name, myname);

	/*if (ENABLE_FEATURE_CLEAN_UP) free(newp); - can't, it may be non-malloced */
 skip:
	if (!newp) {
		bb_error_msg_and_die("password for %s is already %slocked",
			name, (opt & OPT_unlock) ? "un" : "");
	}

	if (ENABLE_FEATURE_CLEAN_UP)
		free(myname);
	return 0;
}