[Pkg-shadow-commits] r1568 - in upstream/trunk: . src

nekral-guest at alioth.debian.org nekral-guest at alioth.debian.org
Mon Dec 31 13:43:05 UTC 2007


Author: nekral-guest
Date: 2007-12-31 13:43:04 +0000 (Mon, 31 Dec 2007)
New Revision: 1568

Modified:
   upstream/trunk/ChangeLog
   upstream/trunk/src/chfn.c
Log:
* New function: process_flags() split out of main().
  The flags variables are now global.
* New functions: check_perms(), update_gecos(),
  get_old_fields(), and check_fields() split out of main().
* Before pam_end(), the return value of the previous
  pam API was already checked. No need to validate it again.


Modified: upstream/trunk/ChangeLog
===================================================================
--- upstream/trunk/ChangeLog	2007-12-31 04:57:54 UTC (rev 1567)
+++ upstream/trunk/ChangeLog	2007-12-31 13:43:04 UTC (rev 1568)
@@ -1,5 +1,14 @@
 2007-12-31  Nicolas François  <nicolas.francois at centraliens.net>
 
+	* src/chfn.c: New function: process_flags() split out of main().
+	The flags variables are now global.
+	* src/chfn.c: New functions: check_perms(), update_gecos(),
+	get_old_fields(), and check_fields() split out of main().
+	* src/chfn.c: Before pam_end(), the return value of the previous
+	pam API was already checked. No need to validate it again.
+
+2007-12-31  Nicolas François  <nicolas.francois at centraliens.net>
+
 	* src/newusers.c: Compilation fix for PAM support (pamh needs to be
 	global since the function split).
 	* src/chpasswd.c: Likewise.

Modified: upstream/trunk/src/chfn.c
===================================================================
--- upstream/trunk/src/chfn.c	2007-12-31 04:57:54 UTC (rev 1567)
+++ upstream/trunk/src/chfn.c	2007-12-31 13:43:04 UTC (rev 1568)
@@ -63,6 +63,15 @@
 static char homeph[BUFSIZ];
 static char slop[BUFSIZ];
 static int amroot;
+/* Flags */
+static int fflg = 0;		/* -f - set full name                */
+static int rflg = 0;		/* -r - set room number              */
+static int wflg = 0;		/* -w - set work phone number        */
+static int hflg = 0;		/* -h - set home phone number        */
+static int oflg = 0;		/* -o - set other information        */
+#ifdef USE_PAM
+static pam_handle_t *pamh = NULL;
+#endif
 
 /*
  * External identifiers
@@ -73,6 +82,10 @@
 static int may_change_field (int);
 static void new_fields (void);
 static char *copy_field (char *, char *, char *);
+static void process_flags (int argc, char **argv);
+static void check_perms (const struct passwd *pw);
+static void update_gecos (const char *user, char *gecos);
+static void get_old_fields (const char *gecos);
 
 /*
  * usage - print command line syntax and exit
@@ -197,60 +210,14 @@
 }
 
 /*
- * chfn - change a user's password file information
+ * process_flags - parse the command line options
  *
- *	This command controls the GECOS field information in the password
- *	file entry.
- *
- *	The valid options are
- *
- *	-f	full name
- *	-r	room number
- *	-w	work phone number
- *	-h	home phone number
- *	-o	other information (*)
- *
- *	(*) requires root permission to execute.
+ *	It will not return if an error is encountered.
  */
