Exemple #1
0
int main (int argc, char **argv)
{
	char buf[BUFSIZ];
	char *fields[8];
	int nfields;
	char *cp;
	const struct passwd *pw;
	struct passwd newpw;
	int errors = 0;
	int line = 0;
	uid_t uid;
	gid_t gid;
#ifdef USE_PAM
	int *lines = NULL;
	char **usernames = NULL;
	char **passwords = NULL;
	unsigned int nusers = 0;
#endif				/* USE_PAM */

	Prog = Basename (argv[0]);

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

	/* FIXME: will not work with an input file */
	process_root_flag ("-R", argc, argv);

	OPENLOG ("newusers");

	process_flags (argc, argv);

	check_perms ();

	is_shadow = spw_file_present ();

#ifdef SHADOWGRP
	is_shadow_grp = sgr_file_present ();
#endif
#ifdef ENABLE_SUBIDS
	is_sub_uid = sub_uid_file_present () && !rflg;
	is_sub_gid = sub_gid_file_present () && !rflg;
#endif				/* ENABLE_SUBIDS */

	open_files ();

	/*
	 * Read each line. The line has the same format as a password file
	 * entry, except that certain fields are not constrained to be
	 * numerical values. If a group ID is entered which does not already
	 * exist, an attempt is made to allocate the same group ID as the
	 * numerical user ID. Should that fail, the next available group ID
	 * over 100 is allocated. The pw_gid field will be updated with that
	 * value.
	 */
	while (fgets (buf, (int) sizeof buf, stdin) != (char *) 0) {
		line++;
		cp = strrchr (buf, '\n');
		if (NULL != cp) {
			*cp = '\0';
		} else {
			if (feof (stdin) == 0) {
				fprintf (stderr,
				         _("%s: line %d: line too long\n"),
				         Prog, line);
				errors++;
				continue;
			}
		}

		/*
		 * Break the string into fields and screw around with them.
		 * There MUST be 7 colon separated fields, although the
		 * values aren't that particular.
		 */
		for (cp = buf, nfields = 0; nfields < 7; nfields++) {
			fields[nfields] = cp;
			cp = strchr (cp, ':');
			if (NULL != cp) {
				*cp = '\0';
				cp++;
			} else {
				break;
			}
		}
		if (nfields != 6) {
			fprintf (stderr, _("%s: line %d: invalid line\n"),
			         Prog, line);
			errors++;
			continue;
		}

		/*
		 * First check if we have to create or update an user
		 */
		pw = pw_locate (fields[0]);
		/* local, no need for xgetpwnam */
		if (   (NULL == pw)
		    && (getpwnam (fields[0]) != NULL)) {
			fprintf (stderr, _("%s: cannot update the entry of user %s (not in the passwd database)\n"), Prog, fields[0]);
			errors++;
			continue;
		}

		if (   (NULL == pw)
		    && (get_user_id (fields[2], &uid) != 0)) {
			fprintf (stderr,
			         _("%s: line %d: can't create user\n"),
			         Prog, line);
			errors++;
			continue;
		}

		/*
		 * Processed is the group name. A new group will be
		 * created if the group name is non-numeric and does not
		 * already exist. If the group name is a number (which is not
		 * an existing GID), a group with the same name as the user
		 * will be created, with the given GID. The given or created
		 * group will be the primary group of the user. If
		 * there is no named group to be a member of, the UID will
		 * be figured out and that value will be a candidate for a
		 * new group, if that group ID exists, a whole new group ID
		 * will be made up.
		 */
		if (   (NULL == pw)
		    && (add_group (fields[0], fields[3], &gid, uid) != 0)) {
			fprintf (stderr,
			         _("%s: line %d: can't create group\n"),
			         Prog, line);
			errors++;
			continue;
		}

		/*
		 * Now we work on the user ID. It has to be specified either
		 * as a numerical value, or left blank. If it is a numerical
		 * value, that value will be used, otherwise the next
		 * available user ID is computed and used. After this there
		 * will at least be a (struct passwd) for the user.
		 */
		if (   (NULL == pw)
		    && (add_user (fields[0], uid, gid) != 0)) {
			fprintf (stderr,
			         _("%s: line %d: can't create user\n"),
			         Prog, line);
			errors++;
			continue;
		}

		/*
		 * The password, gecos field, directory, and shell fields
		 * all come next.
		 */
		pw = pw_locate (fields[0]);
		if (NULL == pw) {
			fprintf (stderr,
			         _("%s: line %d: user '%s' does not exist in %s\n"),
			         Prog, line, fields[0], pw_dbname ());
			errors++;
			continue;
		}
		newpw = *pw;

#ifdef USE_PAM
		/* keep the list of user/password for later update by PAM */
		nusers++;
		lines     = realloc (lines,     sizeof (lines[0])     * nusers);
		usernames = realloc (usernames, sizeof (usernames[0]) * nusers);
		passwords = realloc (passwords, sizeof (passwords[0]) * nusers);
		lines[nusers-1]     = line;
		usernames[nusers-1] = strdup (fields[0]);
		passwords[nusers-1] = strdup (fields[1]);
#endif				/* USE_PAM */
		if (add_passwd (&newpw, fields[1]) != 0) {
			fprintf (stderr,
			         _("%s: line %d: can't update password\n"),
			         Prog, line);
			errors++;
			continue;
		}
		if ('\0' != fields[4][0]) {
			newpw.pw_gecos = fields[4];
		}

		if ('\0' != fields[5][0]) {
			newpw.pw_dir = fields[5];
		}

		if ('\0' != fields[6][0]) {
			newpw.pw_shell = fields[6];
		}

		if (   ('\0' != fields[5][0])
		    && (access (newpw.pw_dir, F_OK) != 0)) {
/* FIXME: should check for directory */
			mode_t msk = 0777 & ~getdef_num ("UMASK",
			                                 GETDEF_DEFAULT_UMASK);
			if (mkdir (newpw.pw_dir, msk) != 0) {
				fprintf (stderr,
				         _("%s: line %d: mkdir %s failed: %s\n"),
				         Prog, line, newpw.pw_dir,
				         strerror (errno));
			} else if (chown (newpw.pw_dir,
			                  newpw.pw_uid,
			                  newpw.pw_gid) != 0) {
				fprintf (stderr,
				         _("%s: line %d: chown %s failed: %s\n"),
				         Prog, line, newpw.pw_dir,
				         strerror (errno));
			}
		}

		/*
		 * Update the password entry with the new changes made.
		 */
		if (pw_update (&newpw) == 0) {
			fprintf (stderr,
			         _("%s: line %d: can't update entry\n"),
			         Prog, line);
			errors++;
			continue;
		}

#ifdef ENABLE_SUBIDS
		/*
		 * Add subordinate uids if the user does not have them.
		 */
		if (is_sub_uid && !sub_uid_assigned(fields[0])) {
			uid_t sub_uid_start = 0;
			unsigned long sub_uid_count = 0;
			if (find_new_sub_uids(fields[0], &sub_uid_start, &sub_uid_count) == 0) {
				if (sub_uid_add(fields[0], sub_uid_start, sub_uid_count) == 0) {
					fprintf (stderr,
						_("%s: failed to prepare new %s entry\n"),
						Prog, sub_uid_dbname ());
				}
			} else {
				fprintf (stderr,
					_("%s: can't find subordinate user range\n"),
					Prog);
				errors++;
			}
		}

		/*
		 * Add subordinate gids if the user does not have them.
		 */
		if (is_sub_gid && !sub_gid_assigned(fields[0])) {
			gid_t sub_gid_start = 0;
			unsigned long sub_gid_count = 0;
			if (find_new_sub_gids(fields[0], &sub_gid_start, &sub_gid_count) == 0) {
				if (sub_gid_add(fields[0], sub_gid_start, sub_gid_count) == 0) {
					fprintf (stderr,
						_("%s: failed to prepare new %s entry\n"),
						Prog, sub_uid_dbname ());
				}
			} else {
				fprintf (stderr,
					_("%s: can't find subordinate group range\n"),
					Prog);
				errors++;
			}
		}
#endif				/* ENABLE_SUBIDS */
	}

	/*
	 * Any detected errors will cause the entire set of changes to be
	 * aborted. Unlocking the password file will cause all of the
	 * changes to be ignored. Otherwise the file is closed, causing the
	 * changes to be written out all at once, and then unlocked
	 * afterwards.
	 */
	if (0 != errors) {
		fprintf (stderr,
		         _("%s: error detected, changes ignored\n"), Prog);
		fail_exit (EXIT_FAILURE);
	}

	close_files ();

	nscd_flush_cache ("passwd");
	nscd_flush_cache ("group");
	sssd_flush_cache (SSSD_DB_PASSWD | SSSD_DB_GROUP);

#ifdef USE_PAM
	unsigned int i;
	/* Now update the passwords using PAM */
	for (i = 0; i < nusers; i++) {
		if (do_pam_passwd_non_interactive ("newusers", usernames[i], passwords[i]) != 0) {
			fprintf (stderr,
			         _("%s: (line %d, user %s) password not changed\n"),
			         Prog, lines[i], usernames[i]);
			errors++;
		}
	}
#endif				/* USE_PAM */

	return ((0 == errors) ? EXIT_SUCCESS : EXIT_FAILURE);
}
Exemple #2
0
/*
 * main - userdel command
 */
