home *** CD-ROM | disk | FTP | other *** search
/ Source Code 1992 March / Source_Code_CD-ROM_Walnut_Creek_March_1992.iso / usenet / altsrcs / 3 / 3340 / chfn.c next >
Encoding:
C/C++ Source or Header  |  1991-05-17  |  11.3 KB  |  505 lines

  1. /*
  2.  * Copyright 1989, 1990, John F. Haugh II
  3.  * All rights reserved.
  4.  *
  5.  * Permission is granted to copy and create derivative works for any
  6.  * non-commercial purpose, provided this copyright notice is preserved
  7.  * in all copies of source code, or included in human readable form
  8.  * and conspicuously displayed on all copies of object code or
  9.  * distribution media.
  10.  */
  11.  
  12. #include <sys/types.h>
  13. #include <syslog.h>
  14. #include <stdio.h>
  15. #include <fcntl.h>
  16. #include <signal.h>
  17.  
  18. #ifndef    lint
  19. static    char    sccsid[] = "@(#)chfn.c    3.4    11:23:40    12/19/90";
  20. #endif
  21.  
  22. /*
  23.  * Set up some BSD defines so that all the BSD ifdef's are
  24.  * kept right here 
  25.  */
  26.  
  27. #ifndef    BSD
  28. #include <string.h>
  29. #include <memory.h>
  30. #else
  31. #include <strings.h>
  32. #define    strchr    index
  33. #define    strrchr    rindex
  34. #endif
  35.  
  36. #include "config.h"
  37. #include "pwd.h"
  38.  
  39. /*
  40.  * Global variables.
  41.  */
  42.  
  43. char    *Progname;
  44. char    user[BUFSIZ];
  45. char    fullnm[BUFSIZ];
  46. char    roomno[BUFSIZ];
  47. char    workph[BUFSIZ];
  48. char    homeph[BUFSIZ];
  49. char    slop[BUFSIZ];
  50. int    amroot;
  51.  
  52. /*
  53.  * External identifiers
  54.  */
  55.  
  56. extern    int    optind;
  57. extern    char    *optarg;
  58. extern    struct    passwd    *getpwuid ();
  59. extern    struct    passwd    *getpwnam ();
  60. extern    char    *getlogin ();
  61. #ifdef    NDBM
  62. extern    int    pw_dbm_mode;
  63. #endif
  64.  
  65. /*
  66.  * #defines for messages.  This facilities foreign language conversion
  67.  * since all messages are defined right here.
  68.  */
  69.  
  70. #define    USAGE \
  71. "Usage: %s [ -f full_name ] [ -r room_no ] [ -w work_ph ] [ -h home_ph ]\n"
  72. #define    ADMUSAGE \
  73. "Usage: %s [ -f full_name ] [ -r room_no ] [ -w work_ph ]\n\
  74.        [ -h home_ph ] [ -o other ] [ user ]\n"
  75. #define    NOPERM        "%s: Permission denied.\n"
  76. #define    WHOAREYOU    "%s: Cannot determine you user name.\n"
  77. #define    INVALID_NAME    "%s: invalid name: \"%s\"\n"
  78. #define    INVALID_ROOM    "%s: invalid room number: \"%s\"\n"
  79. #define    INVALID_WORKPH    "%s: invalid work phone: \"%s\"\n"
  80. #define    INVALID_HOMEPH    "%s: invalid home phone: \"%s\"\n"
  81. #define    INVALID_OTHER    "%s: \"%s\" contains illegal characters\n"
  82. #define    INVALID_FIELDS    "%s: fields too long\n"
  83. #define    NEWFIELDSMSG    "Changing the user information for %s\n"
  84. #define    NEWFIELDSMSG2 \
  85. "Enter the new value, or press return for the default\n\n"
  86. #define    NEWNAME        "Full Name"
  87. #define    NEWROOM        "Room Number"
  88. #define    NEWWORKPHONE    "Work Phone"
  89. #define    NEWHOMEPHONE    "Home Phone"
  90. #define    NEWSLOP        "Other"
  91. #define    UNKUSER        "%s: Unknown user %s\n"
  92. #define    PWDBUSY        "Cannot lock the password file; try again later.\n"
  93. #define    PWDBUSY2    "can't lock /etc/passwd\n"
  94. #define    OPNERROR    "Cannot open the password file.\n"
  95. #define    OPNERROR2    "can't open /etc/passwd\n"
  96. #define    UPDERROR    "Error updating the password entry.\n"
  97. #define    UPDERROR2    "error updating passwd entry\n"
  98. #define    DBMERROR    "Error updating the DBM password entry.\n"
  99. #define    DBMERROR2    "error updating DBM passwd entry.\n"
  100. #define    NOTROOT        "Cannot change ID to root.\n"
  101. #define    NOTROOT2    "can't setuid(0).\n"
  102. #define    CLSERROR    "Cannot commit password file changes.\n"
  103. #define    CLSERROR2    "can't rewrite /etc/passwd.\n"
  104. #define    UNLKERROR    "Cannot unlock the password file.\n"
  105. #define    UNLKERROR2    "can't unlock /etc/passwd.\n"
  106. #define    CHGGECOS    "changed user `%s' information.\n"
  107.  
  108. /*
  109.  * usage - print command line syntax and exit
  110.  */
  111.  
  112. void
  113. usage ()
  114. {
  115.     fprintf (stderr, amroot ? USAGE:ADMUSAGE, Progname);
  116.     exit (1);
  117. }
  118.  
  119. /*
  120.  * new_fields - change the user's GECOS information interactively
  121.  *
  122.  * prompt the user for each of the four fields and fill in the fields
  123.  * from the user's response, or leave alone if nothing was entered.
  124.  */
  125.  
  126. new_fields ()
  127. {
  128.     printf (NEWFIELDSMSG2);
  129.  
  130.     change_field (fullnm, NEWNAME);
  131.     change_field (roomno, NEWROOM);
  132.     change_field (workph, NEWWORKPHONE);
  133.     change_field (homeph, NEWHOMEPHONE);
  134.  
  135.     if (amroot)
  136.         change_field (slop, NEWSLOP);
  137. }
  138.  
  139. /*
  140.  * copy_field - get the next field from the gecos field
  141.  *
  142.  * copy_field copies the next field from the gecos field, returning a
  143.  * pointer to the field which follows, or NULL if there are no more
  144.  * fields.
  145.  */
  146.  
  147. char *
  148. copy_field (in, out, extra)
  149. char    *in;            /* the current GECOS field */
  150. char    *out;            /* where to copy the field to */
  151. char    *extra;            /* fields with '=' get copied here */
  152. {
  153.     char    *cp;
  154.  
  155.     while (in) {
  156.         if (cp = strchr (in, ','))
  157.             *cp++ = '\0';
  158.  
  159.         if (! strchr (in, '='))
  160.             break;
  161.  
  162.         if (extra) {
  163.             if (extra[0])
  164.                 strcat (extra, ",");
  165.  
  166.             strcat (extra, in);
  167.         }
  168.         in = cp;
  169.     }
  170.     if (in && out)
  171.         strcpy (out, in);
  172.  
  173.     return cp;
  174. }
  175.  
  176. /*
  177.  * chfn - change a user's password file information
  178.  *
  179.  *    This command controls the GECOS field information in the
  180.  *    password file entry.
  181.  *
  182.  *    The valid options are
  183.  *
  184.  *    -f    full name
  185.  *    -r    room number
  186.  *    -w    work phone number
  187.  *    -h    home phone number
  188.  *    -o    other information (*)
  189.  *
  190.  *    (*) requires root permission to execute.
  191.  */
  192.  
  193. int
  194. main (argc, argv)
  195. int    argc;
  196. char    **argv;
  197. {
  198.     char    *cp;            /* temporary character pointer       */
  199.     struct    passwd    *pw;        /* password file entry               */
  200.     struct    passwd    pwent;        /* modified password file entry      */
  201.     char    old_gecos[BUFSIZ];    /* buffer for old GECOS fields       */
  202.     char    new_gecos[BUFSIZ];    /* buffer for new GECOS fields       */
  203.     int    flag;            /* flag currently being processed    */
  204.     int    fflg = 0;        /* -f - set full name                */
  205.     int    rflg = 0;        /* -r - set room number              */
  206.     int    wflg = 0;        /* -w - set work phone number        */
  207.     int    hflg = 0;        /* -h - set home phone number        */
  208.     int    oflg = 0;        /* -o - set other information        */
  209.     int    i;            /* loop control variable             */
  210.  
  211.     /*
  212.      * This command behaves different for root and non-root
  213.      * users.
  214.      */
  215.  
  216.     amroot = getuid () == 0;
  217. #ifdef    NDBM
  218.     pw_dbm_mode = O_RDWR;
  219. #endif
  220.  
  221.     /*
  222.      * Get the program name.  The program name is used as a
  223.      * prefix to most error messages.  It is also used as input
  224.      * to the openlog() function for error logging.
  225.      */
  226.  
  227.     if (Progname = strrchr (argv[0], '/'))
  228.         Progname++;
  229.     else
  230.         Progname = argv[0];
  231.  
  232.     openlog (Progname, LOG_PID, LOG_AUTH);
  233.  
  234.     /* 
  235.      * The remaining arguments will be processed one by one and
  236.      * executed by this command.  The name is the last argument
  237.      * if it does not begin with a "-", otherwise the name is
  238.      * determined from the environment and must agree with the
  239.      * real UID.  Also, the UID will be checked for any commands
  240.      * which are restricted to root only.
  241.      */
  242.  
  243.     while ((flag = getopt (argc, argv, "f:r:w:h:o:")) != EOF) {
  244.         switch (flag) {
  245.             case 'f':
  246.                 fflg++;
  247.                 strcpy (fullnm, optarg);
  248.                 break;
  249.             case 'r':
  250.                 rflg++;
  251.                 strcpy (roomno, optarg);
  252.                 break;
  253.             case 'w':
  254.                 wflg++;
  255.                 strcpy (workph, optarg);
  256.                 break;
  257.             case 'h':
  258.                 hflg++;
  259.                 strcpy (homeph, optarg);
  260.                 break;
  261.             case 'o':
  262.                 if (amroot) {
  263.                     oflg++;
  264.                     strcpy (slop, optarg);
  265.                     break;
  266.                 }
  267.                 fprintf (stderr, NOPERM, Progname);
  268.                 exit (1);
  269.             default:
  270.                 usage ();
  271.         }
  272.     }
  273.  
  274.     /*
  275.      * Get the name of the user to check.  It is either
  276.      * the command line name, or the name getlogin()
  277.      * returns.
  278.      */
  279.  
  280.     if (optind < argc) {
  281.         strncpy (user, argv[optind], sizeof user);
  282.         pw = getpwnam (user);
  283.     } else if (cp = getlogin ()) {
  284.         strncpy (user, cp, sizeof user);
  285.         pw = getpwnam (user);
  286.     } else {
  287.         fprintf (stderr, WHOAREYOU, Progname);
  288.         exit (1);
  289.     }
  290.  
  291.     /*
  292.      * Make certain there was a password entry for the
  293.      * user.
  294.      */
  295.  
  296.     if (! pw) {
  297.         fprintf (stderr, UNKUSER, Progname, user);
  298.         exit (1);
  299.     }
  300.  
  301.     /*
  302.      * Non-privileged users are only allowed to change the
  303.      * shell if the UID of the user matches the current
  304.      * real UID.
  305.      */
  306.  
  307.     if (! amroot && pw->pw_uid != getuid ()) {
  308.         fprintf (stderr, NOPERM, Progname);
  309.         exit (1);
  310.     }
  311.  
  312.     /*
  313.      * Make a copy of the user's password file entry so it
  314.      * can be modified without worrying about it be modified
  315.      * elsewhere.
  316.      */
  317.  
  318.     pwent = *pw;
  319.     pwent.pw_name = strdup (pw->pw_name);
  320.     pwent.pw_passwd = strdup (pw->pw_passwd);
  321. #ifdef    ATT_AGE
  322.     pwent.pw_age = strdup (pw->pw_age);
  323. #endif
  324. #ifdef    ATT_COMMENT
  325.     pwent.pw_comment = strdup (pw->pw_comment);
  326. #endif
  327.     pwent.pw_dir = strdup (pw->pw_dir);
  328.     pwent.pw_shell = strdup (pw->pw_shell);
  329.  
  330.     /*
  331.      * Now get the full name.  It is the first comma separated field
  332.      * in the GECOS field.
  333.      */
  334.  
  335.     strcpy (old_gecos, pw->pw_gecos);
  336.     cp = copy_field (old_gecos, fflg ? (char *) 0:fullnm, slop);
  337.  
  338.     /*
  339.      * Now get the room number.  It is the next comma separated field,
  340.      * if there is indeed one.
  341.      */
  342.  
  343.     if (cp)
  344.         cp = copy_field (cp, rflg ? (char *) 0:roomno, slop);
  345.  
  346.     /*
  347.      * Now get the work phone number.  It is the third field.
  348.      */
  349.  
  350.     if (cp)
  351.         cp = copy_field (cp, wflg ? (char *) 0:workph, slop);
  352.  
  353.     /*
  354.      * Now get the home phone number.  It is the fourth field.
  355.      */
  356.  
  357.     if (cp)
  358.         cp = copy_field (cp, hflg ? (char *) 0:homeph, slop);
  359.  
  360.     /*
  361.      * Anything left over is "slop".
  362.      */
  363.  
  364.     if (cp) {
  365.         if (slop[0])
  366.             strcat (slop, ",");
  367.  
  368.         strcat (slop, cp);
  369.     }
  370.  
  371.     /*
  372.      * If none of the fields were changed from the command line,
  373.      * let the user interactively change them.
  374.      */
  375.  
  376.     if (! fflg && ! rflg && ! wflg && ! hflg && ! oflg) {
  377.         printf (NEWFIELDSMSG, user);
  378.         new_fields ();
  379.     }
  380.  
  381.     /*
  382.      * Check all of the fields for valid information
  383.      */
  384.  
  385.     if (valid_field (fullnm, ":,=")) {
  386.         fprintf (stderr, INVALID_NAME, Progname, fullnm);
  387.         exit (1);
  388.     }
  389.     if (valid_field (roomno, ":,=")) {
  390.         fprintf (stderr, INVALID_ROOM, Progname, roomno);
  391.         exit (1);
  392.     }
  393.     if (valid_field (workph, ":,=")) {
  394.         fprintf (stderr, INVALID_WORKPH, Progname, workph);
  395.         exit (1);
  396.     }
  397.     if (valid_field (homeph, ":,=")) {
  398.         fprintf (stderr, INVALID_HOMEPH, Progname, homeph);
  399.         exit (1);
  400.     }
  401.     if (valid_field (slop, ":")) {
  402.         fprintf (stderr, INVALID_OTHER, Progname, slop);
  403.         exit (1);
  404.     }
  405.  
  406.     /*
  407.      * Build the new GECOS field by plastering all the pieces together,
  408.      * if they will fit ...
  409.      */
  410.  
  411.     if (strlen (fullnm) + strlen (roomno) + strlen (workph) +
  412.             strlen (homeph) + strlen (slop) > 80) {
  413.         fprintf (stderr, INVALID_FIELDS, Progname);
  414.         exit (1);
  415.     }
  416.     sprintf (new_gecos, "%s,%s,%s,%s", fullnm, roomno, workph, homeph);
  417.     if (slop[0]) {
  418.         strcat (new_gecos, ",");
  419.         strcat (new_gecos, slop);
  420.     }
  421.     pwent.pw_gecos = new_gecos;
  422.     pw = &pwent;
  423.  
  424.     /*
  425.      * Before going any further, raise the ulimit to prevent
  426.      * colliding into a lowered ulimit, and set the real UID
  427.      * to root to protect against unexpected signals.  Any
  428.      * keyboard signals are set to be ignored.
  429.      */
  430.  
  431.     ulimit (2, 30000);
  432.     if (setuid (0)) {
  433.         fprintf (stderr, NOTROOT);
  434.         syslog (LOG_ERR, NOTROOT2);
  435.         exit (1);
  436.     }
  437.     signal (SIGHUP, SIG_IGN);
  438.     signal (SIGINT, SIG_IGN);
  439.     signal (SIGQUIT, SIG_IGN);
  440. #ifdef    SIGTSTP
  441.     signal (SIGTSTP, SIG_IGN);
  442. #endif
  443.  
  444.     /*
  445.      * The passwd entry is now ready to be committed back to
  446.      * the password file.  Get a lock on the file and open it.
  447.      */
  448.  
  449.     for (i = 0;i < 30;i++)
  450.         if (pw_lock ())
  451.             break;
  452.  
  453.     if (i == 30) {
  454.         fprintf (stderr, PWDBUSY);
  455.         syslog (LOG_WARN, PWDBUSY2);
  456.         exit (1);
  457.     }
  458.     if (! pw_open (O_RDWR)) {
  459.         fprintf (stderr, OPNERROR);
  460.         syslog (LOG_ERR, OPNERROR2);
  461.         (void) pw_unlock ();
  462.         exit (1);
  463.     }
  464.  
  465.     /*
  466.      * Update the passwd file entry.  If there is a DBM file,
  467.      * update that entry as well.
  468.      */
  469.  
  470.     if (! pw_update (pw)) {
  471.         fprintf (stderr, UPDERROR);
  472.         syslog (LOG_ERR, UPDERROR2);
  473.         (void) pw_unlock ();
  474.         exit (1);
  475.     }
  476. #if defined(DBM) || defined(NDBM)
  477.     if (access ("/etc/passwd.pag", 0) == 0 && ! pw_dbm_update (pw)) {
  478.         fprintf (stderr, DBMERROR);
  479.         syslog (LOG_ERR, DBMERROR2);
  480.         (void) pw_unlock ();
  481.         exit (1);
  482.     }
  483. #endif
  484.  
  485.     /*
  486.      * Changes have all been made, so commit them and unlock the
  487.      * file.
  488.      */
  489.  
  490.     if (! pw_close ()) {
  491.         fprintf (stderr, CLSERROR);
  492.         syslog (LOG_ERR, CLSERROR2);
  493.         (void) pw_unlock ();
  494.         exit (1);
  495.     }
  496.     if (! pw_unlock ()) {
  497.         fprintf (stderr, UNLKERROR);
  498.         syslog (LOG_ERR, UNLKERROR2);
  499.         exit (1);
  500.     }
  501.     syslog (LOG_INFO, CHGGECOS, user);
  502.     closelog ();
  503.     exit (0);
  504. }
  505.