home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 January / usenetsourcesnewsgroupsinfomagicjanuary1994.iso / sources / unix / volume25 / npasswd / part03 / npasswd.c < prev    next >
Encoding:
C/C++ Source or Header  |  1991-12-19  |  14.8 KB  |  625 lines

  1.  
  2. /* --------------------------------------------------------------------  */
  3. /*                                                                       */
  4. /*                         Author: Clyde Hoover                          */
  5. /*                          Computation Center                           */
  6. /*                   The University of Texas at Austin                   */
  7. /*                          Austin, Texas 78712                          */
  8. /*                         clyde@emx.utexas.edu                          */
  9. /*                   uunet!cs.utexas.edu!ut-emx!clyde                    */
  10. /*                                                                       */
  11. /*This code may be distributed freely, provided this notice is retained. */
  12. /*                                                                       */
  13. /* --------------------------------------------------------------------  */
  14. /*
  15.  *    This program duplicates the manual page behavior of the 4.XBSD
  16.  *    passwd(1) command.  It can be configured for use with a variety
  17.  *    of passwd systems (/etc/passwd, /etc/shadow, databases).
  18.  *
  19.  *    The System V support is untested (by the author - other sites
  20.  *    tell me it works).
  21.  *
  22.  *    Here we have only the most abstract data needed (login name,
  23.  *    user id, current password, new password).
  24.  *    All other information needed (the full password line, etc),
  25.  *    is kept down in the 'method' routines.
  26.  *
  27.  *    The 'method' routines are:
  28.  *
  29.  *    pw_initalize()        Do initializations 
  30.  *    pw_getuserbyname()    Get user information by name
  31.  *    pw_permission()        Check if user has permission
  32.  *                 to change this users' password
  33.  *    pw_compare()        Compare passwords
  34.  *    pw_check()        Check password
  35.  *                 Returns 1 if ok, 0 otherwise
  36.  *    pw_replace()        Replace the password
  37.  *    pw_cleanup()        Cleanup
  38.  */
  39. #ifdef    SYSLOG
  40. #include <syslog.h>
  41. # ifndef    LOG_AUTH
  42. #  define    LOG_AUTH    0
  43. # endif
  44. # ifndef    LOG_CONS
  45. #  define    LOG_CONS    0
  46. # endif
  47. #endif
  48.  
  49. #include <ctype.h>
  50. #include <signal.h>
  51. #include <sys/types.h>
  52. #include <stdio.h>
  53. #include <pwd.h>
  54. #include <errno.h>
  55. #include "version.h"
  56.  
  57. #ifndef lint
  58. static char sccsid[] = "@(#)npasswd.c    1.16 9/24/90 (cc.utexas.edu) /usr/share/src/private/ut/share/bin/passwd/SCCS/s.npasswd.c";
  59. #endif
  60.  
  61. #ifndef    CONFIG_FILE
  62. #define    CONFIG_FILE    "/usr/adm/passwd.conf"
  63. #endif
  64. #ifndef    HELP_FILE
  65. #define    HELP_FILE    "/usr/adm/passwd.help"
  66. #endif
  67. #ifndef    MOTD_FILE
  68. #define    MOTD_FILE    "/usr/adm/passwd.motd"
  69. #endif
  70.  
  71. extern int    errno;            /* System error code */
  72.  
  73. #ifdef    sun
  74. static char    *options = "alyd:e:F:n:fPsVx:";    /* Command line options */
  75. #else
  76. static char    *options = "fPsV";    /* Command line options */
  77. #endif
  78. static int    retries = 3;        /* Retry limit */
  79. static int    from_prog = 0;        /* Data source is a program */
  80.  
  81. static char    username[16],    /* Name of user changing password */
  82.         password[16];    /* Current password (encrypted) */
  83.  
  84. char    pbuf[16],        /* Password read buffer 1 */
  85.     pbuf2[16],        /* Password read buffer 2 */
  86.     ppbuf[16],        /* Current password */
  87.     mylogin[16];        /* My login name (saved) */
  88.  
  89. char    *getpass(),
  90.     *malloc(),
  91.     *ttyname(),
  92.     *getlogin();
  93.  
  94. #ifdef    SYSV
  95. #define    index    strchr
  96. #endif
  97. char    *index();
  98.  
  99. int    catchit();        /* Signal catcher */
  100.  
  101. /*
  102.  *    passwd - change the password for a user.
  103.  *
  104.  *    This program impliments the 'passwd' command.
  105.  */
  106. main(argc, argv)
  107. int    argc;
  108. char    *argv[];
  109. {
  110.     char    *myname, mysavedname[16];/* My login name */
  111.     struct passwd *pw;        /* My passwd entry */
  112.     int    opt;            /* Option processing temp */
  113.     extern char    *optarg;    /* From getopt() */
  114.     extern int    optind;        /* From getopt() */
  115.  
  116.     /*
  117.      * Handle the 4.3BSD & SunOS 4.0 command line options.
  118.      * Defer everything except password change to other programs.
  119.      */
  120.     while ((opt = getopt(argc, argv, options)) != EOF) {
  121.         switch (opt) {
  122. #ifdef    sun
  123.         /*
  124.          * Recognized the SunOS 4.1 switches
  125.          * but just say that we don't handle them.
  126.          */
  127.         case 'a':
  128.             printf("Option \"-a\" not supported.\n");
  129.             exit(1);
  130.         case 'l':
  131.             printf("Option \"-l\" not supported.\n");
  132.             exit(1);
  133.         case 'y':
  134.             printf("Option \"-y\" not supported.\n");
  135.             exit(1);
  136.         case 'd':
  137.             printf("Option \"-d\" not supported.\n");
  138.             exit(1);
  139.         case 'e':
  140.             printf("Option \"-e\" not supported.\n");
  141.             exit(1);
  142.         case 'n':
  143.             printf("Option \"-n\" not supported.\n");
  144.             exit(1);
  145.         case 'x':
  146.             printf("Option \"-x\" not supported.\n");
  147.             exit(1);
  148.         case 'F':
  149.             printf("Option \"-F\" not supported.\n");
  150.             exit(1);
  151. #endif
  152.         case 'f':
  153.             punt("chfn");
  154.             break;
  155.         case 's':
  156.             punt("chsh");
  157.             break;
  158.         case 'P':        /* Data source is a program */
  159.             if (getuid())
  160.                 quit(0, "Option \"-P\" reserved for super-user.\n");
  161.             from_prog = 1;
  162.             break;
  163.         case 'V':
  164.             printf("%s; patch level %s\n", version, patchlevel);
  165.             exit(0);
  166.         }
  167.     }
  168.     bzero(ppbuf, sizeof(ppbuf));
  169. #ifndef    DEBUG
  170.     if (geteuid())
  171.         quit(0,"Permission denied.\n");
  172. #endif
  173.     checktty();
  174.     savetty();
  175.     myname = getlogin();
  176.     if (myname == 0 || *myname == '\0') {
  177.         if ((pw = getpwuid(getuid())) == ((struct passwd *)NULL))
  178.             quit(1, "Cannot get your login name.\n");
  179.         strncpy(mysavedname, pw->pw_name, sizeof(mysavedname));
  180.         myname = mysavedname;
  181.     }
  182.     (void) strcpy(mylogin, myname);
  183.     (void) signal(SIGINT, catchit);
  184.     (void) signal(SIGQUIT, catchit);
  185.  
  186. #ifdef    SYSLOG
  187.     openlog("passwd", LOG_PID | LOG_CONS, LOG_AUTH);
  188. #endif
  189.     setcheckpasswd("-c", CONFIG_FILE, 0);
  190.     pw_initialize();
  191.  
  192.     if (argv[optind])
  193.         (void) strcpy(username, argv[optind]);
  194.     else
  195.         (void) strcpy(username, mylogin);
  196.  
  197.     if (strcmp(username, mylogin) == 0 && getuid()) {
  198.         if (pw_getuserbyname(username, password) == 0)
  199.             quit(1, "Cannot get your password information.\n");
  200.         if (password[0])
  201.             getpassword(password, ppbuf, sizeof(ppbuf));
  202.     }
  203.     else {
  204.         if (pw_getuserbyname(username, password) == 0)
  205.             quit(0, "No such user %s\n", argv[optind]);
  206.         if (pw_permission() == 0)
  207.             quit(0, "Permission denied.\n");
  208.         printf("Changing password for %s\n", username);
  209.     }
  210.     motd(MOTD_FILE, (char *)0);
  211.  
  212.     for (;;) {
  213.         char    *px;        /* Temp */
  214.         int    ntries = 0;    /* Password match counter */
  215.  
  216.         px = getpass(from_prog ? "" : "New password (? for help): ");
  217.         if (px == NULL)
  218.             quit(0, "EOF during new password read.\n");
  219.         (void) strcpy(pbuf, px);
  220.         if (pbuf[0] == '?') {
  221.             motd(HELP_FILE, "Missing help file");
  222.             continue;
  223.         }
  224.         /* Sanity check the new password */
  225.         if (pw_check(pbuf) == 0)
  226.             continue;
  227.  
  228.         /* Get confirmation */
  229.         px = getpass(from_prog ? "" : "New password (again): ");
  230.         if (px == NULL)
  231.             quit(0, "EOF during new password read.\n");
  232.         (void) strcpy(pbuf2, px);
  233.         if (strcmp(pbuf, pbuf2)) {
  234.             if (ntries++ >= retries) 
  235.                 quit(0, "Too many attempts.\n");
  236.             else
  237.                 printf("They don't match; try again.\n");
  238.             if (from_prog)
  239.                 quit(0, (char *)0);
  240.             else
  241.                 continue;
  242.         }
  243.         /* Disallow new password == old password */
  244.         if (pw_compare(password, pbuf)) {
  245.             printf("New password must be different than old; try again.\n");
  246.             if (from_prog)
  247.                 quit(0, (char *)0);
  248.             else
  249.                 continue;
  250.         }
  251.         else
  252.             break;
  253.     }
  254.     pw_replace(pbuf, ppbuf);
  255. #ifdef    SYSLOG
  256.     syslog(LOG_INFO, "Password changed for %s by %s\n",
  257.         username, mylogin);
  258. #endif
  259.     printf("Password changed for %s\n", username);
  260.     pw_cleanup(0);
  261.     exit(0);
  262. }
  263.  
  264.  
  265. /*
  266.  *    getpassword -- read password and check against current.
  267.  */
  268. getpassword(pwd_crypt, pwd_plain, pwlen)
  269. char    *pwd_crypt,        /* Present password (encrypted) */
  270.     *pwd_plain;        /* Present password (plain)  */
  271. int    pwlen;            /* Length of present password buffer */
  272. {
  273.     int    ntries = 0;    /* Match attempt counter */
  274.     char    *px;        /* Temp */
  275.  
  276.     for (;;) {
  277.         px = getpass(from_prog ? "" : "Current password: ");
  278.         if (px == 0)
  279.             quit(0, "EOF during password read.\n");
  280.         if (*px == '\0')
  281.             continue;
  282.         if (!pw_compare(pwd_crypt, px)) {
  283.             printf("Password incorrect.\n");
  284.             if (ntries++ == retries)
  285.                 quit(0, "Password not matched.\n");
  286.         }
  287.         else
  288.             break;
  289.     }
  290.     if (pwd_plain)
  291.         (void) strncpy(pwd_plain, px, pwlen);
  292. }
  293.  
  294. /* 
  295.  *    randomstring - create a string of random characters
  296.  */
  297. randomstring(buf, len)
  298. char    buf[];        /* String buffer */
  299. int    len;        /* Length of buf */
  300. {
  301.     register int    i,        /* Temp */
  302.             n;        /* Temp */
  303.     time_t        tv;        /* Current time */
  304.     char        proto[128];    /* Build buffer */
  305.  
  306.     (void) time (&tv);
  307.     /*
  308.      * Assumes (implicitly) that sizeof(int) == sizeof(long)
  309.      */
  310.     (void) srand ( (tv & 0x38d9fcff) ^ getpid ());
  311.     for (i = 0; i < sizeof(proto); i++) {    /* fill proto vector */
  312.         int    c;        /* Temp */
  313.  
  314.         for (;;) {
  315.             c = rand () % 0x7f;    /* turn into ASCII */
  316.             if (isalnum (c))
  317.                 break;
  318.         }
  319.         proto[i] = (char )c;
  320.     }
  321.     (void) srand(((unsigned )tv & 0x1a90fefc) ^ getpid());
  322.     for (i = 0; i < len; i++) {
  323.         n = rand() % sizeof(proto);
  324.         buf[i] = proto[n];
  325.     }
  326.     buf[len] = 0;
  327. }
  328.  
  329. /*
  330.  *    quit - print/log error message and exit
  331.  */
  332. quit(logit, message, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10)
  333. /*VARARGS2*/
  334. int    logit;        /* 0 = don't log, <> 0 = log message */
  335. char    *message;    /* Message */
  336. int    *a1, *a2, *a3, *a4, *a5, *a6, *a7, *a8, *a9, *a10;    /* Args */
  337. {
  338.     if (message) {
  339.         /*
  340.          * If used from program, direct failure messages to stdout,
  341.          * else send to stderr.
  342.          */
  343.         fprintf(from_prog ? stdout : stderr,  message,
  344.             a1, a2, a3, a4, a5, a6, a7, a8, a9, a10);
  345. #ifdef    SYSLOG
  346.         if (logit)
  347.             syslog(LOG_ERR,  message,
  348.                 a1, a2, a3, a4, a5, a6, a7, a8, a9, a10);
  349. #endif
  350.     }
  351.     pw_cleanup(1);
  352.     exit(1);
  353. }
  354.  
  355. /*
  356.  *    motd - issue 'message of the day'
  357.  */
  358. motd(fn, complaint)
  359. char    *fn,            /* Name of file to present */
  360.     *complaint;        /* Complaint if missing */
  361. {
  362.     char    cmdbuf[BUFSIZ];        /* Buffer to build command in */
  363.  
  364.     if (access(fn, 0) < 0) {
  365.         if (complaint)
  366.             printf("%s\n", complaint);
  367.         return;
  368.     }
  369.     if (isatty(0))
  370. #ifdef    SYSV
  371.         (void) sprintf(cmdbuf, "pg -n -s %s", fn);
  372. #else
  373.         (void) sprintf(cmdbuf, "more -d %s", fn);
  374. #endif
  375.     else
  376.         (void) sprintf(cmdbuf, "cat %s", fn);
  377.  
  378.     if (fork() == 0) {
  379.         (void) setgid(getgid());
  380.         (void) setuid(getuid());
  381.         (void) system(cmdbuf);
  382.         exit(0);
  383.     }
  384.     (void) wait((int *)0);        /* "Wrong" for BSD, right for SYS V */
  385. }
  386.  
  387. /*
  388.  *    checktty - Attempt to check against being pipe-fed
  389.  */
  390. checktty()
  391. {
  392.     char    *stdin_tty,    /* ttyname(0) */
  393.         *stdout_tty,    /* ttyname(1) */
  394.         *t;        /* Temp */
  395.  
  396.     if (!isatty(0)) {
  397.         if (from_prog == 0)
  398.             quit(0, "Input not a tty.\n");
  399.         if (lseek(0, 0L, 1) < 0) {
  400.             if (errno != ESPIPE)
  401.                 quit(0, "Input not a tty or pipe.\n");
  402.             else
  403.                 return;
  404.         }
  405.     }
  406.     from_prog = 0;        /* Stdin is a tty - behave normal */
  407.     stdin_tty = ttyname(0);
  408.     if (stdin_tty == NULL || *stdin_tty == 0)
  409.         quit(0, "Cannot get name (stdin).\n");
  410.     t = malloc(strlen(stdin_tty) + 1);
  411.     if (t == NULL)
  412.         quit(1, "Cannot allocate temp memory.");
  413.     (void) strcpy(t, stdin_tty);
  414.     stdin_tty = t;
  415.  
  416.     stdout_tty = ttyname(1);
  417.     if (stdout_tty == NULL || *stdout_tty == 0)
  418.         quit(0, "Cannot get name (stdout).\n");
  419.     if (strcmp(stdin_tty, stdout_tty))
  420.         quit(0, "Input and output are not the same tty.\n");
  421.     free(stdin_tty);
  422. }
  423.  
  424. /*
  425.  *    catchit - tty interrupt catcher
  426.  */
  427. catchit()
  428. {
  429.     fixtty();
  430.     pw_cleanup(1);
  431.     quit(0, "\nInterrupted; changes discarded.\n");
  432. }
  433.  
  434.  
  435. #if    defined(SYSV)
  436. #    include <termio.h>        /* Vanilla SV termio */
  437.     struct termio saved_tty_mode;
  438. #endif
  439.  
  440. #if    defined(SUNOS4)
  441. #    include <sys/termios.h>        /* SUN OS 4.0 termio */
  442. #define    TCGETA    TCGETS
  443. #define    TCSETA    TCSETS
  444.     struct termios saved_tty_mode;
  445. #endif
  446.  
  447. #if    !defined(SUNOS4) && !defined(SYSV)
  448. #    include <sgtty.h>        /* BSD tty */
  449.     struct sgttyb saved_tty_mode;
  450.     int    saved_local_flags;
  451. #endif
  452. char    saves_valid  = 0;        /* Are the saved values valid? */
  453.  
  454. /*
  455.  *    savetty - save current terminal settings
  456.  */
  457. savetty()
  458. {
  459. #if    defined(SYSV) || defined(SUNOS4)
  460.     (void) ioctl(0, TCGETA, &saved_tty_mode);
  461. #else
  462.     (void) ioctl(0, TIOCGETP, &saved_tty_mode);
  463.     (void) ioctl(0, TIOCLGET, &saved_local_flags);
  464. #endif
  465.     saves_valid++;
  466. }
  467.  
  468. /*
  469.  *    fixtty - restore saved terminal settings
  470.  */
  471. fixtty()
  472. {
  473.     if (saves_valid) {
  474. #if    defined(SYSV) || defined(SUNOS4)
  475.         (void) ioctl(0, TCSETA, &saved_tty_mode);
  476. #else
  477.         (void) ioctl(0, TIOCSETP, &saved_tty_mode);
  478.         (void) ioctl(0, TIOCLSET, &saved_local_flags);
  479. #endif
  480.     }
  481. }
  482.  
  483. #ifdef    XGETPASS
  484. /*
  485.  *    The system getpass() throws away all but the first 8 characters
  486.  *    of a password string.  If this isn't enough for you, use this
  487.  *    routine instead.  This code assumes that stdin is the terminal.
  488.  */
  489. char    *
  490. getpass(prompt)
  491. char    *prompt;
  492. {
  493. #if    defined(SYSV)
  494.     struct termio    saved,        /* Saved tty characteristics */
  495.             noecho;        /* No-echo tty characteristics */
  496.     char    *strchr();
  497. #endif
  498. #if    defined(SUNOS4)
  499.     struct termios    saved,        /* Saved tty characteristics */
  500.             noecho;        /* No-echo tty characteristics */
  501. #else
  502.     struct sgttyb    saved,        /* Saved tty characteristics */
  503.             noecho;        /* No-echo tty characteristics */
  504. #endif
  505.     static char    ib[64];        /* Input buffer */
  506.     char    *rc;            /* Temp */
  507.  
  508. #if    defined(SYSV) || defined(SUNOS4)
  509.     (void) ioctl(0, TCGETA, &saved);
  510.     noecho = saved;
  511.     noecho.c_lflag &= ~ECHO;
  512.     (void) ioctl(0, TCSETA, &noecho);
  513. #else
  514.     (void) ioctl(0, TIOCGETP, &saved);
  515.     noecho = saved;
  516.     noecho.sg_flags &= ~ECHO;
  517.     (void) ioctl(0, TIOCSETP, &noecho);
  518. #endif
  519.     fprintf(stderr, "%s", prompt);
  520.     fflush(stderr);
  521.     rc = fgets(ib, sizeof(ib), stdin);
  522.     putc('\n', stderr);
  523.     fflush(stderr);
  524.  
  525. #if    defined(SYSV) || defined(SUNOS4)
  526.     (void) ioctl(0, TCSETA, &saved);
  527. #else
  528.     (void) ioctl(0, TIOCSETP, &saved);
  529. #endif
  530.     if (rc == NULL)
  531.         return(NULL);
  532.     if (rc = index(ib, '\n'))
  533.         *rc = 0;
  534.     return(ib);
  535. }
  536. #endif
  537.  
  538. #ifdef    XPUTPWENT
  539. /*
  540.  *    putpwent - replacement for the System V routine
  541.  *        This writes the "standard" passwd file format.
  542.  */
  543. putpwent(p, f)
  544. struct passwd    *p;    /* Passwd entry to put */
  545. FILE    *f;        /* File pointer */
  546. {
  547. #ifdef    UNSIGNED_UID
  548.     fprintf(f, "%s:%s:%u:%u:%s:%s:%s\n",
  549. #else
  550.     fprintf(f, "%s:%s:%d:%d:%s:%s:%s\n",
  551. #endif
  552.         p->pw_name, p->pw_passwd, p->pw_uid, p->pw_gid,
  553.         p->pw_gecos, p->pw_dir, p->pw_shell);
  554. }
  555. #endif
  556.  
  557. #ifdef    XFGETPWENT
  558. /*
  559.  *    fgetpwent() - read passwd(5) entry from a file
  560.  *        This reads the "standard" passwd file format.
  561.  */
  562. struct passwd *
  563. fgetpwent(f)
  564. FILE    *f;            /* Pointer to open passwd format file */
  565. {
  566.     static struct passwd    pwdata;    /* Return data */
  567.     static char    ibuf[BUFSIZ];    /* Input and return data buffer */
  568.     char        *p;        /* ACME Pointer Works, Inc */
  569.  
  570.     bzero((char *)&pwdata, sizeof(pwdata));
  571.     pwdata.pw_name = pwdata.pw_passwd = pwdata.pw_comment =
  572.     pwdata.pw_gecos = pwdata.pw_dir = pwdata.pw_shell = "";
  573.  
  574.     if (fgets(ibuf, sizeof(ibuf), f) == NULL)
  575.         return(0);
  576.     if ((p = index(ibuf, '\n')) == 0)        /* Zap newline */
  577.         quit(1, "Ill-formed passwd entry \"%s\".\n", ibuf);
  578.     else
  579.         *p = 0;
  580. #define    skipc while (*p && *p != ':' && *p != '\n') ++p; if (*p) *p++ = 0
  581.     p = ibuf;
  582.     pwdata.pw_name = p;    skipc;
  583.     pwdata.pw_passwd = p;    skipc;
  584.     pwdata.pw_uid = atoi(p); skipc;
  585.     pwdata.pw_gid = atoi(p); skipc;
  586.     pwdata.pw_gecos = p;    skipc; 
  587.     pwdata.pw_dir = p;    skipc;
  588.     pwdata.pw_shell = p;
  589.     return(&pwdata);
  590. #undef    skipc
  591. }
  592. #endif
  593.  
  594. #ifdef    SYSV
  595. /*
  596.  *    rename - replacement for the 4.2/4.3 BSD rename system call
  597.  */
  598. rename(src, dst)
  599. char    *src,        /* Source path */
  600.     *dst;        /* Destination path */
  601. {
  602.     if (unlink(dst) < 0) {
  603.         if (errno != ENOENT)
  604.             return(-1);
  605.     }
  606.     if (link(src, dst) < 0)
  607.         return(-1);
  608.     return(unlink(src));
  609. }
  610. #endif
  611.  
  612. /*
  613.  *    punt() - run another program to do what we don't do
  614.  */
  615. punt(prog)
  616. char    *prog;        /* Program to run */
  617. {
  618.     (void) setgid(getgid());
  619.     (void) setuid(getuid());
  620.     (void) execlp(prog, prog, 0);
  621.     perror(prog);
  622.     exit(1);
  623. }
  624. /*        End npasswd.c        */
  625.