-int main (int argc, char **argv)
+static void process_flags (int argc, char **argv)
 {
-	char *cp;		/* temporary character pointer       */
-	const struct passwd *pw;	/* password file entry               */
-	struct passwd pwent;	/* modified password file entry      */
-	char old_gecos[BUFSIZ];	/* buffer for old GECOS fields       */
-	char new_gecos[BUFSIZ];	/* buffer for new GECOS fields       */
 	int flag;		/* flag currently being processed    */
-	int fflg = 0;		/* -f - set full name                */
-	int rflg = 0;		/* -r - set room number              */
-	int wflg = 0;		/* -w - set work phone number        */
-	int hflg = 0;		/* -h - set home phone number        */
-	int oflg = 0;		/* -o - set other information        */
-	char *user;
 
-#ifdef USE_PAM
-	pam_handle_t *pamh = NULL;
-	int retval;
-#endif
-
-	sanitize_env ();
-	setlocale (LC_ALL, "");
-	bindtextdomain (PACKAGE, LOCALEDIR);
-	textdomain (PACKAGE);
-
-	/*
-	 * This command behaves different for root and non-root
-	 * users.
-	 */
-	amroot = (getuid () == 0);
-
-	/*
-	 * Get the program name. The program name is used as a
-	 * prefix to most error messages.
-	 */
-	Prog = Basename (argv[0]);
-
-	OPENLOG ("chfn");
-
 	/* 
 	 * The remaining arguments will be processed one by one and executed
 	 * by this command. The name is the last argument if it does not
@@ -309,52 +276,23 @@
 			usage ();
 		}
 	}
+}
 
-	/*
-	 * Get the name of the user to check. It is either the command line
-	 * name, or the name getlogin() returns.
-	 */
-	if (optind < argc) {
-		user = argv[optind];
-		pw = xgetpwnam (user);
-		if (!pw) {
-			fprintf (stderr, _("%s: unknown user %s\n"), Prog,
-				 user);
-			exit (E_NOPERM);
-		}
-	} else {
-		pw = get_my_pwent ();
-		if (!pw) {
-			fprintf (stderr,
-				 _
-				 ("%s: Cannot determine your user name.\n"),
-				 Prog);
-			exit (E_NOPERM);
-		}
-		user = xstrdup (pw->pw_name);
-	}
-
-#ifdef	USE_NIS
-	/*
-	 * Now we make sure this is a LOCAL password entry for this user ...
-	 */
-	if (__ispwNIS ()) {
-		char *nis_domain;
-		char *nis_master;
-
-		fprintf (stderr,
-			 _("%s: cannot change user '%s' on NIS client.\n"),
-			 Prog, user);
-
-		if (!yp_get_default_domain (&nis_domain) &&
-		    !yp_master (nis_domain, "passwd.byname", &nis_master)) {
-			fprintf (stderr,
-				 _
-				 ("%s: '%s' is the NIS master for this client.\n"),
-				 Prog, nis_master);
-		}
-		exit (E_NOPERM);
-	}
+/*
+ * check_perms - check if the caller is allowed to add a group
+ *
+ *	Non-root users are only allowed to change their gecos field.
+ *	(see also may_change_field())
+ *
+ *	Non-root users must be authenticated.
+ *
+ *	It will not return if the user is not allowed.
+ */
+static void check_perms (const struct passwd *pw)
+{
+#ifdef USE_PAM
+	int retval;
+	struct passwd *pampw;
 #endif
 
 	/*
@@ -393,17 +331,13 @@
 #else				/* !USE_PAM */
 	retval = PAM_SUCCESS;
 
-	{
-		struct passwd *pampw;
-		pampw = getpwuid (getuid ()); /* local, no need for xgetpwuid */
-		if (pampw == NULL) {
-			retval = PAM_USER_UNKNOWN;
-		}
+	pampw = getpwuid (getuid ()); /* local, no need for xgetpwuid */
+	if (pampw == NULL) {
+		retval = PAM_USER_UNKNOWN;
+	}
 
-		if (retval == PAM_SUCCESS) {
-			retval = pam_start ("chfn", pampw->pw_name,
-					    &conv, &pamh);
-		}
+	if (retval == PAM_SUCCESS) {
+		retval = pam_start ("chfn", pampw->pw_name, &conv, &pamh);
 	}
 
 	if (retval == PAM_SUCCESS) {
@@ -425,12 +359,117 @@
 		exit (E_NOPERM);
 	}
 #endif				/* USE_PAM */
+}
 
+/*
+ * update_gecos - update the gecos fields in the password database
+ *
+ *	Commit the user's entry after changing her gecos field.
+ */
+static void update_gecos (const char *user, char *gecos)
+{
+	const struct passwd *pw;	/* The user's password file entry */
+	struct passwd pwent;		/* modified password file entry */
+
 	/*
+	 * 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.
+	 */
+	if (setuid (0)) {
+		fprintf (stderr, _("Cannot change ID to root.\n"));
+		SYSLOG ((LOG_ERR, "can't setuid(0)"));
+		closelog ();
+		exit (E_NOPERM);
+	}
+	pwd_init ();
+
+	/*
+	 * The passwd entry is now ready to be committed back to the
+	 * password file. Get a lock on the file and open it.
+	 */
+	if (!pw_lock ()) {
+		fprintf (stderr,
+			 _
+			 ("Cannot lock the password file; try again later.\n"));
+		SYSLOG ((LOG_WARN, "can't lock /etc/passwd"));
+		closelog ();
+		exit (E_NOPERM);
+	}
+	if (!pw_open (O_RDWR)) {
+		fprintf (stderr, _("Cannot open the password file.\n"));
+		pw_unlock ();
+		SYSLOG ((LOG_ERR, "can't open /etc/passwd"));
+		closelog ();
+		exit (E_NOPERM);
+	}
+
+	/*
+	 * Get the entry to update using pw_locate() - we want the real one
+	 * from /etc/passwd, not the one from getpwnam() which could contain
+	 * the shadow password if (despite the warnings) someone enables
+	 * AUTOSHADOW (or SHADOW_COMPAT in libc).  --marekm
+	 */
+	pw = pw_locate (user);
+	if (!pw) {
+		pw_unlock ();
+		fprintf (stderr,
+			 _("%s: %s not found in /etc/passwd\n"), Prog, user);
+		exit (E_NOPERM);
+	}
+
+	/*
+	 * Make a copy of the entry, then change the gecos field. The other
+	 * fields remain unchanged.
+	 */
+	pwent = *pw;
+	pwent.pw_gecos = gecos;
+
+	/*
+	 * Update the passwd file entry. If there is a DBM file, update that
+	 * entry as well.
+	 */
+	if (!pw_update (&pwent)) {
+		fprintf (stderr, _("Error updating the password entry.\n"));
+		pw_unlock ();
+		SYSLOG ((LOG_ERR, "error updating passwd entry"));
+		closelog ();
+		exit (E_NOPERM);
+	}
+
+	/*
+	 * Changes have all been made, so commit them and unlock the file.
+	 */
+	if (!pw_close ()) {
+		fprintf (stderr, _("Cannot commit password file changes.\n"));
+		pw_unlock ();
+		SYSLOG ((LOG_ERR, "can't rewrite /etc/passwd"));
+		closelog ();
+		exit (E_NOPERM);
+	}
+	if (!pw_unlock ()) {
+		fprintf (stderr, _("Cannot unlock the password file.\n"));
+		SYSLOG ((LOG_ERR, "can't unlock /etc/passwd"));
+		closelog ();
+		exit (E_NOPERM);
+	}
+}
+
+/*
+ * get_old_fields - parse the old gecos and use the old value for the fields
+ *                  which are not set on the command line
+ */
+static void get_old_fields (const char *gecos)
+{
+	char *cp;		/* temporary character pointer       */
+	char old_gecos[BUFSIZ];	/* buffer for old GECOS fields       */
+	STRFCPY (old_gecos, gecos);
+
+	/*
 	 * Now get the full name. It is the first comma separated field in
 	 * the GECOS field.
 	 */
-	STRFCPY (old_gecos, pw->pw_gecos);
 	cp = copy_field (old_gecos, fflg ? (char *) 0 : fullnm, slop);
 
 	/*
@@ -461,19 +500,15 @@
 
 		strcat (slop, cp);
 	}
+}
 
-	/*
-	 * If none of the fields were changed from the command line, let the
-	 * user interactively change them.
-	 */
-	if (!fflg && !rflg && !wflg && !hflg && !oflg) {
-		printf (_("Changing the user information for %s\n"), user);
-		new_fields ();
-	}
-
-	/*
-	 * Check all of the fields for valid information
-	 */
+/*
+ * check_fields - check all of the fields for valid information
+ *
+ *	It will not return if a field is not valid.
+ */
+static void check_fields (void)
+{
 	if (valid_field (fullnm, ":,=")) {
 		fprintf (stderr, _("%s: invalid name: '%s'\n"), Prog, fullnm);
 		closelog ();
@@ -504,110 +539,143 @@
 		closelog ();
 		exit (E_NOPERM);
 	}
+}
 
+/*
+ * chfn - change a user's password file information
+ *
+ *	This command controls the GECOS field information in the password
+ *	file entry.
+ *
+ *	The valid options are
+ *
+ *	-f	full name
+ *	-r	room number
+ *	-w	work phone number
+ *	-h	home phone number
+ *	-o	other information (*)
+ *
+ *	(*) requires root permission to execute.
+ */
+int main (int argc, char **argv)
+{
+	const struct passwd *pw;	/* password file entry               */
+	char new_gecos[BUFSIZ];	/* buffer for new GECOS fields       */
+	char *user;
+
+	sanitize_env ();
+	setlocale (LC_ALL, "");
+	bindtextdomain (PACKAGE, LOCALEDIR);
+	textdomain (PACKAGE);
+
 	/*
-	 * Build the new GECOS field by plastering all the pieces together,
-	 * if they will fit ...
+	 * This command behaves different for root and non-root
+	 * users.
 	 */
-	if (strlen (fullnm) + strlen (roomno) + strlen (workph) +
-	    strlen (homeph) + strlen (slop) > (unsigned int) 80) {
-		fprintf (stderr, _("%s: fields too long\n"), Prog);
-		closelog ();
-		exit (E_NOPERM);
-	}
-	snprintf (new_gecos, sizeof new_gecos, "%s,%s,%s,%s%s%s",
-		  fullnm, roomno, workph, homeph, slop[0] ? "," : "", slop);
+	amroot = (getuid () == 0);
 
 	/*
-	 * 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.
+	 * Get the program name. The program name is used as a
+	 * prefix to most error messages.
 	 */
-	if (setuid (0)) {
-		fprintf (stderr, _("Cannot change ID to root.\n"));
-		SYSLOG ((LOG_ERR, "can't setuid(0)"));
-		closelog ();
-		exit (E_NOPERM);
-	}
-	pwd_init ();
+	Prog = Basename (argv[0]);
 
+	OPENLOG ("chfn");
+
+	/* parse the command line options */
+	process_flags (argc, argv);
+
 	/*
-	 * The passwd entry is now ready to be committed back to the
-	 * password file. Get a lock on the file and open it.
+	 * Get the name of the user to check. It is either the command line
+	 * name, or the name getlogin() returns.
 	 */
-	if (!pw_lock ()) {
-		fprintf (stderr,
-			 _
-			 ("Cannot lock the password file; try again later.\n"));
-		SYSLOG ((LOG_WARN, "can't lock /etc/passwd"));
-		closelog ();
-		exit (E_NOPERM);
+	if (optind < argc) {
+		user = argv[optind];
+		pw = xgetpwnam (user);
+		if (!pw) {
+			fprintf (stderr, _("%s: unknown user %s\n"), Prog,
+				 user);
+			exit (E_NOPERM);
+		}
+	} else {
+		pw = get_my_pwent ();
+		if (!pw) {
+			fprintf (stderr,
+				 _
+				 ("%s: Cannot determine your user name.\n"),
+				 Prog);
+			exit (E_NOPERM);
+		}
+		user = xstrdup (pw->pw_name);
 	}
-	if (!pw_open (O_RDWR)) {
-		fprintf (stderr, _("Cannot open the password file.\n"));
-		pw_unlock ();
-		SYSLOG ((LOG_ERR, "can't open /etc/passwd"));
-		closelog ();
-		exit (E_NOPERM);
-	}
 
+#ifdef	USE_NIS
 	/*
-	 * Get the entry to update using pw_locate() - we want the real one
-	 * from /etc/passwd, not the one from getpwnam() which could contain
-	 * the shadow password if (despite the warnings) someone enables
-	 * AUTOSHADOW (or SHADOW_COMPAT in libc).  --marekm
+	 * Now we make sure this is a LOCAL password entry for this user ...
 	 */
-	pw = pw_locate (user);
-	if (!pw) {
-		pw_unlock ();
+	if (__ispwNIS ()) {
+		char *nis_domain;
+		char *nis_master;
+
 		fprintf (stderr,
-			 _("%s: %s not found in /etc/passwd\n"), Prog, user);
+			 _("%s: cannot change user '%s' on NIS client.\n"),
+			 Prog, user);
+
+		if (!yp_get_default_domain (&nis_domain) &&
+		    !yp_master (nis_domain, "passwd.byname", &nis_master)) {
+			fprintf (stderr,
+				 _
+				 ("%s: '%s' is the NIS master for this client.\n"),
+				 Prog, nis_master);
+		}
 		exit (E_NOPERM);
 	}
+#endif
 
+	/* Check that the caller is allowed to change the gecos of the
+	 * specified user */
+	check_perms (pw);
+
+	/* If some fields were not set on the command line, load the value from
+	 * the old gecos fields. */
+	get_old_fields (pw->pw_gecos);
+
 	/*
-	 * Make a copy of the entry, then change the gecos field. The other
-	 * fields remain unchanged.
+	 * If none of the fields were changed from the command line, let the
+	 * user interactively change them.
 	 */
-	pwent = *pw;
-	pwent.pw_gecos = new_gecos;
+	if (!fflg && !rflg && !wflg && !hflg && !oflg) {
+		printf (_("Changing the user information for %s\n"), user);
+		new_fields ();
+	}
 
 	/*
-	 * Update the passwd file entry. If there is a DBM file, update that
-	 * entry as well.
+	 * Check all of the fields for valid information
 	 */
-	if (!pw_update (&pwent)) {
-		fprintf (stderr, _("Error updating the password entry.\n"));
-		pw_unlock ();
-		SYSLOG ((LOG_ERR, "error updating passwd entry"));
-		closelog ();
-		exit (E_NOPERM);
-	}
+	check_fields ();
 
 	/*
-	 * Changes have all been made, so commit them and unlock the file.
+	 * Build the new GECOS field by plastering all the pieces together,
+	 * if they will fit ...
 	 */
-	if (!pw_close ()) {
-		fprintf (stderr, _("Cannot commit password file changes.\n"));
-		pw_unlock ();
-		SYSLOG ((LOG_ERR, "can't rewrite /etc/passwd"));
+	if (strlen (fullnm) + strlen (roomno) + strlen (workph) +
+	    strlen (homeph) + strlen (slop) > (unsigned int) 80) {
+		fprintf (stderr, _("%s: fields too long\n"), Prog);
 		closelog ();
 		exit (E_NOPERM);
 	}
-	if (!pw_unlock ()) {
-		fprintf (stderr, _("Cannot unlock the password file.\n"));
-		SYSLOG ((LOG_ERR, "can't unlock /etc/passwd"));
-		closelog ();
-		exit (E_NOPERM);
-	}
+	snprintf (new_gecos, sizeof new_gecos, "%s,%s,%s,%s%s%s",
+		  fullnm, roomno, workph, homeph, slop[0] ? "," : "", slop);
+
+	/* Rewrite the user's gecos in the passwd file */
+	update_gecos (user, new_gecos);
+
 	SYSLOG ((LOG_INFO, "changed user `%s' information", user));
 
 	nscd_flush_cache ("passwd");
 
 #ifdef USE_PAM
-	if (retval == PAM_SUCCESS)
-		pam_end (pamh, PAM_SUCCESS);
+	pam_end (pamh, PAM_SUCCESS);
 #endif				/* USE_PAM */
 
 	closelog ();




More information about the Pkg-shadow-commits mailing list