home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 January / usenetsourcesnewsgroupsinfomagicjanuary1994.iso / sources / unix / volume25 / npasswd / part03 / pw_yp.c < prev   
Encoding:
C/C++ Source or Header  |  1991-12-19  |  15.7 KB  |  635 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.  *    pw_yp - Routines for dealing with SUN Yellow Pages password files
  16.  *
  17.  *    This code can update local password file, can cause rebuilding of
  18.  *    local YP maps and can use yppasswdd(8) to change YP passwords.
  19.  *
  20.  *    Must be linked with -lrpcsvc
  21.  */
  22. #include <stdio.h>
  23. #include <sys/param.h>
  24. #include <sys/stat.h>
  25. #include <signal.h>
  26. #include <errno.h>
  27. #include <pwd.h>
  28. #include <fcntl.h>
  29. #include <rpc/rpc.h>
  30. #include <rpcsvc/ypclnt.h>
  31. #include <rpcsvc/yppasswd.h>
  32. #include <sys/socket.h>
  33.  
  34. #ifdef    SECURE_RPC
  35. #include <rpc/key_prot.h>
  36. #endif
  37.  
  38. #ifdef    SYSV
  39. #define    index    strchr
  40. #endif
  41.  
  42. #ifdef    SYSLOG
  43. #include <syslog.h>
  44. #endif
  45.  
  46. #ifndef lint
  47. static char sccsid[] = "@(#)pw_yp.c    1.15 1/25/91 (cc.utexas.edu)";
  48. #endif
  49.  
  50. #define    NONE    -1    /* YP not active */
  51. #define    NOT    0    /* YP active - we are not master */
  52. #define    IS    1    /* YP active - we have the password file */
  53.  
  54. #define    SLOP    128    /* Size difference tolerated old <> new passwd file */
  55.  
  56. typedef struct passwd    passwd;
  57. typedef    struct passwd    *passwdp;
  58.  
  59. static passwd    theUser,    /* User being changed */
  60.         Me;        /* User invoking passwd */
  61. static int    myuid,        /* Uid of invoker */
  62.         mytempfile = 0;    /* Does PASSWD_TEMP belong to me? */
  63. static char    *ypmaster,    /* Name of the YP master */
  64.         *ypdomain;    /* YP domain name */
  65.  
  66. #define    PASSWD_MAP    "passwd.byname"        /* Name of YP passwd map */
  67.  
  68. /*
  69.  *    File names
  70.  */
  71. #ifndef    PASSWD_FILE
  72. #define    PASSWD_FILE    "/etc/passwd"
  73. #endif
  74.  
  75. #ifndef    PASSWD_SAVE
  76. #define    PASSWD_SAVE    "/etc/opasswd"
  77. #endif
  78.  
  79. #ifndef    PASSWD_TEMP
  80. #define    PASSWD_TEMP    "/etc/ptmp"
  81. #endif
  82.  
  83. #define    PASSWD_MODE    0644
  84.  
  85. #ifdef    DEBUG
  86. static char    *passwdtemp = "./etc_ptmp",        /* Temp file */
  87.         *passwdfile = "./etc_passwd",        /* Password file */
  88.         *savefile = "./etc_opasswd";        /* Save file */
  89. #else
  90. static char    *passwdtemp = PASSWD_TEMP,
  91.         *passwdfile = PASSWD_FILE,
  92.         *savefile = PASSWD_SAVE;
  93. #endif
  94. static char    auxlockfile[MAXPATHLEN];        /* Aux lock file */
  95.  
  96. extern int    errno;
  97.  
  98. char    *getlogin(),
  99.     *crypt();
  100.  
  101. /*
  102.  *    pw_initialize - set up
  103.  */
  104. pw_initialize()
  105. {
  106.     passwdp    me;            /* Passwd for invoker */
  107.     char    *myname = getlogin();    /* Invoker login name */
  108.  
  109. #ifdef    DEBUG
  110.     setpwfile(passwdfile);
  111. #endif
  112.     myuid = getuid();
  113.     if (myname && *myname) {
  114.         if ((me = getpwnam(myname)) == NULL)
  115.             quit(1, "Cannot get user identification from name.\n");
  116.     } else {
  117.         if ((me = getpwuid(myuid)) == NULL)
  118.             quit(1, "Cannot get user identification from uid.\n");
  119.     }
  120.  
  121.     _cppasswd(me, &Me);
  122.     return(1);
  123. }
  124.  
  125. /*
  126.  *    pw_getuserbyname - get password
  127.  *
  128.  *    Returns 1 if passwd found for <name>
  129.  *        0 otherwise
  130.  */
  131. pw_getuserbyname(name, passwdb)
  132. char    *name,        /* User name */
  133.     *passwdb;    /* Where to stuff password */
  134. {
  135.     passwdp    p;    /* Temp */
  136.  
  137.     if ((p = getpwnam(name)) == NULL)
  138.         return(0);
  139.     _cppasswd(p, &theUser);
  140.     (void) strcpy(passwdb, p->pw_passwd);
  141.     return(1);
  142. }
  143.  
  144. /*
  145.  *    pw_permission - check password change permission
  146.  *
  147.  *    Returns 1 if password can be changed
  148.  *        0 if not
  149.  */
  150. pw_permission()
  151. {
  152.     /*
  153.      * Is this my password or someone elses?
  154.      */
  155.     if (strcmp(Me.pw_name, theUser.pw_name) && myuid)
  156.         return(0);
  157.  
  158.     /*
  159.      * If on a YP client, root cannot change another
  160.      * users' password via yppasswd(), since we can't
  161.      * get a plain text password to pass to yppasswdd().
  162.      */
  163.     if (myuid == 0 && is_yp_master() == NOT) {
  164.         FILE    *pf;
  165.         passwdp px,        /* Password file traversal */
  166.             fgetpwent();
  167.         int    rc = 0;
  168.  
  169.         /* What if passwdfile != /etc/passwd?  */
  170.         if ((pf = fopen(passwdfile, "r")) == NULL)
  171.             quit(1, "Cannot open password file \"%s\".\n", passwdfile);
  172.         /*
  173.          * Scan local password file, looking for user
  174.          * Cannot use getpwnam() because it will use YP - I want to know
  175.          * if the user's password file entry is >>local<<
  176.          */
  177.         while ((px = fgetpwent(pf)) != NULL) {
  178.             if (strcmp(px->pw_name, theUser.pw_name) == 0) {
  179.                 rc = 1;
  180.                 break;
  181.             }
  182.         }
  183.         fclose(pf);
  184.         if (rc) {
  185.             if (strncmp(px->pw_passwd, "##", 2) == 0)
  186.                 quit(0,
  187.                 "Changing of adjunct passwords not supported.\n");
  188.             return(1);
  189.         }
  190.         else
  191.             quit(0, "Password for %s can only be changed on YP server %s.\n",
  192.                 theUser.pw_name, ypmaster);
  193.     }
  194.     /* Check if passwd is '##username' - can't do that yet */
  195.  
  196.     /*
  197.      * Other checks can be put here to determine if the invoker should
  198.      * be allowed to change this password.
  199.      */
  200.     return(1);
  201. }
  202.  
  203. /*
  204.  *      pw_compare - compare old and new passwords
  205.  *
  206.  *      Returns 1 if check = new, 0 if not
  207.  */
  208. pw_compare(current, new)
  209. char    *current,        /* Current pw (encrypted) */
  210.     *new;            /* check pw (plain) */
  211. {
  212.     if (!*current)        /* Is current password null? */
  213.         return(0);
  214.     /* Put other administrative checks here */
  215.     return(!strcmp(current, crypt(new, current)));
  216. }
  217.  
  218. /*
  219.  *      pw_check - sanity check password.  Right now just calls
  220.  *              the password check code
  221.  *
  222.  *      Returns 1 if password is ok to use, 0 otherwise
  223.  */
  224. pw_check(pwd)
  225. char    *pwd;
  226. {
  227.     int    rc = checkpasswd(theUser.pw_uid, pwd);
  228.  
  229. #ifdef    PASSWORD_HISTORY
  230.     if (rc)
  231.         return(rc);
  232.     /* Call password history checker to prevent password reuse */
  233.     rc = passwd_history(theUser.pw_uid, pwd);
  234. #endif
  235.     return(rc);
  236. }
  237.  
  238. /*    Error message for when yppasswdd fails with error code 1.  */
  239. static char *yperrmsg =
  240. "Password change failed: Problem with yppasswdd.\n\n\
  241. This is probably because the YP maps are out of sync\n\
  242. with the YP passwd file for %s on %s.\n\n\
  243. Please try again later.\n";
  244.  
  245. /*
  246.  *      pw_replace - replace password in passwd file 
  247.  */
  248. pw_replace(newpwd, curpwd)
  249. char    *newpwd,        /* New password (plain) */
  250.     *curpwd;        /* Old password (plain) */
  251. {
  252.     passwdp px,        /* Password file traversal */
  253.         fgetpwent();
  254.     long    oldsigs,    /* Old signal mask */
  255.         blocksigs = sigmask(SIGINT) |    /* Sigs to block */
  256.                 sigmask(SIGQUIT) |
  257.                 sigmask(SIGTSTP);
  258.     FILE    *tf,        /* New password file output */
  259.         *pf;        /* Current password file input */
  260.     int    fd,        /* Temp file create fd */
  261.         islocal = 0;    /* Is user in local password file */
  262.     struct stat    oldstat,    /* Old password file stat */
  263.             newstat;    /* New password file stat */
  264.  
  265.     if ((pf = fopen(passwdfile, "r")) == NULL)
  266.         quit(1, "Cannot open password file \"%s\".\n", passwdfile);
  267.     /*
  268.      * Scan local password file, looking for user
  269.      * Cannot use getpwnam() because it will use YP - I want to know
  270.      * if I have to change a >>local<< password file.
  271.      */
  272.     while ((px = fgetpwent(pf)) != NULL) {
  273.         if (*px->pw_name == '+' || *px->pw_name == '-')
  274.             continue;
  275.         if (strcmp(px->pw_name, theUser.pw_name) == 0) {
  276.             if (strncmp(px->pw_passwd, "##", 2) == 0)
  277.                 quit(0,
  278.                 "Changing of adjunct passwords not supported.\n");
  279.             islocal++;
  280.             break;
  281.         }
  282.     }
  283.     rewind(pf);
  284.  
  285.     /*
  286.      * If the user was not in the local password file, use RPC
  287.      * to update the Yellow Pages (NIS) password file.
  288.      */
  289.     if (islocal == 0) {
  290.         if (is_yp_master() == NOT) {
  291.             int    rc;        /* Return code from ypasswdd */
  292.             int    why;        /* RPC call return code */
  293.             int    ypport;        /* Port for RPC call */
  294.             struct yppasswd yppasswd; /* YP passwd change block */
  295.             char    salt[4];    /* Password encryption salt */
  296.  
  297.             if (curpwd[0] == 0)
  298.                 quit(0, "Cannot change YP password without old password.\n");
  299.             randomstring(salt, sizeof(salt));
  300.             theUser.pw_passwd = crypt(newpwd, salt);
  301.             yppasswd.oldpass = curpwd;
  302.             _cppasswd(&theUser, &yppasswd.newpw);
  303. #ifdef    DEBUG
  304.             printf("yppasswd(%s, %s)\n", curpwd, theUser.pw_passwd);
  305. #else
  306.             if ((ypport = getrpcport(ypmaster, YPPASSWDPROG,
  307.                  YPPASSWDPROC_UPDATE, IPPROTO_UDP)) == 0)
  308.                 quit(1, "%s is not running ypassswdd.\n",
  309.                      ypmaster);
  310.  
  311.             if (ypport >= IPPORT_RESERVED)
  312.                 quit(1, "yppasswdd on %s not privleged.\n",
  313.                     ypmaster);
  314.             rc = callrpc(ypmaster, YPPASSWDPROG, YPPASSWDVERS,
  315.                 YPPASSWDPROC_UPDATE, xdr_yppasswd, &yppasswd,
  316.                 xdr_int, &why);
  317.  
  318.             /* RPC call error */
  319.             if (rc)
  320. #if    NO_CLNT_SPERRNO
  321.                 clnt_perrno(rc);
  322.                 quit(1, "Password change failed (%s)\n",
  323.                     ypmaster);
  324. #else
  325.                 quit(1, "Password change failed (%s): %s\n",
  326.                     ypmaster, clnt_sperrno(rc));
  327. #endif
  328.  
  329.             /* Error returned from yppasswdd */
  330.             if (why) {
  331. #ifdef    SYSLOG
  332.                 syslog(LOG_ERR,
  333.                     "yppasswdd error %d on %s for %s",
  334.                     why, ypmaster, theUser.pw_name);
  335. #endif
  336.                 if (why == 1)
  337.                    quit(0, yperrmsg,  ypdomain, ypmaster);
  338.                 else
  339.                    quit(1, "Password change failed.\n");
  340.             }
  341. # ifdef    SECURE_RPC
  342.             reset_secret_key(curpwd);
  343. # endif /* SECURE_RPC */
  344. #endif    /* DEBUG */
  345.             return;
  346.         }
  347.         else    /* User not in local passwd, and not in YP passwd */
  348.             quit(1, "User %s missing from password file.\n",
  349.                 theUser.pw_name);
  350.     }
  351.  
  352.     /*
  353.      * There is a local password file to change
  354.      */
  355.     (void) umask(0);
  356.     (void) fstat(fileno(pf), &oldstat);
  357.     /*
  358.      * Use different temp file if on YP master.
  359.      * This deals with the SunOS 4.0.3 yppasswdd which creates temp files
  360.      * named "passwd-file.ptmp", rather than the traditional "/etc/ptmp".
  361.      * But there are still a lot of applications which use /etc/ptmp,
  362.      * so is it used as the passwd temp file and the 'auxlockfile' is
  363.      * also made --- >>>GROAN<<<.
  364.      */
  365.     auxlockfile[0] = 0;
  366.     if (is_yp_master() == IS) {
  367.         (void) sprintf(auxlockfile, "%s.ptmp", passwdfile);
  368.         close(mklocktemp(auxlockfile));
  369.     }
  370.     mytempfile = 1;
  371.     fd = mklocktemp(passwdtemp);
  372.     if ((tf = fdopen(fd, "w")) == NULL)
  373.         quit(1, "Cannot fdopen temp file.\n");
  374.  
  375.     oldsigs = sigblock(blocksigs);
  376.     while ((px = fgetpwent(pf)) != NULL) {
  377.         if (px->pw_name == 0 || px->pw_name[0] == 0) /* Sanity check */
  378.             continue;
  379.         if (strcmp(px->pw_name, theUser.pw_name) == 0) {
  380.             char    salt[4];    /* Password encryption salt */
  381.  
  382.             randomstring(salt, sizeof(salt));
  383.             theUser.pw_passwd = crypt(newpwd, salt);
  384.             px = &theUser;
  385.         }
  386.         (void) putpwent(px, tf);
  387.     }
  388.     (void) fflush(tf);            /* Force buffers empty */
  389.     (void) fstat(fileno(tf), &newstat);    /* Get size */
  390.     (void) fclose(tf);
  391.     (void) fclose(pf);
  392.  
  393.     /*
  394.      * Check if the new password file is complete.  Since the encrypted
  395.      * password is of a fixed length, the new file should be roughly
  396.      * the same size as the old one.
  397.      *
  398.      * This assumption will FAIL when this program does chfn and chsh!!! -
  399.      * use line counts.
  400.      */
  401.     if (newstat.st_size < (oldstat.st_size - SLOP))
  402.         quit(1,
  403.         "New password file appears to be incomplete - aborting.\n");
  404.  
  405.     if (rename(passwdfile, savefile) < 0) {
  406.         perror("Password file save");
  407.         (void) unlink(passwdtemp);
  408.         quit(1, "Can't save password file.\n");
  409.     }
  410.     if (rename(passwdtemp, passwdfile) < 0) {
  411.         perror("Password file replace");
  412.         (void) unlink(passwdtemp);
  413.         (void) link(savefile, passwdfile);
  414.         quit(1, "Can't replace password file.\n");
  415.     }
  416.     if (is_yp_master() == IS)
  417.         updateyp();
  418.     (void) sigsetmask(oldsigs);
  419. }
  420.  
  421. /*
  422.  *      pw_cleanup - clean up after myself
  423.  */
  424. pw_cleanup(code)
  425. int    code;        /* 0 for normal, 1 for abort */ /*NOTUSED*/
  426. {
  427.     if (mytempfile) {
  428.         (void) unlink(passwdtemp);
  429.         if (auxlockfile[0])
  430.             (void) unlink(auxlockfile);
  431.     }
  432. }
  433.  
  434. /*
  435.  *      _newstr - copy string into new storage
  436.  */
  437. static char *
  438. _newstr(s)
  439. char    *s;        /* String to copy */
  440. {
  441.     register char    *t;    /* Temp */
  442.     char    *malloc();
  443.  
  444.     if (s == NULL)
  445.         return(0);
  446.     t = malloc(strlen(s) + 1);
  447.     if (t == NULL)
  448.         quit(1, "No memory.\n");
  449.     (void) strcpy(t, s);
  450.     return(t);
  451. }
  452.  
  453. /*
  454.  *    mklocktemp - Make temp file with exclusive use checking
  455.  *
  456.  *    Returns file descriptor of created file, else exits with error
  457.  */
  458. static int
  459. mklocktemp(name)
  460. char    *name;
  461. {
  462.     int    fd;
  463.  
  464.     fd = open(name, O_WRONLY|O_CREAT|O_EXCL, PASSWD_MODE);
  465.     if (fd < 0) {
  466.         if (errno == EEXIST)
  467.             quit(0, "Password file busy - try again.\n");
  468.         perror("Tempfile create");
  469.         quit(1, "Cannot create temp file.\n");
  470.     }
  471.     return(fd);
  472. }
  473.  
  474. /*
  475.  *     _cppasswd - copy a passwd structure
  476.  */
  477. static
  478. _cppasswd(f,t)
  479. passwdp    f,        /* From */
  480.     t;        /* To */
  481. {
  482.     *t = *f;
  483.     t->pw_name = _newstr(f->pw_name);
  484.     t->pw_passwd = _newstr(f->pw_passwd);
  485.     t->pw_comment = _newstr(f->pw_comment);
  486.     t->pw_gecos = _newstr(f->pw_gecos);
  487.     t->pw_dir = _newstr(f->pw_dir);
  488.     t->pw_shell = _newstr(f->pw_shell);
  489. }
  490.  
  491. /*
  492.  *    is_yp_master - Figure out whether we are running on the Yellow Pages
  493.  *        master for the password file maps.
  494.  *
  495.  *    Returns:
  496.  *        IS if we are the master
  497.  *        NOT if we are not the master
  498.  *        NONE if there is no master
  499.  */
  500. #include <netdb.h>
  501. #ifndef    MAXHOSTNAMLEN
  502. #define    MAXHOSTNAMLEN 32
  503. #endif
  504.  
  505. is_yp_master()
  506. {
  507.     static char    known = 0,    /* We've been here */
  508.             answer = NOT;    /* ...and this is the answer */
  509.     char    hostname[MAXHOSTNAMLEN];    /* Our host name */
  510.     char    *index();
  511.     struct hostent    *hinfo;
  512.  
  513.     if (known)
  514.         return(answer);
  515.     (void) gethostname(hostname, sizeof(hostname));
  516.     if (yp_get_default_domain(&ypdomain)) {
  517. /*         quit(1, "Cannot get YP domain.\n"); */
  518.         known++;
  519.         return(answer = NONE);        /* Assume no YP running */
  520.     }
  521.  
  522.     if (yp_master(ypdomain, PASSWD_MAP, &ypmaster)) {
  523.         known++;
  524.         return(answer = NONE);        /* Assume no YP running */
  525.     }
  526.  
  527.     known++;
  528. #ifdef    FASTCHECK
  529.     /*
  530.      * Stupid (but fast) hostname check (first component only)
  531.      */
  532.     {
  533.         char    *p;            /* Scratch */
  534.  
  535.         if (p = index(ypmaster, '.')) *p = 0;
  536.         if (p = index(hostname, '.')) *p = 0;
  537.     }
  538. #else
  539.     /*
  540.      * Compare my host name and the YP master's host name.
  541.      * Use gethostbyname() to return the fully qualified form so that
  542.      * a string compare can be done.
  543.      */
  544.     if (index(hostname, '.') == 0) {
  545.         if ((hinfo = gethostbyname(hostname)) == 0)
  546.             quit(1, "Cannot get hostinfo for self.\n");
  547.         (void) strcpy(hostname, hinfo->h_name);
  548.     }
  549.     if (index(ypmaster, '.') == 0) {
  550.         static char    ypmaster_f[MAXHOSTNAMLEN];
  551.  
  552.         if ((hinfo = gethostbyname(ypmaster)) == 0)
  553.             quit(1, "Cannot get hostinfo for ypmaster.\n");
  554.         (void) strcpy(ypmaster_f, hinfo->h_name);
  555.         ypmaster = ypmaster_f;
  556.     }
  557. #endif
  558.     if (strcmp(ypmaster, hostname) == 0)
  559.         return(answer = IS);
  560.     return(answer);
  561. }
  562.  
  563. /*
  564.  *    An example sh(1) script to update YP password map
  565.  */
  566. char    *ypcmd =
  567.     "(PATH=/bin:/usr/bin; export PATH; ypdirs='/var/yp /etc/yp'\n\
  568.     for d in $ypdirs; do\n\
  569.         if [ -d $d ]; then\n\
  570.             cd $d; exec make passwd\n\
  571.         fi\n\
  572.     done\n\
  573.     echo 'passwd: Cannot rebuild YP maps!' 1>&2\n\
  574.     false) >/dev/null &\n";
  575.  
  576. /*
  577.  *    updateyp - update local YP maps
  578.  */
  579. updateyp()
  580. {
  581.     (void) setuid(geteuid());     /* Get all privs */
  582.             /* (This assumes that we are setuid root) */
  583.     /*
  584.      * This machine is the YP master for passwd - invoke something
  585.      * to update the YP maps.
  586.      * Super-user can override the default YP updater by setting
  587.      * env "YP_UPDATE_PROC" to a command to be run instead.
  588.      * The name of the user being changed is piped to stdin of the command.
  589.      */
  590.     if (myuid == 0) {
  591.         char    *getenv();
  592.         char    *proc = getenv("YP_UPDATE_PROC");
  593.  
  594.         if (proc && *proc) {
  595.             char    cmdbuf[BUFSIZ];
  596.  
  597.             (void) sprintf(cmdbuf, "/bin/echo '%s' | ( %s )",
  598.                 theUser.pw_name, proc);
  599. #ifdef    DEBUG
  600.             printf("updateyp (proc): %s", cmdbuf);
  601. #endif
  602.             (void) system(cmdbuf);
  603.             return;
  604.         }
  605.     }
  606. #ifdef    DEBUG
  607.     printf("updateyp: %s", ypcmd);
  608. #endif
  609.     (void) system(ypcmd);
  610. }
  611.  
  612. #ifdef    SECURE_RPC
  613. /*
  614.  *    reset_secret_key - Reset secret key for secure RPC
  615.  */
  616. reset_secret_key(curpwd)
  617. char    *curpwd;
  618. {
  619.     char    mynet[MAXNETNAMELEN+1],
  620.         key[HEXKEYBYTES+1];
  621.  
  622.     getnetname(mynet);
  623.     if (!getsecretkey(mynet, key, curpwd))
  624.         return;        /* Secure RPC not running */
  625.     if (key[0] = 0)
  626.         return;        /* No secret key */
  627.     fprintf(stderr, "Cannot change secure RPC key\n");
  628.     /*
  629.      * Actually I could, but I'd have to steal from Sun source
  630.      * code to do it.  Sun wouldn't like that very much, so I won't.
  631.      */
  632. }
  633. #endif
  634. /*        End pw_yp.c         */
  635.