home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 October / usenetsourcesnewsgroupsinfomagicoctober1994disk2.iso / misc / volume26 / shadow / part02 < prev    next >
Text File  |  1991-11-24  |  56KB  |  2,444 lines

  1. Newsgroups: comp.sources.misc
  2. From: jfh@rpp386.Cactus.ORG (John F Haugh II)
  3. Subject:  v26i055:  shadow - Shadow Password Suite, Part02/11
  4. Message-ID: <1991Nov24.184939.20009@sparky.imd.sterling.com>
  5. X-Md4-Signature: 6188fc99729d72bc4b581316b96e90f5
  6. Date: Sun, 24 Nov 1991 18:49:39 GMT
  7. Approved: kent@sparky.imd.sterling.com
  8.  
  9. Submitted-by: jfh@rpp386.Cactus.ORG (John F Haugh II)
  10. Posting-number: Volume 26, Issue 55
  11. Archive-name: shadow/part02
  12. Environment: UNIX
  13. Supersedes: shadow-2: Volume 06, Issue 22-24
  14.  
  15. #! /bin/sh
  16. # into a shell via "sh file" or similar.  To overwrite existing files,
  17. # type "sh file -c".
  18. # The tool that generated this appeared in the comp.sources.unix newsgroup;
  19. # send mail to comp-sources-unix@uunet.uu.net if you want that tool.
  20. # Contents:  groupadd.c passwd.c usermod.c
  21. # Wrapped by kent@sparky on Sun Nov 24 11:03:41 1991
  22. PATH=/bin:/usr/bin:/usr/ucb ; export PATH
  23. echo If this archive is complete, you will see the following message:
  24. echo '          "shar: End of archive 2 (of 11)."'
  25. if test -f 'groupadd.c' -a "${1}" != "-c" ; then 
  26.   echo shar: Will not clobber existing file \"'groupadd.c'\"
  27. else
  28.   echo shar: Extracting \"'groupadd.c'\" \(7395 characters\)
  29.   sed "s/^X//" >'groupadd.c' <<'END_OF_FILE'
  30. X/*
  31. X * Copyright 1991, John F. Haugh II
  32. X * All rights reserved.
  33. X *
  34. X * Permission is granted to copy and create derivative works for any
  35. X * non-commercial purpose, provided this copyright notice is preserved
  36. X * in all copies of source code, or included in human readable form
  37. X * and conspicuously displayed on all copies of object code or
  38. X * distribution media.
  39. X */
  40. X
  41. X#ifndef lint
  42. Xstatic    char    sccsid[] = "@(#)groupadd.c    3.3    08:43:44    9/12/91";
  43. X#endif
  44. X
  45. X#include <sys/types.h>
  46. X#include <stdio.h>
  47. X#include <grp.h>
  48. X#include <ctype.h>
  49. X#include <fcntl.h>
  50. X
  51. X#ifdef    BSD
  52. X#include <strings.h>
  53. X#else
  54. X#include <string.h>
  55. X#endif
  56. X
  57. X#include "config.h"
  58. X#include "shadow.h"
  59. X
  60. X#ifdef    USE_SYSLOG
  61. X#include <syslog.h>
  62. X#endif
  63. X
  64. Xchar    group_name[BUFSIZ];
  65. Xint    group_id;
  66. X
  67. Xchar    *Prog;
  68. X
  69. Xint    oflg;    /* permit non-unique group ID to be specified with -g         */
  70. Xint    gflg;    /* ID value for the new group                                 */
  71. X
  72. X#ifdef    NDBM
  73. Xextern    int    gr_dbm_mode;
  74. Xextern    int    sg_dbm_mode;
  75. X#endif
  76. Xextern    char    *malloc();
  77. X
  78. Xextern    struct    group    *getgrnam();
  79. Xextern    struct    group    *gr_next();
  80. Xextern    int    gr_lock();
  81. Xextern    int    gr_unlock();
  82. Xextern    int    gr_rewind();
  83. Xextern    int    gr_open();
  84. X
  85. X#ifdef    SHADOWGRP
  86. Xextern    int    sgr_lock();
  87. Xextern    int    sgr_unlock();
  88. Xextern    int    sgr_open();
  89. X#endif
  90. X
  91. X/*
  92. X * usage - display usage message and exit
  93. X */
  94. X
  95. Xusage ()
  96. X{
  97. X    fprintf (stderr, "usage: groupadd [-g gid [-o]] group\n");
  98. X    exit (2);
  99. X}
  100. X
  101. X/*
  102. X * new_grent - initialize the values in a group file entry
  103. X *
  104. X *    new_grent() takes all of the values that have been entered and
  105. X *    fills in a (struct group) with them.
  106. X */
  107. X
  108. Xvoid
  109. Xnew_grent (grent)
  110. Xstruct    group    *grent;
  111. X{
  112. X    static    char    *empty_list = 0;
  113. X
  114. X    memset (grent, 0, sizeof *grent);
  115. X    grent->gr_name = group_name;
  116. X    grent->gr_passwd = "*";
  117. X    grent->gr_gid = group_id;
  118. X    grent->gr_mem = &empty_list;
  119. X}
  120. X
  121. X#ifdef    SHADOWGRP
  122. X/*
  123. X * new_sgent - initialize the values in a shadow group file entry
  124. X *
  125. X *    new_sgent() takes all of the values that have been entered and
  126. X *    fills in a (struct sgrp) with them.
  127. X */
  128. X
  129. Xvoid
  130. Xnew_sgent (sgent)
  131. Xstruct    sgrp    *sgent;
  132. X{
  133. X    static    char    *empty_list = 0;
  134. X
  135. X    memset (sgent, 0, sizeof *sgent);
  136. X    sgent->sg_name = group_name;
  137. X    sgent->sg_passwd = "!";
  138. X    sgent->sg_adm = &empty_list;
  139. X    sgent->sg_mem = &empty_list;
  140. X}
  141. X#endif    /* SHADOWGRP */
  142. X
  143. X/*
  144. X * grp_update - add new group file entries
  145. X *
  146. X *    grp_update() writes the new records to the group files.
  147. X */
  148. X
  149. Xvoid
  150. Xgrp_update ()
  151. X{
  152. X    struct    group    grp;
  153. X#ifdef    SHADOWGRP
  154. X    struct    sgrp    sgrp;
  155. X#endif    /* SHADOWGRP */
  156. X
  157. X    /*
  158. X     * Create the initial entries for this new group.
  159. X     */
  160. X
  161. X    new_grent (&grp);
  162. X#ifdef    SHADOWGRP
  163. X    new_sgent (&sgrp);
  164. X#endif    /* SHADOWGRP */
  165. X
  166. X    /*
  167. X     * Write out the new group file entry.
  168. X     */
  169. X
  170. X    if (! gr_update (&grp)) {
  171. X        fprintf (stderr, "%s: error adding new group entry\n", Prog);
  172. X        exit (1);
  173. X    }
  174. X#ifdef    NDBM
  175. X
  176. X    /*
  177. X     * Update the DBM group file with the new entry as well.
  178. X     */
  179. X
  180. X    if (! gr_dbm_update (&grp)) {
  181. X        fprintf (stderr, "%s: cannot add new dbm group entry\n", Prog);
  182. X        exit (1);
  183. X    }
  184. X    endgrent ();
  185. X#endif    /* NDBM */
  186. X
  187. X#ifdef    SHADOWGRP
  188. X
  189. X    /*
  190. X     * Write out the new shadow group entries as well.
  191. X     */
  192. X
  193. X    if (! sgr_update (&sgrp)) {
  194. X        fprintf (stderr, "%s: error adding new group entry\n", Prog);
  195. X        exit (1);
  196. X    }
  197. X#ifdef    NDBM
  198. X
  199. X    /*
  200. X     * Update the DBM group file with the new entry as well.
  201. X     */
  202. X
  203. X    if (! sgr_dbm_update (&sgrp)) {
  204. X        fprintf (stderr, "%s: cannot add new dbm group entry\n", Prog);
  205. X        exit (1);
  206. X    }
  207. X    endsgent ();
  208. X#endif    /* NDBM */
  209. X#endif    /* SHADOWGRP */
  210. X#ifdef    USE_SYSLOG
  211. X    syslog (LOG_INFO, "new group: name=%s, gid=%d\n",
  212. X        group_name, group_id);
  213. X#endif    /* USE_SYSLOG */
  214. X}
  215. X
  216. X/*
  217. X * find_new_gid - find the next available GID
  218. X *
  219. X *    find_new_gid() locates the next highest unused GID in the group
  220. X *    file, or checks the given group ID against the existing ones for
  221. X *    uniqueness.
  222. X */
  223. X
  224. Xvoid
  225. Xfind_new_gid ()
  226. X{
  227. X    struct    group    *grp;
  228. X
  229. X    /*
  230. X     * Start with some GID value if the user didn't provide us with
  231. X     * one already.
  232. X     */
  233. X
  234. X    if (! gflg)
  235. X        group_id = 100;
  236. X
  237. X    /*
  238. X     * Search the entire group file, either looking for this
  239. X     * GID (if the user specified one with -g) or looking for the
  240. X     * largest unused value.
  241. X     */
  242. X
  243. X    for (gr_rewind (), grp = gr_next ();grp;grp = gr_next ()) {
  244. X        if (strcmp (group_name, grp->gr_name) == 0) {
  245. X            fprintf (stderr, "%s: name %s is not unique\n",
  246. X                Prog, group_name);
  247. X            exit (1);
  248. X        }
  249. X        if (gflg && group_id == grp->gr_gid) {
  250. X            fprintf (stderr, "%s: gid %d is not unique\n",
  251. X                Prog, group_id);
  252. X            exit (1);
  253. X        }
  254. X        if (! gflg && grp->gr_gid >= group_id)
  255. X            group_id = grp->gr_gid + 1;
  256. X    }
  257. X}
  258. X
  259. X/*
  260. X * process_flags - perform command line argument setting
  261. X *
  262. X *    process_flags() interprets the command line arguments and sets
  263. X *    the values that the user will be created with accordingly.  The
  264. X *    values are checked for sanity.
  265. X */
  266. X
  267. Xvoid
  268. Xprocess_flags (argc, argv)
  269. Xint    argc;
  270. Xchar    **argv;
  271. X{
  272. X    extern    int    optind;
  273. X    extern    char    *optarg;
  274. X    char    *end;
  275. X    int    arg;
  276. X
  277. X    while ((arg = getopt (argc, argv, "og:")) != EOF) {
  278. X        switch (arg) {
  279. X            case 'g':
  280. X                gflg++;
  281. X                if (! isdigit (optarg[0]))
  282. X                    usage ();
  283. X
  284. X                group_id = strtol (optarg, &end, 10);
  285. X                if (*end != '\0') {
  286. X                    fprintf (stderr, "%s: invalid group %s\n",
  287. X                        Prog, optarg);
  288. X                    exit (3);
  289. X                }
  290. X                break;
  291. X            case 'o':
  292. X                if (! gflg)
  293. X                    usage ();
  294. X
  295. X                oflg++;
  296. X                break;
  297. X            default:
  298. X                usage ();
  299. X        }
  300. X    }
  301. X    if (! gflg)
  302. X        find_new_gid ();
  303. X
  304. X    if (optind == argc - 1)
  305. X        strcpy (group_name, argv[argc - 1]);
  306. X    else
  307. X        usage ();
  308. X}
  309. X
  310. X/*
  311. X * close_files - close all of the files that were opened
  312. X *
  313. X *    close_files() closes all of the files that were opened for this
  314. X *    new group.  This causes any modified entries to be written out.
  315. X */
  316. X
  317. Xclose_files ()
  318. X{
  319. X    if (! gr_close ()) {
  320. X        fprintf (stderr, "%s: cannot rewrite group file\n", Prog);
  321. X        exit (1);
  322. X    }
  323. X    (void) gr_unlock ();
  324. X#ifdef    SHADOWGRP
  325. X    if (! sgr_close ()) {
  326. X        fprintf (stderr, "%s: cannot rewrite shadow group file\n",
  327. X            Prog);
  328. X        exit (1);
  329. X    }
  330. X    (void) sgr_unlock ();
  331. X#endif    /* SHADOWGRP */
  332. X}
  333. X
  334. X/*
  335. X * open_files - lock and open the group files
  336. X *
  337. X *    open_files() opens the two group files.
  338. X */
  339. X
  340. Xopen_files ()
  341. X{
  342. X    if (! gr_lock ()) {
  343. X        fprintf (stderr, "%s: unable to lock group file\n", Prog);
  344. X        exit (1);
  345. X    }
  346. X    if (! gr_open (O_RDWR)) {
  347. X        fprintf (stderr, "%s: unable to open group file\n", Prog);
  348. X        exit (1);
  349. X    }
  350. X#ifdef    SHADOWGRP
  351. X    if (! sgr_lock ()) {
  352. X        fprintf (stderr, "%s: unable to lock shadow group file\n",
  353. X            Prog);
  354. X        exit (1);
  355. X    }
  356. X    if (! sgr_open (O_RDWR)) {
  357. X        fprintf (stderr, "%s: unable to open shadow group file\n",
  358. X            Prog);
  359. X        exit (1);
  360. X    }
  361. X#endif    /* SHADOWGRP */
  362. X}
  363. X
  364. X/*
  365. X * main - useradd command
  366. X */
  367. X
  368. Xmain (argc, argv)
  369. Xint    argc;
  370. Xchar    **argv;
  371. X{
  372. X
  373. X    /*
  374. X     * Get my name so that I can use it to report errors.
  375. X     */
  376. X
  377. X    if (Prog = strrchr (argv[0], '/'))
  378. X        Prog++;
  379. X    else
  380. X        Prog = argv[0];
  381. X
  382. X#ifdef    USE_SYSLOG
  383. X    openlog (Prog, LOG_PID|LOG_CONS|LOG_NOWAIT, LOG_AUTH);
  384. X#endif    /* USE_SYSLOG */
  385. X
  386. X    /*
  387. X     * The open routines for the DBM files don't use read-write
  388. X     * as the mode, so we have to clue them in.
  389. X     */
  390. X
  391. X#ifdef    NDBM
  392. X    gr_dbm_mode = O_RDWR;
  393. X#ifdef    SHADOWGRP
  394. X    sg_dbm_mode = O_RDWR;
  395. X#endif    /* SHADOWGRP */
  396. X#endif    /* NDBM */
  397. X    process_flags (argc, argv);
  398. X
  399. X    /*
  400. X     * Start with a quick check to see if the group exists.
  401. X     */
  402. X
  403. X    if (getgrnam (group_name)) {
  404. X        fprintf (stderr, "%s: group %s exists\n", Prog, group_name);
  405. X        exit (9);
  406. X    }
  407. X
  408. X    /*
  409. X     * Do the hard stuff - open the files, create the group entries,
  410. X     * then close and update the files.
  411. X     */
  412. X
  413. X    open_files ();
  414. X
  415. X    grp_update ();
  416. X
  417. X    close_files ();
  418. X    exit (0);
  419. X    /*NOTREACHED*/
  420. X}
  421. END_OF_FILE
  422.   if test 7395 -ne `wc -c <'groupadd.c'`; then
  423.     echo shar: \"'groupadd.c'\" unpacked with wrong size!
  424.   fi
  425.   # end of 'groupadd.c'
  426. fi
  427. if test -f 'passwd.c' -a "${1}" != "-c" ; then 
  428.   echo shar: Will not clobber existing file \"'passwd.c'\"
  429. else
  430.   echo shar: Extracting \"'passwd.c'\" \(18487 characters\)
  431.   sed "s/^X//" >'passwd.c' <<'END_OF_FILE'
  432. X/*
  433. X * Copyright 1989, 1990, 1991, John F. Haugh II
  434. X * All rights reserved.
  435. X *
  436. X * Permission is granted to copy and create derivative works for any
  437. X * non-commercial purpose, provided this copyright notice is preserved
  438. X * in all copies of source code, or included in human readable form
  439. X * and conspicuously displayed on all copies of object code or
  440. X * distribution media.
  441. X */
  442. X
  443. X#include <sys/types.h>
  444. X#include <time.h>
  445. X#include <stdio.h>
  446. X#include <fcntl.h>
  447. X#include <signal.h>
  448. X
  449. X#ifndef    lint
  450. Xstatic    char    sccsid[] = "@(#)passwd.c    3.6    08:43:55    9/12/91";
  451. X#endif
  452. X
  453. X/*
  454. X * Set up some BSD defines so that all the BSD ifdef's are
  455. X * kept right here 
  456. X */
  457. X
  458. X#ifndef    BSD
  459. X#include <string.h>
  460. X#include <memory.h>
  461. X#define    bzero(a,n)    memset(a, 0, n)
  462. X#else
  463. X#include <strings.h>
  464. X#define    strchr    index
  465. X#define    strrchr    rindex
  466. X#endif
  467. X
  468. X#include "config.h"
  469. X#include "pwd.h"
  470. X#include "lastlog.h"
  471. X#include "shadow.h"
  472. X
  473. X#ifdef    USE_SYSLOG
  474. X#include <syslog.h>
  475. X
  476. X#ifndef    LOG_WARN
  477. X#define    LOG_WARN    LOG_WARNING
  478. X#endif
  479. X#endif
  480. X
  481. X/*
  482. X * Password aging constants
  483. X *
  484. X *    DAY - seconds in a day
  485. X *    WEEK - seconds in a week
  486. X *    SCALE - convert from clock to aging units
  487. X */
  488. X
  489. X#define    DAY    (24L*3600L)
  490. X#define    WEEK    (7L*DAY)
  491. X
  492. X#ifdef    ITI_AGING
  493. X#define    SCALE    (1)
  494. X#else
  495. X#define    SCALE    DAY
  496. X#endif
  497. X
  498. X/*
  499. X * Global variables
  500. X */
  501. X
  502. Xchar    name[32];        /* The user's name */
  503. Xchar    *Prog;            /* Program name */
  504. Xint    amroot;            /* The real UID was 0 */
  505. X
  506. X/*
  507. X * External identifiers
  508. X */
  509. X
  510. Xextern    char    *getpass();
  511. Xextern    char    *pw_encrypt();
  512. Xextern    char    *getlogin();
  513. Xextern    int    optind;        /* Index into argv[] for current option */
  514. Xextern    char    *optarg;    /* Pointer to current option value */
  515. X#ifdef    NDBM
  516. Xextern    int    sp_dbm_mode;
  517. Xextern    int    pw_dbm_mode;
  518. X#endif
  519. X
  520. X/*
  521. X * #defines for messages.  This facilities foreign language conversion
  522. X * since all messages are defined right here.
  523. X */
  524. X
  525. X#define    USAGE        "usage: %s [ -f | -s ] [ name ]\n"
  526. X#define    ADMUSAGE \
  527. X    "       %s [ -x max ] [ -n min ] [ -w warn ] [ -i inact ] name\n"
  528. X#define    ADMUSAGE2 \
  529. X    "       %s { -l | -d | -S } name\n"
  530. X#define    OLDPASS        "Old Password:"
  531. X#define    NEWPASSMSG \
  532. X"Enter the new password (minimum of 5 characters)\n\
  533. XPlease use a combination of upper and lower case letters and numbers.\n"
  534. X#define    CHANGING    "Changing password for %s\n"
  535. X#define NEWPASS        "New Password:"
  536. X#define    NEWPASS2    "Re-enter new password:"
  537. X#define    WRONGPWD    "Incorrect password for %s.\n"
  538. X#define    WRONGPWD2    "incorrect password for `%s'\n"
  539. X#define    NOMATCH        "They don't match; try again.\n"
  540. X#define    CANTCHANGE    "The password for %s cannot be changed.\n"
  541. X#define    CANTCHANGE2    "password locked for `%s'\n"
  542. X#define    TOOSOON        "Sorry, the password for %s cannot be changed yet.\n"
  543. X#define    TOOSOON2    "now < sp_min for `%s'\n"
  544. X#define    EXECFAILED    "%s: Cannot execute %s"
  545. X#define    EXECFAILED2    "cannot execute %s\n"
  546. X#define    WHOAREYOU    "%s: Cannot determine you user name.\n"
  547. X#define    UNKUSER        "%s: Unknown user %s\n"
  548. X#define    NOPERM        "You may not change the password for %s.\n"
  549. X#define    NOPERM2        "can't change pwd for `%s'\n"
  550. X#define    UNCHANGED    "The password for %s is unchanged.\n"
  551. X#define    SPWDBUSY    "Cannot lock the password file; try again later.\n"
  552. X#define    SPWDBUSY2    "can't lock /etc/shadow\n"
  553. X#define    OPNERROR    "Cannot open the password file.\n"
  554. X#define    OPNERROR2    "can't open /etc/shadow\n"
  555. X#define    UPDERROR    "Error updating the password entry.\n"
  556. X#define    UPDERROR2    "error updating shadow entry\n"
  557. X#define    DBMERROR    "Error updating the DBM password entry.\n"
  558. X#define    DBMERROR2    "error updating DBM shadow entry.\n"
  559. X#define    NOTROOT        "Cannot change ID to root.\n"
  560. X#define    NOTROOT2    "can't setuid(0).\n"
  561. X#define    CLSERROR    "Cannot commit shadow file changes.\n"
  562. X#define    CLSERROR2    "can't rewrite /etc/shadow.\n"
  563. X#define    UNLKERROR    "Cannot unlock the shadow file.\n"
  564. X#define    UNLKERROR2    "can't unlock /etc/shadow.\n"
  565. X#define    TRYAGAIN    "Try again.\n"
  566. X#define    CHGPASSWD    "changed password for `%s'\n"
  567. X
  568. X/*
  569. X * usage - print command usage and exit
  570. X */
  571. X
  572. Xvoid
  573. Xusage ()
  574. X{
  575. X    fprintf (stderr, USAGE, Prog);
  576. X    if (amroot) {
  577. X        fprintf (stderr, ADMUSAGE, Prog);
  578. X        fprintf (stderr, ADMUSAGE2, Prog);
  579. X    }
  580. X    exit (1);
  581. X}
  582. X
  583. X/*
  584. X * new_password - validate old password and replace with new
  585. X */
  586. X
  587. X/*ARGSUSED*/
  588. Xint
  589. Xnew_password (pw, sp)
  590. Xstruct    passwd    *pw;
  591. Xstruct    spwd    *sp;
  592. X{
  593. X    char    *clear;        /* Pointer to clear text */
  594. X    char    *cipher;    /* Pointer to cipher text */
  595. X    char    *cp;        /* Pointer to getpass() response */
  596. X    char    orig[BUFSIZ];    /* Original password */
  597. X    char    pass[BUFSIZ];    /* New password */
  598. X    int    i;        /* Counter for retries */
  599. X
  600. X    /*
  601. X     * Authenticate the user.  The user will be prompted for their
  602. X     * own password.
  603. X     */
  604. X
  605. X    if (! amroot && sp->sp_pwdp[0]) {
  606. X        bzero (orig, sizeof orig);
  607. X
  608. X        if (! (clear = getpass (OLDPASS)))
  609. X            return -1;
  610. X
  611. X        cipher = pw_encrypt (clear, sp->sp_pwdp);
  612. X        if (strcmp (cipher, sp->sp_pwdp) != 0) {
  613. X            sleep (1);
  614. X            fprintf (stderr, WRONGPWD, sp->sp_namp);
  615. X#ifdef    USE_SYSLOG
  616. X            syslog (LOG_WARN, WRONGPWD2, sp->sp_namp);
  617. X#endif
  618. X            return -1;
  619. X        }
  620. X        strcpy (orig, clear);
  621. X        bzero (cipher, strlen (cipher));
  622. X        bzero (clear, strlen (clear));
  623. X    }
  624. X
  625. X    /*
  626. X     * Get the new password.  The user is prompted for the new password
  627. X     * and has three tries to get it right.  The password will be tested
  628. X     * for strength, unless it is the root user.  This provides an escape
  629. X     * for initial login passwords.
  630. X     */
  631. X
  632. X    printf (NEWPASSMSG);
  633. X    for (i = 0;i < 3;i++) {
  634. X        if (! (cp = getpass (NEWPASS))) {
  635. X            bzero (orig, sizeof orig);
  636. X            return -1;
  637. X        } else
  638. X            strcpy (pass, cp);
  639. X
  640. X        if (! amroot && ! obscure (orig, pass)) {
  641. X            printf (TRYAGAIN);
  642. X            continue;
  643. X        }
  644. X        if (! (cp = getpass (NEWPASS2))) {
  645. X            bzero (orig, sizeof orig);
  646. X            return -1;
  647. X        }
  648. X        if (strcmp (cp, pass))
  649. X            fprintf (stderr, NOMATCH);
  650. X        else {
  651. X            bzero (cp, strlen (cp));
  652. X            break;
  653. X        }
  654. X    }
  655. X    bzero (orig, sizeof orig);
  656. X
  657. X    if (i == 3) {
  658. X        bzero (pass, sizeof pass);
  659. X        return -1;
  660. X    }
  661. X
  662. X    /*
  663. X     * Encrypt the password.  The new password is encrypted and
  664. X     * the shadow password structure updated to reflect the change.
  665. X     */
  666. X
  667. X    sp->sp_pwdp = pw_encrypt (pass, (char *) 0);
  668. X    sp->sp_lstchg = time ((time_t *) 0) / SCALE;
  669. X    bzero (pass, sizeof pass);
  670. X
  671. X    return 0;
  672. X}
  673. X
  674. X/*
  675. X * check_password - test a password to see if it can be changed
  676. X *
  677. X *    check_password() sees if the invoker has permission to change the
  678. X *    password for the given user.
  679. X */
  680. X
  681. X/*ARGSUSED*/
  682. Xvoid
  683. Xcheck_password (pw, sp)
  684. Xstruct    passwd    *pw;
  685. Xstruct    spwd    *sp;
  686. X{
  687. X    time_t    now = time ((time_t *) 0) / SCALE;
  688. X
  689. X    /*
  690. X     * Root can change any password any time.
  691. X     */
  692. X
  693. X    if (amroot)
  694. X        return;
  695. X
  696. X    /*
  697. X     * Expired accounts cannot be changed ever.  Passwords
  698. X     * which are locked may not be changed.  Passwords where
  699. X     * min > max may not be changed.  Passwords which have
  700. X     * been inactive too long cannot be changed.
  701. X     */
  702. X
  703. X    if ((sp->sp_expire > 0 && now >= sp->sp_expire) ||
  704. X        (sp->sp_inact >= 0 && sp->sp_max >= 0 &&
  705. X        now >= (sp->sp_lstchg + sp->sp_inact + sp->sp_max)) ||
  706. X            strcmp (sp->sp_pwdp, "!") == 0 ||
  707. X            sp->sp_min > sp->sp_max) {
  708. X        fprintf (stderr, CANTCHANGE, sp->sp_namp);
  709. X#ifdef    USE_SYSLOG
  710. X        syslog (LOG_WARN, CANTCHANGE2, sp->sp_namp);
  711. X        closelog ();
  712. X#endif
  713. X        exit (1);
  714. X    }
  715. X
  716. X    /*
  717. X     * Passwords may only be changed after sp_min time is up.
  718. X     */
  719. X
  720. X    if (sp->sp_min >= 0 && now < (sp->sp_lstchg + sp->sp_min)) {
  721. X        fprintf (stderr, TOOSOON, sp->sp_namp);
  722. X#ifdef    USE_SYSLOG
  723. X        syslog (LOG_WARN, TOOSOON2, sp->sp_namp);
  724. X        closelog ();
  725. X#endif
  726. X        exit (1);
  727. X    }
  728. X}
  729. X
  730. X/*
  731. X * pwd_to_spwd - create entries for new spwd structure
  732. X *
  733. X *    pwd_to_spwd() creates a new (struct spwd) containing the
  734. X *    information in the pointed-to (struct passwd).
  735. X */
  736. X
  737. Xvoid
  738. Xpwd_to_spwd (pw, sp)
  739. Xstruct    passwd    *pw;
  740. Xstruct    spwd    *sp;
  741. X{
  742. X    time_t    t;
  743. X
  744. X    /*
  745. X     * Nice, easy parts first.  The name and passwd map directly
  746. X     * from the old password structure to the new one.
  747. X     */
  748. X
  749. X    sp->sp_namp = strdup (pw->pw_name);
  750. X    sp->sp_pwdp = strdup (pw->pw_passwd);
  751. X#ifdef    ATT_AGE
  752. X
  753. X    /*
  754. X     * AT&T-style password aging maps the sp_min, sp_max, and
  755. X     * sp_lstchg information from the pw_age field, which appears
  756. X     * after the encrypted password.
  757. X     */
  758. X
  759. X    if (pw->pw_age[0]) {
  760. X        t = (c64i (pw->pw_age[0]) * WEEK) / SCALE;
  761. X        sp->sp_max = t;
  762. X
  763. X        if (pw->pw_age[1]) {
  764. X            t = (c64i (pw->pw_age[1]) * WEEK) / SCALE;
  765. X            sp->sp_min = t;
  766. X        } else
  767. X            sp->sp_min = (10000L * DAY) / SCALE;
  768. X
  769. X        if (pw->pw_age[1] && pw->pw_age[2]) {
  770. X            t = (a64l (pw->pw_age + 2) * WEEK) / SCALE;
  771. X            sp->sp_lstchg = t;
  772. X        } else
  773. X            sp->sp_lstchg = time ((time_t *) 0) / SCALE;
  774. X    } else {
  775. X        sp->sp_min = 0;
  776. X        sp->sp_max = (10000L * DAY) / SCALE;
  777. X        sp->sp_lstchg = time ((time_t *) 0) / SCALE;
  778. X    }
  779. X#else
  780. X    /*
  781. X     * BSD does not use the pw_age field and has no aging information
  782. X     * anywheres.  The default values are used to initialize the
  783. X     * fields which are in the missing pw_age field;
  784. X     */
  785. X
  786. X    sp->sp_min = 0;
  787. X    sp->sp_max = (10000L * DAY) / SCALE;
  788. X    sp->sp_lstchg = time ((time_t *) 0) / SCALE;
  789. X#endif
  790. X
  791. X    /*
  792. X     * These fields have no corresponding information in the password
  793. X     * file.  They are set to uninitialized values.
  794. X     */
  795. X
  796. X    sp->sp_warn = -1;
  797. X    sp->sp_inact = -1;
  798. X    sp->sp_expire = -1;
  799. X    sp->sp_flag = -1;
  800. X}
  801. X
  802. X/*
  803. X * print_status - print current password status
  804. X */
  805. X
  806. X/*ARGSUSED*/
  807. Xvoid
  808. Xprint_status (pw, sp)
  809. Xstruct    passwd    *pw;
  810. Xstruct    spwd    *sp;
  811. X{
  812. X    struct    tm    *tm;
  813. X    time_t    last_time;
  814. X
  815. X    last_time = sp->sp_lstchg * SCALE;
  816. X    tm = gmtime (&last_time);
  817. X
  818. X    printf ("%s ", sp->sp_namp);
  819. X    printf ("%s ",
  820. X        sp->sp_pwdp[0] ? (sp->sp_pwdp[0] == '!' ? "L":"P"):"NP");
  821. X    printf ("%02.2d/%02.2d/%02.2d ",
  822. X        tm->tm_mon + 1, tm->tm_mday, tm->tm_year % 100);
  823. X    printf ("%d %d %d %d\n",
  824. X        (sp->sp_min * SCALE) / DAY, (sp->sp_max * SCALE) / DAY,
  825. X        (sp->sp_warn * SCALE) / DAY, (sp->sp_inact * SCALE) / DAY);
  826. X}
  827. X
  828. X/*
  829. X * passwd - change a user's password file information
  830. X *
  831. X *    This command controls the password file and commands which are
  832. X *     used to modify it.
  833. X *
  834. X *    The valid options are
  835. X *
  836. X *    -l    lock the named account (*)
  837. X *    -d    delete the password for the named account (*)
  838. X *    -x #    set sp_max to # days (*)
  839. X *    -n #    set sp_min to # days (*)
  840. X *    -w #    set sp_warn to # days (*)
  841. X *    -i #    set sp_inact to # days (*)
  842. X *    -S    show password status of named account (*)
  843. X *    -g    execute gpasswd command to interpret flags
  844. X *    -f    execute chfn command to interpret flags
  845. X *    -s    execute chsh command to interpret flags
  846. X *
  847. X *    (*) requires root permission to execute.
  848. X *
  849. X *    All of the time fields are entered in days and converted to the
  850. X *     appropriate internal format.  For finer resolute the chage
  851. X *    command must be used.
  852. X */
  853. X
  854. Xint
  855. Xmain (argc, argv)
  856. Xint    argc;
  857. Xchar    **argv;
  858. X{
  859. X    char    buf[BUFSIZ];        /* I/O buffer for messages, etc.      */
  860. X    char    *cp;            /* Miscellaneous character pointing   */
  861. X    time_t    min;            /* Minimum days before change         */
  862. X    time_t    max;            /* Maximum days until change          */
  863. X    time_t    warn;            /* Warning days before change         */
  864. X    time_t    inact;            /* Days without change before locked  */
  865. X    int    i;            /* Loop control variable              */
  866. X    int    flag;            /* Current option to process          */
  867. X    int    lflg = 0;        /* -l - lock account option           */
  868. X    int    dflg = 0;        /* -d - delete password option        */
  869. X    int    xflg = 0;        /* -x - set maximum days              */
  870. X    int    nflg = 0;        /* -n - set minimum days              */
  871. X    int    wflg = 0;        /* -w - set warning days              */
  872. X    int    iflg = 0;        /* -i - set inactive days             */
  873. X    int    Sflg = 0;        /* -S - show password status          */
  874. X    struct    passwd    *pw;        /* Password file entry for user       */
  875. X    struct    spwd    *sp;        /* Shadow file entry for user         */
  876. X    struct    spwd    tspwd;        /* New shadow file entry if none      */
  877. X
  878. X    /*
  879. X     * The program behaves differently when executed by root
  880. X     * than when executed by a normal user.
  881. X     */
  882. X
  883. X    amroot = getuid () == 0;
  884. X#ifdef    NDBM
  885. X    sp_dbm_mode = O_RDWR;
  886. X    pw_dbm_mode = O_RDWR;
  887. X#endif
  888. X
  889. X    /*
  890. X     * Get the program name.  The program name is used as a
  891. X     * prefix to most error messages.  It is also used as input
  892. X     * to the openlog() function for error logging.
  893. X     */
  894. X
  895. X    if (Prog = strrchr (argv[0], '/'))
  896. X        Prog++;
  897. X    else
  898. X        Prog = argv[0];
  899. X
  900. X#ifdef    USE_SYSLOG
  901. X    openlog (Prog, LOG_PID|LOG_CONS|LOG_NOWAIT, LOG_AUTH);
  902. X#endif
  903. X
  904. X    /*
  905. X     * Start with the flags which cause another command to be
  906. X     * executed.  The effective UID will be set back to the
  907. X     * real UID and the new command executed with the flags
  908. X     */
  909. X
  910. X    if (argc > 1 && argv[1][0] == '-' && strchr ("gfs", argv[1][1])) {
  911. X        setuid (getuid ());
  912. X        switch (argv[1][1]) {
  913. X            case 'g':
  914. X                argv[1] = "gpasswd";
  915. X                execv ("/bin/gpasswd", &argv[1]);
  916. X                break;
  917. X            case 'f':
  918. X                argv[1] = "chfn";
  919. X                execv ("/bin/chfn", &argv[1]);
  920. X                break;
  921. X            case 's':
  922. X                argv[1] = "chsh";
  923. X                execv ("/bin/chsh", &argv[1]);
  924. X                break;
  925. X            default:
  926. X                usage ();
  927. X        }
  928. X        sprintf (buf, EXECFAILED, Prog, argv[1]);
  929. X        perror (buf);
  930. X#ifdef    USE_SYSLOG
  931. X        syslog (LOG_CRIT, EXECFAILED2, argv[1]);
  932. X        closelog ();
  933. X#endif
  934. X        exit (1);
  935. X    }
  936. X
  937. X    /* 
  938. X     * The remaining arguments will be processed one by one and
  939. X     * executed by this command.  The name is the last argument
  940. X     * if it does not begin with a "-", otherwise the name is
  941. X     * determined from the environment and must agree with the
  942. X     * real UID.  Also, the UID will be checked for any commands
  943. X     * which are restricted to root only.
  944. X     */
  945. X
  946. X    while ((flag = getopt (argc, argv, "ldx:n:w:i:S")) != EOF) {
  947. X        switch (flag) {
  948. X            case 'x':
  949. X                max = strtol (optarg, &cp, 10);
  950. X                if (*cp || getuid ())
  951. X                    usage ();
  952. X
  953. X                xflg++;
  954. X                break;
  955. X            case 'n':
  956. X                min = strtol (optarg, &cp, 10);
  957. X                if (*cp || getuid ())
  958. X                    usage ();
  959. X
  960. X                nflg++;
  961. X                break;
  962. X            case 'w':
  963. X                warn = strtol (optarg, &cp, 10);
  964. X                if (*cp || getuid ())
  965. X                    usage ();
  966. X
  967. X                wflg++;
  968. X                break;
  969. X            case 'i':
  970. X                inact = strtol (optarg, &cp, 10);
  971. X                if (*cp || getuid ())
  972. X                    usage ();
  973. X
  974. X                iflg++;
  975. X                break;
  976. X            case 'S':
  977. X                if (getuid ())
  978. X                    usage ();
  979. X
  980. X                Sflg++;
  981. X                break;
  982. X            case 'd':
  983. X                dflg++;
  984. X                break;
  985. X            case 'l':
  986. X                lflg++;
  987. X                break;
  988. X            default:
  989. X                usage ();
  990. X        }
  991. X    }
  992. X
  993. X    /*
  994. X     * If any of the flags were given, a user name must be supplied
  995. X     * on the command line.  Only an unadorned command line doesn't
  996. X     * require the user's name be given.  Also, on -x, -n, -m, and
  997. X     * -i may appear with each other.  -d, -l and -S must appear alone.
  998. X     */
  999. X
  1000. X    if ((dflg || lflg || xflg || nflg ||
  1001. X                wflg || iflg || Sflg) && optind >= argc)
  1002. X        usage ();
  1003. X
  1004. X    if ((dflg + lflg + (xflg || nflg || wflg || iflg) + Sflg) > 1)
  1005. X        usage ();
  1006. X
  1007. X    /*
  1008. X     * Now I have to get the user name.  The name will be gotten 
  1009. X     * from the command line if possible.  Otherwise it is figured
  1010. X     * out from the environment.
  1011. X     */
  1012. X
  1013. X    if (optind < argc) {
  1014. X        strncpy (name, argv[optind], sizeof name);
  1015. X        name[sizeof name - 1] = '\0';
  1016. X    } else if (amroot) {
  1017. X        strcpy (name, "root");
  1018. X    } else if (cp = getlogin ()) {
  1019. X        strncpy (name, cp, sizeof name);
  1020. X        name[sizeof name - 1] = '\0';
  1021. X    } else {
  1022. X        fprintf (stderr, WHOAREYOU, Prog);
  1023. X#ifdef    USE_SYSLOG
  1024. X        closelog ();
  1025. X#endif
  1026. X        exit (1);
  1027. X    }
  1028. X
  1029. X    /*
  1030. X     * Now I have a name, let's see if the UID for the name
  1031. X     * matches the current real UID.
  1032. X     */
  1033. X
  1034. X    if (! (pw = getpwnam (name))) {
  1035. X        fprintf (stderr, UNKUSER, Prog, name);
  1036. X#ifdef    USE_SYSLOG
  1037. X        closelog ();
  1038. X#endif
  1039. X        exit (1);
  1040. X    }
  1041. X    if (! amroot && pw->pw_uid != getuid ()) {
  1042. X        fprintf (stderr, NOPERM, name);
  1043. X#ifdef    USE_SYSLOG
  1044. X        syslog (LOG_WARN, NOPERM2, name);
  1045. X        closelog ();
  1046. X#endif
  1047. X        exit (1);
  1048. X    }
  1049. X
  1050. X    /*
  1051. X     * Let the user know whose password is being changed.
  1052. X     */
  1053. X
  1054. X    if (! Sflg)
  1055. X        printf (CHANGING, name);
  1056. X
  1057. X    /*
  1058. X     * The user name is valid, so let's get the shadow file
  1059. X     * entry.
  1060. X     */
  1061. X
  1062. X    if (! (sp = getspnam (name)))
  1063. X        pwd_to_spwd (pw, sp = &tspwd);
  1064. X
  1065. X    /*
  1066. X     * Save the shadow entry off to the side so it doesn't
  1067. X     * get changed by any of the following code.
  1068. X     */
  1069. X
  1070. X    if (sp != &tspwd) {
  1071. X        tspwd = *sp;
  1072. X        sp = &tspwd;
  1073. X    }
  1074. X    tspwd.sp_namp = strdup (sp->sp_namp);
  1075. X    tspwd.sp_pwdp = strdup (sp->sp_pwdp);
  1076. X
  1077. X    if (Sflg) {
  1078. X        print_status (pw, sp);
  1079. X#ifdef    USE_SYSLOG
  1080. X        closelog ();
  1081. X#endif
  1082. X        exit (0);
  1083. X    }
  1084. X
  1085. X    /*
  1086. X     * If there are no other flags, just change the password.
  1087. X     */
  1088. X
  1089. X    if (! (dflg || lflg || xflg || nflg || wflg || iflg)) {
  1090. X
  1091. X        /*
  1092. X         * See if the user is permitted to change the password.
  1093. X         * Otherwise, go ahead and set a new password.
  1094. X         */
  1095. X
  1096. X        check_password (pw, sp);
  1097. X
  1098. X        if (new_password (pw, sp)) {
  1099. X            fprintf (stderr, UNCHANGED, name);
  1100. X#ifdef    USE_SYSLOG
  1101. X            closelog ();
  1102. X#endif
  1103. X            exit (1);
  1104. X        }
  1105. X    }
  1106. X
  1107. X    /*
  1108. X     * The other options are incredibly simple.  Just modify the
  1109. X     * field in the shadow file entry.
  1110. X     */
  1111. X
  1112. X    if (dflg)            /* Set password to blank */
  1113. X        sp->sp_pwdp = "";
  1114. X
  1115. X    if (lflg)            /* Set password to "locked" value */
  1116. X        sp->sp_pwdp = "!";
  1117. X
  1118. X    if (xflg)
  1119. X        sp->sp_max = (max * DAY) / SCALE;
  1120. X
  1121. X    if (nflg)
  1122. X        sp->sp_min = (min * DAY) / SCALE;
  1123. X
  1124. X    if (wflg)
  1125. X        sp->sp_warn = (warn * DAY) / SCALE;
  1126. X
  1127. X    if (iflg)
  1128. X        sp->sp_inact = (inact * DAY) / SCALE;
  1129. X
  1130. X    /*
  1131. X     * Before going any further, raise the ulimit to prevent
  1132. X     * colliding into a lowered ulimit, and set the real UID
  1133. X     * to root to protect against unexpected signals.  Any
  1134. X     * keyboard signals are set to be ignored.
  1135. X     */
  1136. X
  1137. X    ulimit (2, 30000);
  1138. X    if (setuid (0)) {
  1139. X        fprintf (stderr, NOTROOT);
  1140. X#ifdef    USE_SYSLOG
  1141. X        syslog (LOG_ERR, NOTROOT2);
  1142. X        closelog ();
  1143. X#endif
  1144. X        exit (1);
  1145. X    }
  1146. X    signal (SIGHUP, SIG_IGN);
  1147. X    signal (SIGINT, SIG_IGN);
  1148. X    signal (SIGQUIT, SIG_IGN);
  1149. X#ifdef    SIGTSTP
  1150. X    signal (SIGTSTP, SIG_IGN);
  1151. X#endif
  1152. X
  1153. X    /*
  1154. X     * The shadow entry is now ready to be committed back to
  1155. X     * the shadow file.  Get a lock on the file and open it.
  1156. X     */
  1157. X
  1158. X    for (i = 0;i < 30;i++)
  1159. X        if (spw_lock ())
  1160. X            break;
  1161. X
  1162. X    if (i == 30) {
  1163. X        fprintf (stderr, SPWDBUSY);
  1164. X#ifdef    USE_SYSLOG
  1165. X        syslog (LOG_WARN, SPWDBUSY2);
  1166. X        closelog ();
  1167. X#endif
  1168. X        exit (1);
  1169. X    }
  1170. X    if (! spw_open (O_RDWR)) {
  1171. X        fprintf (stderr, OPNERROR);
  1172. X#ifdef    USE_SYSLOG
  1173. X        syslog (LOG_ERR, OPNERROR2);
  1174. X        closelog ();
  1175. X#endif
  1176. X        (void) spw_unlock ();
  1177. X        exit (1);
  1178. X    }
  1179. X
  1180. X    /*
  1181. X     * Update the shadow file entry.  If there is a DBM file,
  1182. X     * update that entry as well.
  1183. X     */
  1184. X
  1185. X    if (! spw_update (sp)) {
  1186. X        fprintf (stderr, UPDERROR);
  1187. X#ifdef    USE_SYSLOG
  1188. X        syslog (LOG_ERR, UPDERROR2);
  1189. X        closelog ();
  1190. X#endif
  1191. X        (void) spw_unlock ();
  1192. X        exit (1);
  1193. X    }
  1194. X#ifdef    NDBM
  1195. X    if (access ("/etc/shadow.pag", 0) == 0 && ! sp_dbm_update (sp)) {
  1196. X        fprintf (stderr, DBMERROR);
  1197. X#ifdef    USE_SYSLOG
  1198. X        syslog (LOG_ERR, DBMERROR2);
  1199. X        closelog ();
  1200. X#endif
  1201. X        (void) spw_unlock ();
  1202. X        exit (1);
  1203. X    }
  1204. X    endspent ();
  1205. X#endif
  1206. X
  1207. X    /*
  1208. X     * Changes have all been made, so commit them and unlock the
  1209. X     * file.
  1210. X     */
  1211. X
  1212. X    if (! spw_close ()) {
  1213. X        fprintf (stderr, CLSERROR);
  1214. X#ifdef    USE_SYSLOG
  1215. X        syslog (LOG_ERR, CLSERROR2);
  1216. X        closelog ();
  1217. X#endif
  1218. X        (void) spw_unlock ();
  1219. X        exit (1);
  1220. X    }
  1221. X    if (! spw_unlock ()) {
  1222. X        fprintf (stderr, UNLKERROR);
  1223. X#ifdef    USE_SYSLOG
  1224. X        syslog (LOG_ERR, UNLKERROR2);
  1225. X        closelog ();
  1226. X#endif
  1227. X        exit (1);
  1228. X    }
  1229. X#ifdef    USE_SYSLOG
  1230. X    syslog (LOG_INFO, CHGPASSWD, name);
  1231. X    closelog ();
  1232. X#endif
  1233. X    exit (0);
  1234. X    /*NOTREACHED*/
  1235. X}
  1236. END_OF_FILE
  1237.   if test 18487 -ne `wc -c <'passwd.c'`; then
  1238.     echo shar: \"'passwd.c'\" unpacked with wrong size!
  1239.   fi
  1240.   # end of 'passwd.c'
  1241. fi
  1242. if test -f 'usermod.c' -a "${1}" != "-c" ; then 
  1243.   echo shar: Will not clobber existing file \"'usermod.c'\"
  1244. else
  1245.   echo shar: Extracting \"'usermod.c'\" \(24732 characters\)
  1246.   sed "s/^X//" >'usermod.c' <<'END_OF_FILE'
  1247. X/*
  1248. X * Copyright 1991, John F. Haugh II
  1249. X * All rights reserved.
  1250. X *
  1251. X * Permission is granted to copy and create derivative works for any
  1252. X * non-commercial purpose, provided this copyright notice is preserved
  1253. X * in all copies of source code, or included in human readable form
  1254. X * and conspicuously displayed on all copies of object code or
  1255. X * distribution media.
  1256. X */
  1257. X
  1258. X#ifndef lint
  1259. Xstatic    char    sccsid[] = "@(#)usermod.c    3.5    14:38:40    10/27/91";
  1260. X#endif
  1261. X
  1262. X#include <sys/types.h>
  1263. X#include <sys/stat.h>
  1264. X#include <stdio.h>
  1265. X#include <errno.h>
  1266. X#include "pwd.h"
  1267. X#include <grp.h>
  1268. X#include <ctype.h>
  1269. X#include <fcntl.h>
  1270. X#include <time.h>
  1271. X
  1272. X#ifdef    BSD
  1273. X#include <strings.h>
  1274. X#else
  1275. X#include <string.h>
  1276. X#endif
  1277. X
  1278. X#include "config.h"
  1279. X#include "shadow.h"
  1280. X
  1281. X#ifdef    USE_SYSLOG
  1282. X#include <syslog.h>
  1283. X
  1284. X#ifndef    LOG_WARN
  1285. X#define    LOG_WARN LOG_WARNING
  1286. X#endif
  1287. X#endif
  1288. X
  1289. X#ifndef    NGROUPS_MAX
  1290. X#define    NGROUPS_MAX    64
  1291. X#endif
  1292. X
  1293. X#define    VALID(s)    (strcspn (s, ":\n") == strlen (s))
  1294. X
  1295. Xchar    user_name[BUFSIZ];
  1296. Xchar    user_newname[BUFSIZ];
  1297. Xuid_t    user_id;
  1298. Xuid_t    user_newid;
  1299. Xgid_t    user_gid;
  1300. Xchar    user_comment[BUFSIZ];
  1301. Xchar    user_home[BUFSIZ];
  1302. Xchar    user_newhome[BUFSIZ];
  1303. Xchar    user_shell[BUFSIZ];
  1304. Xlong    user_expire;
  1305. Xlong    user_inactive;
  1306. Xint    user_ngroups = -1;
  1307. Xgid_t    user_groups[NGROUPS_MAX];
  1308. Xstruct    passwd    user_pwd;
  1309. Xstruct    spwd    user_spwd;
  1310. X
  1311. Xchar    *Prog;
  1312. X
  1313. Xint    uflg;    /* specify user ID for new account                            */
  1314. Xint    oflg;    /* permit non-unique user ID to be specified with -u          */
  1315. Xint    gflg;    /* primary group ID  for new account                          */
  1316. Xint    Gflg;    /* secondary group set for new account                        */
  1317. Xint    dflg;    /* home directory for new account                             */
  1318. Xint    sflg;    /* shell program for new account                              */
  1319. Xint    cflg;    /* comment (GECOS) field for new account                      */
  1320. Xint    mflg;    /* create user's home directory if it doesn't exist           */
  1321. Xint    fflg;    /* days until account with expired password is locked         */
  1322. Xint    eflg;    /* days after password changed before it becomes expired      */
  1323. Xint    lflg;    /* new user name for user                                     */
  1324. X
  1325. X#ifdef    NDBM
  1326. Xextern    int    pw_dbm_mode;
  1327. Xextern    int    sp_dbm_mode;
  1328. Xextern    int    gr_dbm_mode;
  1329. X#ifdef    SHADOWGRP
  1330. Xextern    int    sg_dbm_mode;
  1331. X#endif
  1332. X#endif
  1333. Xextern    FILE    *fopen();
  1334. Xextern    int    fclose();
  1335. Xextern    char    *malloc();
  1336. Xextern    char    *mktemp();
  1337. X
  1338. Xextern    struct    group    *getgrnam();
  1339. Xextern    struct    group    *getgrgid();
  1340. Xextern    struct    group    *gr_next();
  1341. Xextern    struct    group    *gr_locate();
  1342. Xextern    int    gr_lock();
  1343. Xextern    int    gr_unlock();
  1344. Xextern    int    gr_rewind();
  1345. Xextern    int    gr_open();
  1346. X
  1347. X#ifdef    SHADOWGRP
  1348. Xextern    struct    sgrp    *sgr_next();
  1349. Xextern    int    sgr_lock();
  1350. Xextern    int    sgr_unlock();
  1351. Xextern    int    sgr_rewind();
  1352. Xextern    int    sgr_open();
  1353. X#endif
  1354. X
  1355. Xextern    struct    passwd    *getpwnam();
  1356. Xextern    struct    passwd    *pw_next();
  1357. Xextern    struct    passwd    *pw_locate();
  1358. Xextern    int    pw_lock();
  1359. Xextern    int    pw_unlock();
  1360. Xextern    int    pw_rewind();
  1361. Xextern    int    pw_open();
  1362. X
  1363. Xextern    int    spw_lock();
  1364. Xextern    int    spw_unlock();
  1365. Xextern    int    spw_open();
  1366. Xextern    struct    spwd    *spw_locate();
  1367. X
  1368. X#define    DAY    (24L*3600L)
  1369. X#define    WEEK    (7*DAY)
  1370. X
  1371. X#ifdef    ITI_AGING
  1372. X#define    SCALE    (1)
  1373. X#else
  1374. X#define    SCALE    (DAY)
  1375. X#endif
  1376. X
  1377. X/*
  1378. X * days and juldays are used to compute the number of days in the
  1379. X * current month, and the cummulative number of days in the preceding
  1380. X * months.  they are declared so that january is 1, not 0.
  1381. X */
  1382. X
  1383. Xstatic    short    days[13] = { 0,
  1384. X    31,    28,    31,    30,    31,    30,    /* JAN - JUN */
  1385. X    31,    31,    30,    31,    30,    31 };    /* JUL - DEC */
  1386. X
  1387. Xstatic    short    juldays[13] = { 0,
  1388. X    0,    31,    59,    90,    120,    151,    /* JAN - JUN */
  1389. X    181,    212,    243,    273,    304,    334 };    /* JUL - DEC */
  1390. X
  1391. X#ifdef    NEED_RENAME
  1392. X/*
  1393. X * rename - rename a file to another name
  1394. X *
  1395. X *    rename is provided for systems which do not include the rename()
  1396. X *    system call.
  1397. X */
  1398. X
  1399. Xint
  1400. Xrename (begin, end)
  1401. Xchar    *begin;
  1402. Xchar    *end;
  1403. X{
  1404. X    struct    stat    s1, s2;
  1405. X    extern    int    errno;
  1406. X    int    orig_err = errno;
  1407. X
  1408. X    if (stat (begin, &s1))
  1409. X        return -1;
  1410. X
  1411. X    if (stat (end, &s2)) {
  1412. X        errno = orig_err;
  1413. X    } else {
  1414. X
  1415. X        /*
  1416. X         * See if this is a cross-device link.  We do this to
  1417. X         * insure that the link below has a chance of working.
  1418. X         */
  1419. X
  1420. X        if (s1.st_dev != s2.st_dev) {
  1421. X            errno = EXDEV;
  1422. X            return -1;
  1423. X        }
  1424. X
  1425. X        /*
  1426. X         * See if we can unlink the existing destination
  1427. X         * file.  If the unlink works the directory is writable,
  1428. X         * so there is no need here to figure that out.
  1429. X         */
  1430. X
  1431. X        if (unlink (end))
  1432. X            return -1;
  1433. X    }
  1434. X
  1435. X    /*
  1436. X     * Now just link the original name to the final name.  If there
  1437. X     * was no file previously, this link will fail if the target
  1438. X     * directory isn't writable.  The unlink will fail if the source
  1439. X     * directory isn't writable, but life stinks ...
  1440. X     */
  1441. X
  1442. X    if (link (begin, end) || unlink (begin))
  1443. X        return -1;
  1444. X
  1445. X    return 0;
  1446. X}
  1447. X#endif
  1448. X
  1449. X/*
  1450. X * strtoday - compute the number of days since 1970.
  1451. X *
  1452. X * the total number of days prior to the current date is
  1453. X * computed.  january 1, 1970 is used as the origin with
  1454. X * it having a day number of 0.
  1455. X */
  1456. X
  1457. Xlong
  1458. Xstrtoday (str)
  1459. Xchar    *str;
  1460. X{
  1461. X    char    slop[2];
  1462. X    int    month;
  1463. X    int    day;
  1464. X    int    year;
  1465. X    long    total;
  1466. X
  1467. X    /*
  1468. X     * start by separating the month, day and year.  this is
  1469. X     * a chauvanistic program - it only takes date input in
  1470. X     * the standard USA format.
  1471. X     */
  1472. X
  1473. X    if (sscanf (str, "%d/%d/%d%c", &month, &day, &year, slop) != 3)
  1474. X        return -1;
  1475. X
  1476. X    /*
  1477. X     * the month, day of the month, and year are checked for
  1478. X     * correctness and the year adjusted so it falls between
  1479. X     * 1970 and 2069.
  1480. X     */
  1481. X
  1482. X    if (month < 1 || month > 12)
  1483. X        return -1;
  1484. X
  1485. X    if (day < 1)
  1486. X        return -1;
  1487. X
  1488. X    if ((month != 2 || (year % 4) != 0) && day > days[month])
  1489. X        return -1;
  1490. X    else if ((month == 2 && (year % 4) == 0) && day > 29)
  1491. X        return -1;
  1492. X
  1493. X    if (year < 0)
  1494. X        return -1;
  1495. X    else if (year < 69)
  1496. X        year += 2000;
  1497. X    else if (year < 99)
  1498. X        year += 1900;
  1499. X
  1500. X    if (year < 1970 || year > 2069)
  1501. X        return -1;
  1502. X
  1503. X    /*
  1504. X     * the total number of days is the total number of days in all
  1505. X     * the whole years, plus the number of leap days, plus the
  1506. X     * number of days in the whole months preceding, plus the number
  1507. X     * of days so far in the month.
  1508. X     */
  1509. X
  1510. X    total = (long) ((year - 1970) * 365L) + (((year + 1) - 1970) / 4);
  1511. X    total += (long) juldays[month] + (month > 2 && (year % 4) == 0 ? 1:0);
  1512. X    total += (long) day - 1;
  1513. X
  1514. X    return total;
  1515. X}
  1516. X
  1517. X/*
  1518. X * add_list - add a member to a list of group members
  1519. X *
  1520. X *    the array of member names is searched for the new member
  1521. X *    name, and if not present it is added to a freshly allocated
  1522. X *    list of users.
  1523. X */
  1524. X
  1525. Xchar **
  1526. Xadd_list (list, member)
  1527. Xchar    **list;
  1528. Xchar    *member;
  1529. X{
  1530. X    int    i;
  1531. X    char    **tmp;
  1532. X
  1533. X    /*
  1534. X     * Scan the list for the new name.  Return the original list
  1535. X     * pointer if it is present.
  1536. X     */
  1537. X
  1538. X    for (i = 0;list[i] != (char *) 0;i++)
  1539. X        if (strcmp (list[i], member) == 0)
  1540. X            return list;
  1541. X
  1542. X    /*
  1543. X     * Allocate a new list pointer large enough to hold all the
  1544. X     * old entries, and the new entries as well.
  1545. X     */
  1546. X
  1547. X    if (! (tmp = (char **) malloc ((i + 2) * sizeof member)))
  1548. X        return 0;
  1549. X
  1550. X    /*
  1551. X     * Copy the original list to the new list, then append the
  1552. X     * new member and NULL terminate the result.  This new list
  1553. X     * is returned to the invoker.
  1554. X     */
  1555. X
  1556. X    for (i = 0;list[i] != (char *) 0;i++)
  1557. X        tmp[i] = list[i];
  1558. X
  1559. X    tmp[i++] = strdup (member);
  1560. X    tmp[i] = (char *) 0;
  1561. X
  1562. X    return tmp;
  1563. X}
  1564. X
  1565. X/*
  1566. X * del_list - delete a member from a list of group members
  1567. X *
  1568. X *    the array of member names is searched for the old member
  1569. X *    name, and if present it is deleted from a freshly allocated
  1570. X *    list of users.
  1571. X */
  1572. X
  1573. Xchar **
  1574. Xdel_list (list, member)
  1575. Xchar    **list;
  1576. Xchar    *member;
  1577. X{
  1578. X    int    i, j;
  1579. X    char    **tmp;
  1580. X
  1581. X    /*
  1582. X     * Scan the list for the new name.  Return the original list
  1583. X     * pointer if it is present.
  1584. X     */
  1585. X
  1586. X    for (i = j = 0;list[i] != (char *) 0;i++)
  1587. X        if (strcmp (list[i], member))
  1588. X            j++;
  1589. X
  1590. X    if (j == i)
  1591. X        return list;
  1592. X
  1593. X    /*
  1594. X     * Allocate a new list pointer large enough to hold all the
  1595. X     * old entries, and the new entries as well.
  1596. X     */
  1597. X
  1598. X    if (! (tmp = (char **) malloc ((j + 2) * sizeof member)))
  1599. X        return 0;
  1600. X
  1601. X    /*
  1602. X     * Copy the original list to the new list, then append the
  1603. X     * new member and NULL terminate the result.  This new list
  1604. X     * is returned to the invoker.
  1605. X     */
  1606. X
  1607. X    for (i = j = 0;list[i] != (char *) 0;i++)
  1608. X        if (strcmp (list[i], member))
  1609. X            tmp[j++] = list[i];
  1610. X
  1611. X    tmp[j] = (char *) 0;
  1612. X
  1613. X    return tmp;
  1614. X}
  1615. X
  1616. X/*
  1617. X * get_groups - convert a list of group names to an array of group IDs
  1618. X *
  1619. X *    get_groups() takes a comma-separated list of group names and
  1620. X *    converts it to an array of group ID values.  Any unknown group
  1621. X *    names are reported as errors.
  1622. X */
  1623. X
  1624. Xint
  1625. Xget_groups (list)
  1626. Xchar    *list;
  1627. X{
  1628. X    char    *cp;
  1629. X    struct    group    *grp;
  1630. X    int    errors = 0;
  1631. X
  1632. X    /*
  1633. X     * Initialize the list to be empty
  1634. X     */
  1635. X
  1636. X    user_ngroups = 0;
  1637. X
  1638. X    if (! *list)
  1639. X        return 0;
  1640. X
  1641. X    /*
  1642. X     * So long as there is some data to be converted, strip off
  1643. X     * each name and look it up.  A mix of numerical and string
  1644. X     * values for group identifiers is permitted.
  1645. X     */
  1646. X
  1647. X    do {
  1648. X        /*
  1649. X         * Strip off a single name from the list
  1650. X         */
  1651. X
  1652. X        if (cp = strchr (list, ','))
  1653. X            *cp++ = '\0';
  1654. X
  1655. X        /*
  1656. X         * Names starting with digits are treated as numerical
  1657. X         * GID values, otherwise the string is looked up as is.
  1658. X         */
  1659. X
  1660. X        if (isdigit (*list))
  1661. X            grp = getgrgid (atoi (list));
  1662. X        else
  1663. X            grp = getgrnam (list);
  1664. X
  1665. X        /*
  1666. X         * There must be a match, either by GID value or by
  1667. X         * string name.
  1668. X         */
  1669. X
  1670. X        if (! grp) {
  1671. X            fprintf (stderr, "%s: unknown group %s\n", Prog, list);
  1672. X            errors++;
  1673. X        }
  1674. X
  1675. X        /*
  1676. X         * Add the GID value from the group file to the user's
  1677. X         * list of groups.
  1678. X         */
  1679. X
  1680. X        user_groups[user_ngroups++] = grp->gr_gid;
  1681. X
  1682. X        list = cp;
  1683. X    } while (list);
  1684. X
  1685. X    /*
  1686. X     * Any errors in finding group names are fatal
  1687. X     */
  1688. X
  1689. X    if (errors)
  1690. X        return -1;
  1691. X
  1692. X    return 0;
  1693. X}
  1694. X
  1695. X/*
  1696. X * usage - display usage message and exit
  1697. X */
  1698. X
  1699. Xusage ()
  1700. X{
  1701. X    fprintf (stderr,
  1702. X        "usage: %s [-u uid [-o]] [-g group] [-G group,...] \n", Prog);
  1703. X    fprintf (stderr,
  1704. X        "\t\t[-d home [-m]] [-s shell] [-c comment] [-l new_name]\n");
  1705. X    fprintf (stderr,
  1706. X        "\t\t[-f inactive] [-e expire] name\n");
  1707. X
  1708. X    exit (2);
  1709. X}
  1710. X
  1711. X/*
  1712. X * new_pwent - initialize the values in a password file entry
  1713. X *
  1714. X *    new_pwent() takes all of the values that have been entered and
  1715. X *    fills in a (struct passwd) with them.
  1716. X */
  1717. X
  1718. Xvoid
  1719. Xnew_pwent (pwent)
  1720. Xstruct    passwd    *pwent;
  1721. X{
  1722. X    if (lflg)
  1723. X        pwent->pw_name = strdup (user_newname);
  1724. X
  1725. X    if (uflg)
  1726. X        pwent->pw_uid = user_newid;
  1727. X
  1728. X    if (gflg)
  1729. X        pwent->pw_gid = user_gid;
  1730. X
  1731. X    if (cflg)
  1732. X        pwent->pw_gecos = strdup (user_comment);
  1733. X
  1734. X    if (dflg)
  1735. X        pwent->pw_dir = strdup (user_newhome);
  1736. X
  1737. X    if (sflg)
  1738. X        pwent->pw_shell = strdup (user_shell);
  1739. X}
  1740. X
  1741. X/*
  1742. X * new_spent - initialize the values in a shadow password file entry
  1743. X *
  1744. X *    new_spent() takes all of the values that have been entered and
  1745. X *    fills in a (struct spwd) with them.
  1746. X */
  1747. X
  1748. Xvoid
  1749. Xnew_spent (spent)
  1750. Xstruct    spwd    *spent;
  1751. X{
  1752. X    if (lflg)
  1753. X        spent->sp_namp = strdup (user_newname);
  1754. X
  1755. X    spent->sp_inact = user_inactive;
  1756. X    spent->sp_expire = user_expire;
  1757. X}
  1758. X
  1759. X/*
  1760. X * grp_update - add user to secondary group set
  1761. X *
  1762. X *    grp_update() takes the secondary group set given in user_groups
  1763. X *    and adds the user to each group given by that set.
  1764. X */
  1765. X
  1766. Xvoid
  1767. Xgrp_update ()
  1768. X{
  1769. X    int    i;
  1770. X    int    is_member;
  1771. X    int    was_member;
  1772. X    int    was_admin;
  1773. X    struct    group    *grp;
  1774. X    struct    sgrp    *sgrp;
  1775. X
  1776. X    /*
  1777. X     * Lock and open the group file.  This will load all of the group
  1778. X     * entries.
  1779. X     */
  1780. X
  1781. X    if (! gr_lock ()) {
  1782. X        fprintf (stderr, "%s: error locking group file\n", Prog);
  1783. X        exit (1);
  1784. X    }
  1785. X    if (! gr_open (O_RDWR)) {
  1786. X        fprintf (stderr, "%s: error opening group file\n", Prog);
  1787. X        exit (1);
  1788. X    }
  1789. X#ifdef    SHADOWGRP
  1790. X    if (! sgr_lock ()) {
  1791. X        fprintf (stderr, "%s: error locking shadow group file\n", Prog);
  1792. X        exit (1);
  1793. X    }
  1794. X    if (! sgr_open (O_RDWR)) {
  1795. X        fprintf (stderr, "%s: error opening shadow group file\n", Prog);
  1796. X        exit (1);
  1797. X    }
  1798. X#endif
  1799. X
  1800. X    /*
  1801. X     * Scan through the entire group file looking for the groups that
  1802. X     * the user is a member of.
  1803. X     */
  1804. X
  1805. X    for (gr_rewind (), grp = gr_next ();grp;grp = gr_next ()) {
  1806. X
  1807. X        /*
  1808. X         * See if the user specified this group as one of their
  1809. X         * concurrent groups.
  1810. X         */
  1811. X
  1812. X        for (i = 0;i < user_ngroups;i++)
  1813. X            if (grp->gr_gid == user_groups[i])
  1814. X                break;
  1815. X
  1816. X        is_member = i == user_ngroups ? -1:i;
  1817. X
  1818. X        for (i = 0;grp->gr_mem[i];i++)
  1819. X            if (strcmp (user_name, grp->gr_mem[i]) == 0)
  1820. X                break;
  1821. X
  1822. X        was_member = grp->gr_mem[i] ? i:-1;
  1823. X
  1824. X        if (is_member == -1 && was_member == -1)
  1825. X            continue;
  1826. X
  1827. X        if (was_member >= 0 && (! Gflg || is_member >= 0)) {
  1828. X            if (lflg) {
  1829. X                grp->gr_mem = del_list (grp->gr_mem,
  1830. X                    user_name);
  1831. X                grp->gr_mem = add_list (grp->gr_mem,
  1832. X                    user_newname);
  1833. X#ifdef    USE_SYSLOG
  1834. X                syslog (LOG_INFO,
  1835. X                    "change `%s' to `%s' in group `%s'\n",
  1836. X                    user_name, user_newname, grp->gr_name);
  1837. X#endif
  1838. X            }
  1839. X        } else if (was_member >= 0 && Gflg && is_member < 0) {
  1840. X            grp->gr_mem = del_list (grp->gr_mem, user_name);
  1841. X#ifdef    USE_SYSLOG
  1842. X            syslog (LOG_INFO, "delete `%s' from group `%s'\n",
  1843. X                user_name, grp->gr_name);
  1844. X#endif
  1845. X        } else if (was_member < 0 && Gflg && is_member >= 0) {
  1846. X            grp->gr_mem = add_list (grp->gr_mem,
  1847. X                lflg ? user_newname:user_name);
  1848. X#ifdef    USE_SYSLOG
  1849. X            syslog (LOG_INFO, "add `%s' to group `%s'\n",
  1850. X                lflg ? user_newname:user_name, grp->gr_name);
  1851. X#endif
  1852. X        } else
  1853. X            continue;
  1854. X
  1855. X        if (! gr_update (grp)) {
  1856. X            fprintf (stderr, "%s: error adding new group entry\n",
  1857. X                Prog);
  1858. X            exit (1);
  1859. X        }
  1860. X#ifdef    NDBM
  1861. X        /*
  1862. X         * Update the DBM group file with the new entry as well.
  1863. X         */
  1864. X
  1865. X        if (! gr_dbm_update (grp)) {
  1866. X            fprintf (stderr, "%s: cannot add new dbm group entry\n",
  1867. X                Prog);
  1868. X            exit (1);
  1869. X        }
  1870. X        endgrent ();
  1871. X#endif
  1872. X    }
  1873. X
  1874. X#ifdef    SHADOWGRP
  1875. X    /*
  1876. X     * Scan through the entire shadow group file looking for the groups
  1877. X     * that the user is a member of.
  1878. X     */
  1879. X
  1880. X    for (sgr_rewind (), sgrp = sgr_next ();sgrp;sgrp = sgr_next ()) {
  1881. X
  1882. X        /*
  1883. X         * See if the user was a member of this group
  1884. X         */
  1885. X
  1886. X        for (i = 0;sgrp->sg_mem[i];i++)
  1887. X            if (strcmp (sgrp->sg_mem[i], user_name) == 0)
  1888. X                break;
  1889. X
  1890. X        was_member = sgrp->sg_mem[i] ? i:-1;
  1891. X
  1892. X        /*
  1893. X         * See if the user was an administrator of this group
  1894. X         */
  1895. X
  1896. X        for (i = 0;sgrp->sg_adm[i];i++)
  1897. X            if (strcmp (sgrp->sg_adm[i], user_name) == 0)
  1898. X                break;
  1899. X
  1900. X        was_admin = sgrp->sg_adm[i] ? i:-1;
  1901. X
  1902. X        /*
  1903. X         * See if the user specified this group as one of their
  1904. X         * concurrent groups.
  1905. X         */
  1906. X
  1907. X        for (i = 0;i < user_ngroups;i++) {
  1908. X            if (! (grp = gr_locate (sgrp->sg_name)))
  1909. X                continue;
  1910. X
  1911. X            if (grp->gr_gid == user_groups[i])
  1912. X                break;
  1913. X        }
  1914. X        is_member = i == user_ngroups ? -1:i;
  1915. X
  1916. X        if (is_member == -1 && was_admin == -1 && was_member == -1)
  1917. X            continue;
  1918. X
  1919. X        if (was_admin >= 0 && lflg) {
  1920. X            sgrp->sg_adm = del_list (sgrp->sg_adm, user_name);
  1921. X            sgrp->sg_adm = add_list (sgrp->sg_adm, user_newname);
  1922. X#ifdef    USE_SYSLOG
  1923. X            syslog (LOG_INFO, "change admin `%s' to `%s' in shadow group `%s'\n",
  1924. X                user_name, user_newname, sgrp->sg_name);
  1925. X#endif
  1926. X        }
  1927. X        if (was_member >= 0 && (! Gflg || is_member >= 0)) {
  1928. X            if (lflg) {
  1929. X                sgrp->sg_mem = del_list (sgrp->sg_mem,
  1930. X                    user_name);
  1931. X                sgrp->sg_mem = add_list (sgrp->sg_mem,
  1932. X                    user_newname);
  1933. X#ifdef    USE_SYSLOG
  1934. X                syslog (LOG_INFO, "change `%s' to `%s' in shadow group `%s'\n",
  1935. X                    user_name, user_newname, sgrp->sg_name);
  1936. X#endif
  1937. X            }
  1938. X        } else if (was_member >= 0 && Gflg && is_member < 0) {
  1939. X            sgrp->sg_mem = del_list (sgrp->sg_mem, user_name);
  1940. X#ifdef    USE_SYSLOG
  1941. X            syslog (LOG_INFO, "delete `%s' from shadow group `%s'\n",
  1942. X                user_name, sgrp->sg_name);
  1943. X#endif
  1944. X        } else if (was_member < 0 && Gflg && is_member >= 0) {
  1945. X            sgrp->sg_mem = add_list (sgrp->sg_mem,
  1946. X                lflg ? user_newname:user_name);
  1947. X#ifdef    USE_SYSLOG
  1948. X            syslog (LOG_INFO, "add `%s' to shadow group `%s'\n",
  1949. X                lflg ? user_newname:user_name, grp->gr_name);
  1950. X#endif
  1951. X        } else
  1952. X            continue;
  1953. X
  1954. X        /* 
  1955. X         * Update the group entry to reflect the changes.
  1956. X         */
  1957. X
  1958. X        if (! sgr_update (sgrp)) {
  1959. X            fprintf (stderr, "%s: error adding new group entry\n",
  1960. X                Prog);
  1961. X            exit (1);
  1962. X        }
  1963. X#ifdef    NDBM
  1964. X        /*
  1965. X         * Update the DBM group file with the new entry as well.
  1966. X         */
  1967. X
  1968. X        if (! sgr_dbm_update (sgrp)) {
  1969. X            fprintf (stderr, "%s: cannot add new dbm group entry\n",
  1970. X                Prog);
  1971. X            exit (1);
  1972. X        }
  1973. X        endsgent ();
  1974. X#endif
  1975. X    }
  1976. X#endif
  1977. X}
  1978. X
  1979. X/*
  1980. X * check_new_id - verify the new UID for uniqueness
  1981. X *
  1982. X *    check_new_id() insures that the new UID does not exist already.
  1983. X *    It does this by checking that the UID has changed and that there
  1984. X *    is no current entry for this user ID.
  1985. X */
  1986. X
  1987. Xint
  1988. Xcheck_new_id ()
  1989. X{
  1990. X    /*
  1991. X     * First, the easy stuff.  If the ID can be duplicated, or if
  1992. X     * the ID didn't really change, just return.  If the ID didn't
  1993. X     * change, turn off those flags.  No sense doing needless work.
  1994. X     */
  1995. X
  1996. X    if (oflg)
  1997. X        return 0;
  1998. X
  1999. X    if (user_id == user_newid) {
  2000. X        uflg = lflg = 0;
  2001. X        return 0;
  2002. X    }
  2003. X    if (getpwuid (user_newid))
  2004. X        return -1;
  2005. X
  2006. X    return 0;
  2007. X}
  2008. X
  2009. X/*
  2010. X * process_flags - perform command line argument setting
  2011. X *
  2012. X *    process_flags() interprets the command line arguments and sets
  2013. X *    the values that the user will be created with accordingly.  The
  2014. X *    values are checked for sanity.
  2015. X */
  2016. X
  2017. Xvoid
  2018. Xprocess_flags (argc, argv)
  2019. Xint    argc;
  2020. Xchar    **argv;
  2021. X{
  2022. X    extern    int    optind;
  2023. X    extern    char    *optarg;
  2024. X    struct    group    *grp;
  2025. X    struct    passwd    *pwd;
  2026. X    struct    spwd    *spwd;
  2027. X    long    l;
  2028. X    int    anyflag = 0;
  2029. X    int    arg;
  2030. X
  2031. X    if (argc == 1 || argv[argc - 1][0] == '-')
  2032. X        usage ();
  2033. X
  2034. X    if (! (pwd = getpwnam (argv[argc - 1]))) {
  2035. X        fprintf (stderr, "%s: user %s does not exist\n",
  2036. X            Prog, argv[argc - 1]);
  2037. X        exit (6);
  2038. X    }
  2039. X    strcpy (user_name, pwd->pw_name);
  2040. X    user_id = pwd->pw_uid;
  2041. X    user_gid = pwd->pw_gid;
  2042. X    strcpy (user_comment, pwd->pw_gecos);
  2043. X    strcpy (user_home, pwd->pw_dir);
  2044. X    strcpy (user_shell, pwd->pw_shell);
  2045. X
  2046. X    if (spwd = getspnam (user_name)) {
  2047. X        user_expire = spwd->sp_expire;
  2048. X        user_inactive = spwd->sp_inact;
  2049. X    }
  2050. X    while ((arg = getopt (argc, argv, "u:og:G:d:s:c:mf:e:l:")) != EOF) {
  2051. X        switch (arg) {
  2052. X            case 'c':
  2053. X                if (! VALID (optarg)) {
  2054. X                    fprintf (stderr,
  2055. X                        "%s: invalid field `%s'\n",
  2056. X                        Prog, optarg);
  2057. X                    exit (3);
  2058. X                }
  2059. X                if (strcmp (optarg, user_comment)) {
  2060. X                    cflg++;
  2061. X                    strncpy (user_comment, optarg, BUFSIZ);
  2062. X                }
  2063. X                break;
  2064. X            case 'd':
  2065. X                if (! VALID (optarg)) {
  2066. X                    fprintf (stderr,
  2067. X                        "%s: invalid field `%s'\n",
  2068. X                        Prog, optarg);
  2069. X                    exit (3);
  2070. X                }
  2071. X                dflg++;
  2072. X                strncpy (user_newhome, optarg, BUFSIZ);
  2073. X                break;
  2074. X            case 'e':
  2075. X                l = strtoday (optarg);
  2076. X#ifdef    ITI_AGING
  2077. X                l *= DAY;
  2078. X#endif
  2079. X                if (l != user_expire) {
  2080. X                    eflg++;
  2081. X                    user_expire = l;
  2082. X                }
  2083. X                break;
  2084. X            case 'f':
  2085. X                if (user_inactive != atoi (optarg)) {
  2086. X                    fflg++;
  2087. X                    user_inactive = atoi (optarg);
  2088. X                }
  2089. X                break;
  2090. X            case 'g':
  2091. X                if (isdigit (optarg[0]))
  2092. X                    grp = getgrgid (atoi (optarg));
  2093. X                else
  2094. X                    grp = getgrnam (optarg);
  2095. X
  2096. X                if (! grp) {
  2097. X                    fprintf (stderr,
  2098. X                        "%s: unknown group %s\n",
  2099. X                        Prog, optarg);
  2100. X                    exit (1);
  2101. X                }
  2102. X                if (grp->gr_gid != user_gid) {
  2103. X                    gflg++;
  2104. X                    user_gid = grp->gr_gid;
  2105. X                }
  2106. X                break;
  2107. X            case 'G':
  2108. X                Gflg++;
  2109. X                if (get_groups (optarg))
  2110. X                    exit (1);
  2111. X
  2112. X                break;
  2113. X            case 'l':
  2114. X                if (! VALID (optarg)) {
  2115. X                    fprintf (stderr,
  2116. X                        "%s: invalid field `%s'\n",
  2117. X                        Prog, optarg);
  2118. X                    exit (3);
  2119. X                }
  2120. X                if (strcmp (user_name, optarg)) {
  2121. X                    lflg++;
  2122. X                    strcpy (user_newname, optarg);
  2123. X                }
  2124. X                break;
  2125. X            case 'm':
  2126. X                if (! dflg)
  2127. X                    usage ();
  2128. X
  2129. X                mflg++;
  2130. X                break;
  2131. X            case 'o':
  2132. X                if (! uflg)
  2133. X                    usage ();
  2134. X
  2135. X                oflg++;
  2136. X                break;
  2137. X            case 's':
  2138. X                if (! VALID (optarg)) {
  2139. X                    fprintf (stderr,
  2140. X                        "%s: invalid field `%s'\n",
  2141. X                        Prog, optarg);
  2142. X                    exit (3);
  2143. X                }
  2144. X                if (strcmp (user_shell, optarg)) {
  2145. X                    strncpy (user_shell, optarg, BUFSIZ);
  2146. X                    sflg++;
  2147. X                }
  2148. X                break;
  2149. X            case 'u':
  2150. X                uflg++;
  2151. X                user_newid = atoi (optarg);
  2152. X                break;
  2153. X            default:
  2154. X                usage ();
  2155. X        }
  2156. X        anyflag++;
  2157. X    }
  2158. X    if (anyflag == 0) {
  2159. X        fprintf (stderr, "%s: no flags given\n", Prog);
  2160. X        exit (1);
  2161. X    }
  2162. X    if (optind != argc - 1)
  2163. X        usage ();
  2164. X
  2165. X    if (dflg && strcmp (user_home, user_newhome) == 0)
  2166. X        dflg = mflg = 0;
  2167. X
  2168. X    if (uflg && user_id == user_newid)
  2169. X        uflg = oflg = 0;
  2170. X
  2171. X    if (lflg && getpwnam (user_newname)) {
  2172. X        fprintf (stderr, "%s: user %s exists\n", Prog, user_newname);
  2173. X        exit (9);
  2174. X    }
  2175. X}
  2176. X
  2177. X/*
  2178. X * close_files - close all of the files that were opened
  2179. X *
  2180. X *    close_files() closes all of the files that were opened for this
  2181. X *    new user.  This causes any modified entries to be written out.
  2182. X */
  2183. X
  2184. Xclose_files ()
  2185. X{
  2186. X    if (! pw_close ()) {
  2187. X        fprintf (stderr, "%s: cannot rewrite password file\n", Prog);
  2188. X        exit (1);
  2189. X    }
  2190. X    if (! spw_close ()) {
  2191. X        fprintf (stderr, "%s: cannot rewrite shadow password file\n",    
  2192. X            Prog);
  2193. X        exit (1);
  2194. X    }
  2195. X    if (user_ngroups >= 0) {
  2196. X        if (! gr_close ()) {
  2197. X            fprintf (stderr, "%s: cannot rewrite group file\n",
  2198. X                Prog);
  2199. X            exit (1);
  2200. X        }
  2201. X        (void) gr_unlock ();
  2202. X#ifdef    SHADOWGRP
  2203. X        if (! sgr_close ()) {
  2204. X            fprintf (stderr, "%s: cannot rewrite shadow group file\n",
  2205. X                Prog);
  2206. X            exit (1);
  2207. X        }
  2208. X        (void) sgr_unlock ();
  2209. X#endif
  2210. X    }
  2211. X    (void) spw_unlock ();
  2212. X    (void) pw_unlock ();
  2213. X}
  2214. X
  2215. X/*
  2216. X * open_files - lock and open the password files
  2217. X *
  2218. X *    open_files() opens the two password files.
  2219. X */
  2220. X
  2221. Xopen_files ()
  2222. X{
  2223. X    if (! pw_lock ()) {
  2224. X        fprintf (stderr, "%s: unable to lock password file\n", Prog);
  2225. X        exit (1);
  2226. X    }
  2227. X    if (! pw_open (O_RDWR)) {
  2228. X        fprintf (stderr, "%s: unable to open password file\n", Prog);
  2229. X        exit (1);
  2230. X    }
  2231. X    if (! spw_lock ()) {
  2232. X        fprintf (stderr, "%s: cannot lock shadow password file\n", Prog);
  2233. X        exit (1);
  2234. X    }
  2235. X    if (! spw_open (O_RDWR)) {
  2236. X        fprintf (stderr, "%s: cannot open shadow password file\n", Prog);
  2237. X        exit (1);
  2238. X    }
  2239. X}
  2240. X
  2241. X/*
  2242. X * usr_update - create the user entries
  2243. X *
  2244. X *    usr_update() creates the password file entries for this user
  2245. X *    and will update the group entries if required.
  2246. X */
  2247. X
  2248. Xusr_update ()
  2249. X{
  2250. X    struct    passwd    pwent;
  2251. X    struct    spwd    spent;
  2252. X    struct    passwd    *pwd;
  2253. X    struct    spwd    *spwd;
  2254. X
  2255. X    pwd = pw_locate (user_name);
  2256. X    pwent = *pwd;
  2257. X
  2258. X    spwd = spw_locate (user_name);
  2259. X    spent = *spwd;
  2260. X
  2261. X    new_pwent (&pwent);
  2262. X    new_spent (&spent);
  2263. X
  2264. X    if (lflg || uflg || gflg || cflg || dflg || sflg) {
  2265. X        if (! pw_update (&pwent)) {
  2266. X            fprintf (stderr, "%s: error changing password entry\n",
  2267. X                Prog);
  2268. X            exit (1);
  2269. X        }
  2270. X        if (lflg && ! pw_remove (user_name)) {
  2271. X            fprintf (stderr, "%s: error removing password entry\n",
  2272. X                Prog);
  2273. X            exit (1);
  2274. X        }
  2275. X#if defined(DBM) || defined(NDBM)
  2276. X        if (access ("/etc/passwd.pag", 0) == 0) {
  2277. X            if (! pw_dbm_update (&pwent)) {
  2278. X                fprintf (stderr, "%s: error adding password dbm entry\n",
  2279. X                    Prog);
  2280. X                exit (1);
  2281. X            }
  2282. X            if (lflg && (pwd = getpwnam (user_name)) && ! pw_dbm_remove (pwd)) {
  2283. X                fprintf (stderr, "%s: error removing passwd dbm entry\n",
  2284. X                    Prog);
  2285. X                exit (1);
  2286. X            }
  2287. X            endpwent ();
  2288. X        }
  2289. X#endif
  2290. X    }
  2291. X    if (lflg || eflg || fflg) {
  2292. X        if (! spw_update (&spent)) {
  2293. X            fprintf (stderr, "%s: error adding new shadow password entry\n",
  2294. X                Prog);
  2295. X            exit (1);
  2296. X        }
  2297. X        if (lflg && ! spw_remove (user_name)) {
  2298. X            fprintf (stderr, "%s: error removing shadow password entry\n",
  2299. X                Prog);
  2300. X            exit (1);
  2301. X        }
  2302. X    }
  2303. X#ifdef    NDBM
  2304. X    if (access ("/etc/shadow.pag", 0) == 0) {
  2305. X        if (! sp_dbm_update (&spent)) {
  2306. X            fprintf (stderr, "%s: error updating shadow passwd dbm entry\n",
  2307. X                Prog);
  2308. X            exit (1);
  2309. X        }
  2310. X        if (lflg && ! sp_dbm_remove (user_name)) {
  2311. X            fprintf (stderr, "%s: error removing shadow passwd db entry\n",
  2312. X                Prog);
  2313. X            exit (1);
  2314. X        }
  2315. X        endspent ();
  2316. X    }
  2317. X#endif
  2318. X    if (Gflg || lflg)
  2319. X        grp_update ();
  2320. X}
  2321. X
  2322. X/*
  2323. X * move_home - move the user's home directory
  2324. X *
  2325. X *    move_home() creates the user's home directory if it does not
  2326. X *    already exist.  It will be created mode 755 owned by the user
  2327. X *    with the user's default group.
  2328. X */
  2329. X
  2330. Xmove_home ()
  2331. X{
  2332. X    struct    stat    sb;
  2333. X
  2334. X    if (mflg && stat (user_home, &sb) == 0) {
  2335. X        if (access (user_newhome, 0) == 0) {
  2336. X            fprintf (stderr, "%s: directory %s exists\n",
  2337. X                Prog, user_newhome);
  2338. X            exit (12);
  2339. X        } else if (rename (user_home, user_newhome)) {
  2340. X            if (errno == EXDEV) {
  2341. X                if (mkdir (user_newhome, sb.st_mode & 0777)) {
  2342. X                    fprintf (stderr,
  2343. X                        "%s: can't create %s\n",
  2344. X                        Prog, user_newhome);
  2345. X                }
  2346. X                chown (user_newhome, sb.st_uid, sb.st_gid);
  2347. X                if (copy_tree (user_home, user_newhome,
  2348. X                        -1, -1) == 0 &&
  2349. X                    remove_tree (user_home) == 0 &&
  2350. X                        rmdir (user_home) == 0)
  2351. X                    return;
  2352. X
  2353. X                remove_tree (user_newhome);
  2354. X                rmdir (user_newhome);
  2355. X            }
  2356. X            fprintf (stderr,
  2357. X                "%s: cannot rename directory %s to %s\n",
  2358. X                Prog, user_home, user_newhome);
  2359. X            exit (12);
  2360. X        }
  2361. X    }
  2362. X    if (uflg || gflg)
  2363. X        chown (dflg ? user_newhome:user_home, user_id, user_gid);
  2364. X}
  2365. X
  2366. X/*
  2367. X * main - useradd command
  2368. X */
  2369. X
  2370. Xmain (argc, argv)
  2371. Xint    argc;
  2372. Xchar    **argv;
  2373. X{
  2374. X    /*
  2375. X     * Get my name so that I can use it to report errors.
  2376. X     */
  2377. X
  2378. X    if (Prog = strrchr (argv[0], '/'))
  2379. X        Prog++;
  2380. X    else
  2381. X        Prog = argv[0];
  2382. X
  2383. X#ifdef    USE_SYSLOG
  2384. X    openlog (Prog, LOG_PID|LOG_CONS|LOG_NOWAIT, LOG_AUTH);
  2385. X#endif
  2386. X
  2387. X    /*
  2388. X     * The open routines for the DBM files don't use read-write
  2389. X     * as the mode, so we have to clue them in.
  2390. X     */
  2391. X
  2392. X#if defined(DBM) || defined(NDBM)
  2393. X    pw_dbm_mode = O_RDWR;
  2394. X#endif
  2395. X#ifdef    NDBM
  2396. X    sp_dbm_mode = O_RDWR;
  2397. X    gr_dbm_mode = O_RDWR;
  2398. X#ifdef    SHADOWGRP
  2399. X    sg_dbm_mode = O_RDWR;
  2400. X#endif
  2401. X#endif
  2402. X    process_flags (argc, argv);
  2403. X
  2404. X    /*
  2405. X     * Do the hard stuff - open the files, change the user entries,
  2406. X     * change the home directory, then close and update the files.
  2407. X     */
  2408. X
  2409. X    open_files ();
  2410. X
  2411. X    usr_update ();
  2412. X
  2413. X    close_files ();
  2414. X
  2415. X    if (mflg)
  2416. X        move_home ();
  2417. X
  2418. X    exit (0);
  2419. X    /*NOTREACHED*/
  2420. X}
  2421. END_OF_FILE
  2422.   if test 24732 -ne `wc -c <'usermod.c'`; then
  2423.     echo shar: \"'usermod.c'\" unpacked with wrong size!
  2424.   fi
  2425.   # end of 'usermod.c'
  2426. fi
  2427. echo shar: End of archive 2 \(of 11\).
  2428. cp /dev/null ark2isdone
  2429. MISSING=""
  2430. for I in 1 2 3 4 5 6 7 8 9 10 11 ; do
  2431.     if test ! -f ark${I}isdone ; then
  2432.     MISSING="${MISSING} ${I}"
  2433.     fi
  2434. done
  2435. if test "${MISSING}" = "" ; then
  2436.     echo You have unpacked all 11 archives.
  2437.     rm -f ark[1-9]isdone ark[1-9][0-9]isdone
  2438. else
  2439.     echo You still must unpack the following archives:
  2440.     echo "        " ${MISSING}
  2441. fi
  2442. exit 0
  2443. exit 0 # Just in case...
  2444.