home *** CD-ROM | disk | FTP | other *** search
- /*
- * Copyright 1989, 1990, John F. Haugh II
- * All rights reserved.
- *
- * Use, duplication, and disclosure prohibited without
- * the express written permission of the author.
- */
-
- #include <sys/types.h>
- #include <stdio.h>
- #include <pwd.h>
- #include <fcntl.h>
- #include <signal.h>
- #include <errno.h>
- #include <ctype.h>
- #ifndef BSD
- #include <string.h>
- #include <memory.h>
- #else
- #include <strings.h>
- #define strchr index
- #define strrchr rindex
- #endif
- #include "config.h"
-
- #ifdef DBM
- #include <dbm.h>
- #endif
-
- #ifndef lint
- static char _sccsid[] = "@(#)chfn.c 2.2 19:17:28 8/4/90";
- #endif
-
- char *myname;
-
- char user[BUFSIZ];
- char fullnm[BUFSIZ];
- char roomno[BUFSIZ];
- char workph[BUFSIZ];
- char homeph[BUFSIZ];
- char slop[BUFSIZ];
- int fflg;
- int rflg;
- int wflg;
- int hflg;
- int oflg;
-
- struct passwd pwent;
-
- extern int errno;
-
- char Usage[] =
- "Usage: %s [ -f full_name ] [ -r room_no ] [ -w work_ph ] [ -h home_ph ]\n";
-
- char Usage_root[] =
- "Usage: %s [ -f full_name ] [ -r room_no ] [ -w work_ph ]\n\
- [ -h home_ph ] [ -o other ] [ user ]\n";
-
- /*
- * usage - print command line syntax and exit
- */
-
- void
- usage ()
- {
- fprintf (stderr, getuid () == 0 ? Usage_root:Usage, myname);
- exit (1);
- }
-
- /*
- * valid_field - insure that a field contains all legal characters
- *
- * The supplied field is scanned for non-printing and other illegal
- * characters. If any illegal characters are found, valid_field
- * prints a message and exits.
- */
-
- void
- valid_field (field, illegal)
- char *field;
- char *illegal;
- {
- char *cp;
-
- for (cp = field;*cp && isprint (*cp) && ! strchr (illegal, *cp);cp++)
- ;
-
- if (*cp) {
- fprintf (stderr, "%s: invalid field: %s\n", myname, field);
- exit (1);
- }
- }
-
- /*
- * change_field - change a single field if a new value is given.
- *
- * prompt the user with the name of the field being changed and the
- * current value.
- */
-
- void
- change_field (buf, prompt)
- char *buf;
- char *prompt;
- {
- char new[BUFSIZ];
- char *cp;
-
- printf ("\t%s [%s]: ", prompt, buf);
- fgets (new, BUFSIZ, stdin);
-
- if (cp = strchr (new, '\n'))
- *cp = '\0';
- else
- return;
-
- if (new[0])
- strcpy (buf, new);
- }
-
- /*
- * new_fields - change the user's GECOS information interactively
- *
- * prompt the user for each of the four fields and fill in the fields
- * from the user's response, or leave alone if nothing was entered.
- */
-
- new_fields ()
- {
- printf ("Enter the new value, or press return for the default\n\n");
-
- change_field (fullnm, "Full Name");
- change_field (roomno, "Room Number");
- change_field (workph, "Work Phone");
- change_field (homeph, "Home Phone");
-
- if (getuid () == 0)
- change_field (slop, "Other");
- }
-
- /*
- * copy_field - get the next field from the gecos field
- *
- * copy_field copies the next field from the gecos field, returning a
- * pointer to the field which follows, or NULL if there are no more
- * fields.
- */
-
- char *
- copy_field (in, out, extra)
- char *in; /* the current GECOS field */
- char *out; /* where to copy the field to */
- char *extra; /* fields with '=' get copied here */
- {
- char *cp;
-
- while (in) {
- if (cp = strchr (in, ','))
- *cp++ = '\0';
-
- if (! strchr (in, '='))
- break;
-
- if (extra) {
- if (extra[0])
- strcat (extra, ",");
-
- strcat (extra, in);
- }
- in = cp;
- }
- if (in && out)
- strcpy (out, in);
-
- return cp;
- }
-
- #ifdef DBM
- /*
- * update_dbm
- *
- * Updates the DBM password files, if they exist.
- */
-
- update_dbm (pw)
- struct passwd *pw;
- {
- datum key;
- datum content;
- char data[BUFSIZ];
- int len;
-
- strcpy (data, PWDFILE);
- strcat (data, ".pag");
- if (access (data, 0))
- return;
-
- len = pw_pack (pw, data);
- content.dsize = len;
- content.dptr = data;
-
- key.dsize = strlen (pw->pw_name);
- key.dptr = pw->pw_name;
- store (key, content);
-
- key.dsize = sizeof pw->pw_uid;
- key.dptr = (char *) &pw->pw_uid;
- store (key, content);
- }
- #endif
-
- int
- main (argc, argv)
- int argc;
- char **argv;
- {
- extern int optind;
- extern char *optarg;
- void die ();
- char *cp;
- char *getlogin ();
- int amroot;
- int lockfd = -1;
- int flag;
- struct passwd *pw;
- struct passwd *getpwuid ();
- struct passwd *getpwnam ();
- struct passwd *sgetpwent ();
- FILE *npwd;
- FILE *pwd;
- char buf[BUFSIZ];
- char tmp[BUFSIZ];
-
- amroot = getuid () == 0;
- if (myname = strchr (argv[0], '/'))
- myname++;
- else
- myname = argv[0];
-
- while ((flag = getopt (argc, argv, "f:r:w:h:o:")) != EOF) {
- switch (flag) {
- case 'f':
- fflg++;
- strcpy (fullnm, optarg);
- break;
- case 'r':
- rflg++;
- strcpy (roomno, optarg);
- break;
- case 'w':
- wflg++;
- strcpy (workph, optarg);
- break;
- case 'h':
- hflg++;
- strcpy (homeph, optarg);
- break;
- case 'o':
- if (getuid () == 0) {
- oflg++;
- strcpy (slop, optarg);
- break;
- }
- fprintf (stderr, "%s: permission denied\n",
- myname);
- exit (1);
- default:
- usage ();
- }
- }
- if (argc > optind) {
- if (argc > optind + 1)
- usage ();
-
- if (! (pw = getpwnam (argv[optind]))) {
- fprintf (stderr, "%s: unknown user: %s\n",
- myname, argv[optind]);
- exit (1);
- }
- } else {
- if (cp = getlogin ()) {
- if (! (pw = getpwnam (cp))) {
- fprintf (stderr, "%s: unknown user: %s\n",
- myname, cp);
- exit (1);
- }
- } else if (! (pw = getpwuid (getuid ()))) {
- fprintf (stderr, "%s: who are you?\n",
- myname);
- exit (1);
- }
- }
- if (! amroot && pw->pw_uid != getuid ()) {
- fprintf (stderr, "%s: permission denied\n",
- myname);
- exit (1);
- }
- strcpy (user, pw->pw_name);
-
- pwent = *pw;
- pwent.pw_name = strdup (pw->pw_name);
- pwent.pw_passwd = strdup (pw->pw_passwd);
- pwent.pw_dir = strdup (pw->pw_dir);
- pwent.pw_shell = strdup (pw->pw_shell);
-
- /*
- * Now get the full name. It is the first comma separated field
- * in the GECOS field.
- */
-
- strcpy (buf, pw->pw_gecos);
- cp = copy_field (buf, fflg ? (char *) 0:fullnm, slop);
-
- /*
- * Now get the room number. It is the next comma separated field,
- * if there is indeed one.
- */
-
- if (cp)
- cp = copy_field (cp, rflg ? (char *) 0:roomno, slop);
-
- /*
- * Now get the work phone number. It is the third field.
- */
-
- if (cp)
- cp = copy_field (cp, wflg ? (char *) 0:workph, slop);
-
- /*
- * Now get the home phone number. It is the fourth field.
- */
-
- if (cp)
- cp = copy_field (cp, hflg ? (char *) 0:homeph, slop);
-
- /*
- * Anything left over is "slop".
- */
-
- if (cp) {
- if (slop[0])
- strcat (slop, ",");
-
- 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
- */
-
- valid_field (fullnm, ":,=");
- valid_field (roomno, ":,=");
- valid_field (workph, ":,=");
- valid_field (homeph, ":,=");
- valid_field (slop, ":");
-
- /*
- * Build the new GECOS field by plastering all the pieces together,
- * if they will fit ...
- */
-
- if (strlen (fullnm) + strlen (roomno) + strlen (workph) +
- strlen (homeph) + strlen (slop) > 80) {
- fprintf (stderr, "%s: fields too long\n", myname);
- exit (1);
- }
- sprintf (tmp, "%s,%s,%s,%s", fullnm, roomno, workph, homeph);
- if (slop[0]) {
- strcat (tmp, ",");
- strcat (tmp, slop);
- }
- pwent.pw_gecos = tmp;
-
- /*
- * Now we get to race the bad guy. I don't think he can get us.
- *
- * Ignore most reasonable signals.
- * Maybe we should ignore more? He can't hurt us until the end.
- *
- * Get a lock file.
- *
- * Copy first part of password file to new file.
- * Illegal lines are copied verbatim.
- * File permissions are r--r--r--, owner root, group root.
- *
- * Output the new entry.
- * Only fields in struct passwd are output.
- *
- * Copy the rest of the file verbatim.
- *
- * Rename (link, unlink) password file to backup.
- * Kill me now and nothing changes or no one gets in.
- *
- * Rename (link, unlink) temporary file to password file.
- * Kill me now and no one gets in or lock is left.
- *
- * Remove locking file.
- *
- * That's all folks ...
- */
-
- signal (SIGHUP, SIG_IGN);
- signal (SIGINT, SIG_IGN);
- signal (SIGQUIT, SIG_IGN);
- signal (SIGTERM, SIG_IGN);
-
- ulimit (30000); /* prevent any funny business */
- umask (0); /* get new files modes correct */
- #ifndef NDEBUG
- if ((lockfd = open (".pwdlock", O_RDONLY|O_CREAT|O_EXCL), 0444) == -1)
- #else
- if ((lockfd = open (PWDLOCK, O_RDONLY|O_CREAT|O_EXCL), 0444) == -1)
- #endif /* NDEBUG */
- {
- puts ("Can't get lock");
- exit (1);
- }
- umask (077); /* close security holes to come ... */
-
- #ifdef DBM
- update_dbm (&pwent);
- #endif
- if (access (NPWDFILE, 0) == 0 && unlink (NPWDFILE) == -1) {
- perror (NPWDFILE);
- exit (1);
- }
- #ifndef NDEBUG
- if ((npwd = fopen ("npasswd", "w")) == (FILE *) 0)
- #else
- umask (077); /* no permissions for non-roots */
-
- if ((npwd = fopen (NPWDFILE, "w")) == (FILE *) 0)
- #endif /* NDEBUG */
- {
- perror (NPWDFILE);
- exit (1);
- }
- #ifndef NDEBUG
- chmod (NPWDFILE, 0444); /* lets have some security here ... */
- chown (NPWDFILE, 0, 0); /* ... and keep the bad guy away */
- #endif /* NDEBUG */
- if ((pwd = fopen (PWDFILE, "r")) == (FILE *) 0) {
- perror (NPWDFILE);
- exit (1);
- }
- while (fgets (buf, BUFSIZ, pwd) != (char *) 0) {
- if (buf[0] == '#' || ! (pw = sgetpwent (buf))) {
- fputs (buf, npwd);
- } else if (strcmp (pw->pw_name, pwent.pw_name) != 0)
- fputs (buf, npwd);
- else
- break;
- }
- (void) fprintf (npwd, "%s:", pw->pw_name);
- if (pwent.pw_age)
- (void) fprintf (npwd, "%s,%s:", pwent.pw_passwd, pwent.pw_age);
- else
- (void) fprintf (npwd, "%s:", pwent.pw_passwd);
-
- (void) fprintf (npwd, "%d:%d:%s:%s:%s\n",
- pwent.pw_uid, pwent.pw_gid, pwent.pw_gecos, pwent.pw_dir,
- pwent.pw_shell ? pwent.pw_shell:"");
-
- while (fgets (buf, BUFSIZ, pwd) != (char *) 0)
- fputs (buf, npwd);
-
- if (ferror (npwd)) {
- perror (NPWDFILE);
- if (unlink (NPWDFILE) || unlink (PWDLOCK))
- fputs ("Help!\n", stderr);
-
- exit (1);
- }
- fflush (npwd);
- fclose (npwd);
- #ifdef NDEBUG
- chmod (NPWDFILE, 0644);
- if (unlink (OPWDFILE) == -1) {
- if (errno != ENOENT) {
- puts ("Can't unlink backup file");
- goto unlock;
- }
- }
- if (link (PWDFILE, OPWDFILE) || unlink (PWDFILE)) {
- puts ("Can't save backup file");
- goto unlock;
- }
- #ifndef BSD
- if (link (NPWDFILE, PWDFILE) || unlink (NPWDFILE))
- #else
- if (rename (NPWDFILE, PWDFILE))
- #endif
- {
- puts ("Can't rename new file");
- goto unlock;
- }
- #endif /* NDEBUG */
- #ifndef NDEBUG
- (void) unlink (".pwdlock");
- #else
- (void) unlink (PWDLOCK);
- #endif
- exit (0);
- /*NOTREACHED*/
-
- unlock:
- if (lockfd >= 0)
- (void) unlink (PWDLOCK);
-
- (void) unlink (NPWDFILE);
- exit (1);
- /*NOTREACHED*/
- }
-