int main (int argc, char **argv)
{
	int errors = 0; /* Error in the removal of the home directory */

#ifdef ACCT_TOOLS_SETUID
#ifdef USE_PAM
	pam_handle_t *pamh = NULL;
	int retval;
#endif				/* USE_PAM */
#endif				/* ACCT_TOOLS_SETUID */

	/*
	 * Get my name so that I can use it to report errors.
	 */
	Prog = Basename (argv[0]);
	(void) setlocale (LC_ALL, "");
	(void) bindtextdomain (PACKAGE, LOCALEDIR);
	(void) textdomain (PACKAGE);

	process_root_flag ("-R", argc, argv);

	OPENLOG ("userdel");
#ifdef WITH_AUDIT
	audit_help_open ();
#endif				/* WITH_AUDIT */

	{
		/*
		 * Parse the command line options.
		 */
		int c;
		static struct option long_options[] = {
			{"force",        no_argument,       NULL, 'f'},
			{"help",         no_argument,       NULL, 'h'},
			{"remove",       no_argument,       NULL, 'r'},
			{"root",         required_argument, NULL, 'R'},
#ifdef WITH_SELINUX
			{"selinux-user", no_argument,       NULL, 'Z'},
#endif				/* WITH_SELINUX */
			{NULL, 0, NULL, '\0'}
		};
		while ((c = getopt_long (argc, argv,
#ifdef WITH_SELINUX             
		                         "fhrR:Z",
#else				/* !WITH_SELINUX */
		                         "fhrR:",
#endif				/* !WITH_SELINUX */
		                         long_options, NULL)) != -1) {
			switch (c) {
			case 'f':	/* force remove even if not owned by user */
				fflg = true;
				break;
			case 'h':
				usage (E_SUCCESS);
				break;
			case 'r':	/* remove home dir and mailbox */
				rflg = true;
				break;
			case 'R': /* no-op, handled in process_root_flag () */
				break;
#ifdef WITH_SELINUX             
			case 'Z':
				if (is_selinux_enabled () > 0) {
					Zflg = true;
				} else {
					fprintf (stderr,
					         _("%s: -Z requires SELinux enabled kernel\n"),
					         Prog);

					exit (E_BAD_ARG);
				}
				break;
#endif				/* WITH_SELINUX */
			default:
				usage (E_USAGE);
			}
		}
	}

	if ((optind + 1) != argc) {
		usage (E_USAGE);
	}

#ifdef ACCT_TOOLS_SETUID
#ifdef USE_PAM
	{
		struct passwd *pampw;
		pampw = getpwuid (getuid ()); /* local, no need for xgetpwuid */
		if (pampw == NULL) {
			fprintf (stderr,
			         _("%s: Cannot determine your user name.\n"),
			         Prog);
			exit (E_PW_UPDATE);
		}

		retval = pam_start ("userdel", pampw->pw_name, &conv, &pamh);
	}

	if (PAM_SUCCESS == retval) {
		retval = pam_authenticate (pamh, 0);
	}

	if (PAM_SUCCESS == retval) {
		retval = pam_acct_mgmt (pamh, 0);
	}

	if (PAM_SUCCESS != retval) {
		fprintf (stderr, _("%s: PAM: %s\n"),
		         Prog, pam_strerror (pamh, retval));
		SYSLOG((LOG_ERR, "%s", pam_strerror (pamh, retval)));
		if (NULL != pamh) {
			(void) pam_end (pamh, retval);
		}
		exit (E_PW_UPDATE);
	}
	(void) pam_end (pamh, retval);
#endif				/* USE_PAM */
#endif				/* ACCT_TOOLS_SETUID */

	is_shadow_pwd = spw_file_present ();
#ifdef SHADOWGRP
	is_shadow_grp = sgr_file_present ();
#endif				/* SHADOWGRP */
#ifdef ENABLE_SUBIDS
	is_sub_uid = sub_uid_file_present ();
	is_sub_gid = sub_gid_file_present ();
#endif				/* ENABLE_SUBIDS */

	/*
	 * Start with a quick check to see if the user exists.
	 */
	user_name = argv[argc - 1];
	{
		struct passwd *pwd;
		pwd = getpwnam (user_name); /* local, no need for xgetpwnam */
		if (NULL == pwd) {
			fprintf (stderr, _("%s: user '%s' does not exist\n"),
				 Prog, user_name);
#ifdef WITH_AUDIT
			audit_logger (AUDIT_DEL_USER, Prog,
			              "deleting user not found",
			              user_name, AUDIT_NO_ID,
			              SHADOW_AUDIT_FAILURE);
#endif				/* WITH_AUDIT */
			exit (E_NOTFOUND);
		}
		user_id = pwd->pw_uid;
		user_gid = pwd->pw_gid;
		user_home = xstrdup (pwd->pw_dir);
	}
#ifdef WITH_TCB
	if (shadowtcb_set_user (user_name) == SHADOWTCB_FAILURE) {
		exit (E_NOTFOUND);
	}
#endif				/* WITH_TCB */
#ifdef	USE_NIS

	/*
	 * Now make sure it isn't an NIS user.
	 */
	if (__ispwNIS ()) {
		char *nis_domain;
		char *nis_master;

		fprintf (stderr,
		         _("%s: user %s is a NIS user\n"), Prog, user_name);
		if (   !yp_get_default_domain (&nis_domain)
		    && !yp_master (nis_domain, "passwd.byname", &nis_master)) {
			fprintf (stderr,
			         _("%s: %s is the NIS master\n"),
			         Prog, nis_master);
		}
		exit (E_NOTFOUND);
	}
#endif				/* USE_NIS */
	/*
	 * Check to make certain the user isn't logged in.
	 * Note: This is a best effort basis. The user may log in between,
	 * a cron job may be started on her behalf, etc.
	 */
	if (user_busy (user_name, user_id) != 0) {
		if (!fflg) {
#ifdef WITH_AUDIT
			audit_logger (AUDIT_DEL_USER, Prog,
			              "deleting user logged in",
			              user_name, AUDIT_NO_ID,
			              SHADOW_AUDIT_FAILURE);
#endif				/* WITH_AUDIT */
			exit (E_USER_BUSY);
		}
	}

	/*
	 * Do the hard stuff - open the files, create the user entries,
	 * create the home directory, then close and update the files.
	 */
	open_files ();
	update_user ();
	update_groups ();

	if (rflg) {
		errors += remove_mailbox ();
	}
	if (rflg) {
		int home_owned = is_owner (user_id, user_home);
		if (-1 == home_owned) {
			fprintf (stderr,
			         _("%s: %s home directory (%s) not found\n"),
			         Prog, user_name, user_home);
			rflg = 0;
		} else if ((0 == home_owned) && !fflg) {
			fprintf (stderr,
			         _("%s: %s not owned by %s, not removing\n"),
			         Prog, user_home, user_name);
			rflg = 0;
			errors++;
			/* continue */
		}
	}

#ifdef EXTRA_CHECK_HOME_DIR
	/* This may be slow, the above should be good enough. */
	if (rflg && !fflg) {
		struct passwd *pwd;
		/*
		 * For safety, refuse to remove the home directory if it
		 * would result in removing some other user's home
		 * directory. Still not perfect so be careful, but should
		 * prevent accidents if someone has /home or / as home
		 * directory...  --marekm
		 */
		setpwent ();
		while ((pwd = getpwent ())) {
			if (strcmp (pwd->pw_name, user_name) == 0) {
				continue;
			}
			if (path_prefix (user_home, pwd->pw_dir)) {
				fprintf (stderr,
				         _("%s: not removing directory %s (would remove home of user %s)\n"),
				         Prog, user_home, pwd->pw_name);
				rflg = false;
				errors++;
				/* continue */
				break;
			}
		}
		endpwent ();
	}
#endif				/* EXTRA_CHECK_HOME_DIR */

	if (rflg) {
		if (remove_tree (user_home, true) != 0) {
			fprintf (stderr,
			         _("%s: error removing directory %s\n"),
			         Prog, user_home);
			errors++;
			/* continue */
		}
#ifdef WITH_AUDIT
		else
		{
			audit_logger (AUDIT_DEL_USER, Prog,
			              "deleting home directory",
			              user_name, (unsigned int) user_id,
			              SHADOW_AUDIT_SUCCESS);
		}
#endif				/* WITH_AUDIT */
	}
#ifdef WITH_AUDIT
	if (0 != errors) {
		audit_logger (AUDIT_DEL_USER, Prog,
		              "deleting home directory",
		              user_name, AUDIT_NO_ID,
		              SHADOW_AUDIT_FAILURE);
	}
#endif				/* WITH_AUDIT */

#ifdef WITH_SELINUX
	if (Zflg) {
		if (del_seuser (user_name) != 0) {
			fprintf (stderr,
			         _("%s: warning: the user name %s to SELinux user mapping removal failed.\n"),
			         Prog, user_name);
#ifdef WITH_AUDIT
			audit_logger (AUDIT_ADD_USER, Prog,
			              "removing SELinux user mapping",
			              user_name, (unsigned int) user_id,
			              SHADOW_AUDIT_FAILURE);
#endif				/* WITH_AUDIT */
			fail_exit (E_SE_UPDATE);
		}
	}
#endif				/* WITH_SELINUX */

	/*
	 * Cancel any crontabs or at jobs. Have to do this before we remove
	 * the entry from /etc/passwd.
	 */
	user_cancel (user_name);
	close_files ();

#ifdef WITH_TCB
	errors += remove_tcbdir (user_name, user_id);
#endif				/* WITH_TCB */

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

	return ((0 != errors) ? E_HOMEDIR : E_SUCCESS);
}