]> granicus.if.org Git - shadow/commitdiff
* New function: process_flags() split out of main().
authornekral-guest <nekral-guest@5a98b0ae-9ef6-0310-add3-de5d479b70d7>
Mon, 31 Dec 2007 13:43:04 +0000 (13:43 +0000)
committernekral-guest <nekral-guest@5a98b0ae-9ef6-0310-add3-de5d479b70d7>
Mon, 31 Dec 2007 13:43:04 +0000 (13:43 +0000)
  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.

ChangeLog
src/chfn.c

index 7278a44e6690a2ca53fea8f69e8f598a14266e7f..dd8ace44aea31debf57c9fe1802000492bd8494e 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,12 @@
+2007-12-31  Nicolas François  <nicolas.francois@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@centraliens.net>
 
        * src/newusers.c: Compilation fix for PAM support (pamh needs to be
index 6811806510635619c11a71906c9048e993bad3f4..da167f6b3f4932f0d0212bb0b093a04e40a23afe 100644 (file)
@@ -63,6 +63,15 @@ static char workph[BUFSIZ];
 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 void usage (void);
 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,59 +210,13 @@ static char *copy_field (char *in, char *out, char *extra)
 }
 
 /*
- * 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 (*)
+ * process_flags - parse the command line options
  *
- *     (*) 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
@@ -309,52 +276,23 @@ int main (int argc, char **argv)
                        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 @@ int main (int argc, char **argv)
 #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 @@ int main (int argc, char **argv)
                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 @@ int main (int argc, char **argv)
 
                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 @@ int main (int argc, char **argv)
                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 (!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);
+       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
        /*
-        * 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"));
-               closelog ();
-               exit (E_NOPERM);
-       }
-       if (!pw_unlock ()) {
-               fprintf (stderr, _("Cannot unlock the password file.\n"));
-               SYSLOG ((LOG_ERR, "can't unlock /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);
        }
+       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 ();