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

  1. Newsgroups: comp.sources.misc
  2. From: jfh@rpp386.Cactus.ORG (John F Haugh II)
  3. Subject:  v26i054:  shadow - Shadow Password Suite, Part01/11
  4. Message-ID: <csm-v26i054=shadow.124731@sparky.IMD.Sterling.COM>
  5. X-Md4-Signature: ca4b30ecd506ff36955f1053784a1c9b
  6. Date: Sun, 24 Nov 1991 18:49:19 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 54
  11. Archive-name: shadow/part01
  12. Environment: UNIX
  13. Supersedes: shadow-2: Volume 06, Issue 22-24
  14.  
  15. This is John F. Haugh II's login replacement, release 3.  
  16.  
  17. New for Release 3:
  18.     The objects are being combined into libraries to make maintenance
  19.     easier and to encourage developers to use the modules as the
  20.     basis for new tools.  New tools are planned based on SVR4 commands.
  21.  
  22.     New lint rules have been added to make the code easier to lint.
  23.     Files will gradually be fixed so that they lint cleanly.
  24.  
  25.     DBM file access has been added to everything that would tolerate
  26.     it.  The files /etc/passwd, /etc/group, and /etc/shadow all have
  27.     DBM interfaces.  The new file, /etc/gshadow, has been added to
  28.     support shadowed group information and it too has a DBM interface.
  29.     Additional information has been added to the shadow group file to
  30.     define the notion of a group administrator.
  31.  
  32.     SVR4 utilities to add and modify user and group information have
  33.     been added.  The man pages for these commands have been written
  34.     as well.
  35.  
  36. Begin by reading and editing the config.h file.  All options are selected
  37. by using #define's.  A brief description for each available option appears
  38. in the README.  You may want to print this file out as it is LONG and you 
  39. will need to refer to it while editting config.h.  You will also have to 
  40. edit the Makefile.  The possible differences are documented there.  Pay 
  41. close attention to the install: rule.  Login now runs on about 30 different
  42. varieties of UNIX that I have been made aware of.
  43.  
  44. Note that there are MANY options.  As distributed most options are turned
  45. on, which produces a really nice package.  This is the system as used on
  46. some of the authors' machines.  There are many options which may be
  47. selected at run time.  You should refer to the login.5 manual page for
  48. more information regarding these options.
  49.  
  50. Special thanks are due to Chip Rosenthal for his fine testing efforts;
  51. to Steve Simmons for his work in porting this code to BSD; and to Bill
  52. Kennedy for his contributions of LaserJet printer time and energies.
  53. Also, thanks for Dennis L. Mumaugh for the initial shadow password
  54. information and to Tony Walton (olapw@olgb1.oliv.co.uk) for the System
  55. V Release 4 changes.  Effort in porting to SunOS has been contributed
  56. by Dr. Michael Newberry (miken@cs.adfa.oz.au).
  57.  
  58. --------
  59. #! /bin/sh
  60. # This is a shell archive.  Remove anything before this line, then feed it
  61. # into a shell via "sh file" or similar.  To overwrite existing files,
  62. # type "sh file -c".
  63. # The tool that generated this appeared in the comp.sources.unix newsgroup;
  64. # send mail to comp-sources-unix@uunet.uu.net if you want that tool.
  65. # Contents:  README lmain.c useradd.c
  66. # Wrapped by kent@sparky on Sun Nov 24 11:03:40 1991
  67. PATH=/bin:/usr/bin:/usr/ucb ; export PATH
  68. echo If this archive is complete, you will see the following message:
  69. echo '          "shar: End of archive 1 (of 11)."'
  70. if test -f 'README' -a "${1}" != "-c" ; then 
  71.   echo shar: Will not clobber existing file \"'README'\"
  72. else
  73.   echo shar: Extracting \"'README'\" \(9097 characters\)
  74.   sed "s/^X//" >'README' <<'END_OF_FILE'
  75. X[    @(#)README    3.6    08:27:47    10/31/91    ]
  76. X
  77. XThis is the explanatory document for John F. Haugh II's login replacement,
  78. Xrelease 3.  This document was last updated 10/31/91.
  79. X
  80. XThis software is copyright 1988, 1989, 1990, 1991, John F. Haugh II.  All
  81. Xrights reserved.  Use, duplication and disclosure is permitted according
  82. Xto the guidelines listed below.
  83. X
  84. XThis software is being provided as a freely redistributable login clone.
  85. XYou may distribute this software provided you do not charge for other than
  86. Xtransmission costs.  You are free to copy this software provided you
  87. Xdo not restrict the rights of the recipients to further copy this software.
  88. X
  89. XTHIS SOFTWARE IS BEING DISTRIBUTED AS-IS.  THE AUTHORS DISCLAIM ALL
  90. XLIABILITY FOR ANY CONSEQUENCES OF USE.  THE USER IS SOLELY RESPONSIBLE
  91. XFOR THE MAINTENANCE OF THIS SOFTWARE PACKAGE.  THE AUTHORS ARE UNDER NO
  92. XOBLIGATION TO PROVIDE MODIFICATIONS OR IMPROVEMENTS.  THE USER IS
  93. XENCOURAGE TO TAKE ANY AND ALL STEPS NEEDED TO PROTECT AGAINST ACCIDENTAL
  94. XLOSS OF INFORMATION OR MACHINE RESOURCES.
  95. X
  96. XSpecial thanks are due to Chip Rosenthal for his fine testing efforts;
  97. Xto Steve Simmons for his work in porting this code to BSD; and to Bill
  98. XKennedy for his contributions of LaserJet printer time and energies.
  99. XAlso, thanks for Dennis L. Mumaugh for the initial shadow password
  100. Xinformation and to Tony Walton (olapw@olgb1.oliv.co.uk) for the System
  101. XV Release 4 changes.  Effort in porting to SunOS has been contributed
  102. Xby Dr. Michael Newberry (miken@cs.adfa.oz.au).
  103. X
  104. XNew for Release 3:
  105. X    The objects are being combined into libraries to make maintenance
  106. X    easier and to encourage developers to use the modules as the
  107. X    basis for new tools.  New tools are planned based on SVR4 commands.
  108. X
  109. X    New lint rules have been added to make the code easier to lint.
  110. X    Files will gradually be fixed so that they lint cleanly.
  111. X
  112. X    DBM file access has been added to everything that would tolerate
  113. X    it.  The files /etc/passwd, /etc/group, and /etc/shadow all have
  114. X    DBM interfaces.  The new file, /etc/gshadow, has been added to
  115. X    support shadowed group information and it too has a DBM interface.
  116. X    Additional information has been added to the shadow group file to
  117. X    define the notion of a group administrator.
  118. X
  119. X    SVR4 utilities to add and modify user and group information have
  120. X    been added.  The man pages for these commands have been written
  121. X    as well.
  122. X
  123. XBegin by reading and editing the config.h file.  All options are selected
  124. Xby using #define's.  A brief description for each available option appears
  125. Xbelow.  You may want to print this file out as it is LONG and you will
  126. Xneed to refer to it while editting config.h.  You will also have to edit
  127. Xthe Makefile.  The possible differences are documented there.  Pay close
  128. Xattention to the install: rule.  Login now runs on about 30 different
  129. Xvarieties of UNIX that I have been made aware of.
  130. X
  131. XNote that there are MANY options.  As distributed most options are turned
  132. Xon, which produces a really nice package.  This is the system as used on
  133. Xsome of the authors' machines.  There are many options which may be
  134. Xselected at run time.  You should refer to the login.5 manual page for
  135. Xmore information regarding these options.
  136. X
  137. XLogin Defaults File -
  138. X    This option selects the name of the file to read for the
  139. X    run-time configurable options.  The default value for
  140. X    LOGINDEFS is "/etc/login.defs".
  141. X
  142. XShadow [ unreadable ] Password Files -
  143. X    This option utilizes an alternate, non-readable file to
  144. X    contain the actual encrypted passwords.  This is presumed
  145. X    to increase system security by increasing the difficulty
  146. X    with which system crackers obtain encrypted passwords.
  147. X
  148. X    Select this option by defining the SHADOWPWD macro.
  149. X
  150. XShadow Group Files -
  151. X    This option utilizes an alternate, non-readable file to
  152. X    contain encrypted group passwords and group administrator
  153. X    information.
  154. X
  155. X    This feature allows one or more users to be defined as
  156. X    the administrators of a group for the purpose of adding
  157. X    or deleting members and changing the group password.
  158. X
  159. X    Select this option by defining the SHADOWGRP macro.  You
  160. X    must also create an emptry /etc/gshadow file.
  161. X
  162. XDBM Password Files -
  163. X    This option utilizes the DBM database access routines to
  164. X    increase the performance of user name and ID lookups in the
  165. X    password file.  You may select the NDBM database instead
  166. X    and have DBM-style access to all user information files.
  167. X
  168. X    Select this option by defining both the DBM and GETPWENT
  169. X    macros.  The FGETPWENT macro must also be defined or the
  170. X    fgetpwent() library routine must be present.
  171. X
  172. XDouble Length Passwords -
  173. X    This option extends the maximum length of a user password
  174. X    to 16 characters from eight.
  175. X
  176. X    Select this option by defining the DOUBLESIZE macro.
  177. X    Credit for this option is due Jonathan Bayer.
  178. X
  179. XPassword Aging -
  180. X    This option includes code to perform password aging.
  181. X    Password aging is presumed to increase system security
  182. X    by forcing users to change passwords on a regular
  183. X    basis.  The resolution on password age is in weeks for
  184. X    non-shadow password systems and in days otherwise.
  185. X
  186. X    Select this option by defining the AGING macro.
  187. X
  188. XSyslog -
  189. X    This option causes the code to log various errors or
  190. X    special conditions to the syslog daemon.  The types of
  191. X    information that are logged security violations, changes
  192. X    to the user database, and program errors.
  193. X
  194. X    Select syslog processing by defining the USE_SYSLOG
  195. X    macro.
  196. X
  197. XRemote Login -
  198. X    This option causes certain network login code to be
  199. X    inserted to enable the "rlogin" and "telnet" commands to
  200. X    work.  To enable network logins, define the RLOGIN macro.
  201. X    If your <utmp.h> file includes a ut_host member, you must
  202. X    also define the UT_HOST macro.
  203. X
  204. XDirectory Reading Routines -
  205. X    Three different macros are defined for opening and reading
  206. X    directories.  They are DIR_XENIX, DIR_BSD, and DIR_SYSV.
  207. X    Refer to config.h for more details.
  208. X
  209. XLibrary Configuration Macros -
  210. X    The following macros define the functions which are present
  211. X    in your system library:
  212. X
  213. X    HAVE_ULIMIT    - Define if your UNIX supports ulimit()
  214. X    GETPWENT    - Define if you want my GETPWENT(3) routines
  215. X    GETGRENT    - Define if you want my GETGRENT(3) routines
  216. X    NEED_AL64    - Define if library does not include a64l()
  217. X    NEED_MKDIR    - Define if system does not have mkdir()
  218. X    NEED_RMDIR    - Define if system does not have rmdir()
  219. X    NEED_RENAME    - Define if system does not have rename()
  220. X    NEED_STRSTR    - Define if library does not include strstr()
  221. X
  222. XPassword File Information -
  223. X    The following macros define the fields which are present in
  224. X    your system password file.  Because the system was compiled
  225. X    to use the password file in its original form, these macros
  226. X    must agree with the actual contents of the file.
  227. X
  228. X    BSD_QUOTA    - the pw_quota field exists
  229. X    ATT_AGE        - the pw_age field exists
  230. X    ATT_COMMENT    - the pw_comment field exists
  231. X
  232. XSignal Return Type -
  233. X    Because different systems return different data types for
  234. X    the signal() system call, you must define SIGTYPE to be
  235. X    the data type your system uses.  The default is "int", but
  236. X    "void" is another popular value.
  237. X
  238. XBSD Notes:    Steve Simmons    scs@iti.org
  239. X
  240. XThe full port of the shadow package to BSD is not complete; but some
  241. Xof the issues have been worked out.  These notes describe the current
  242. Xstate of things:
  243. X
  244. XIn order to make use of password aging under BSD, minor changes to
  245. X/usr/include/pwd.h and getpwent() are needed.  These changes are to
  246. Xkeep the password age from messing up the encrypted password when not
  247. Xusing shadow passwords, and involve placing a new field in the password
  248. Xdata structure.  To use this, you should apply the following two patches:
  249. X    pwd.h.patch
  250. X    getpwent.c.patch
  251. Xto the BSD /usr/include/pwd.h and /usr/src/lib/libc/gen/getpwent.c,
  252. Xrespectively.  After applying the patches, rebuild your standard C
  253. Xlibrary with the new getpwent.  Programs which use the old getpwent
  254. Xwill fail on password checking if they do a strcmp rather than a strncmp.
  255. X[ I do not seem to have these two patches.  I have provided an entire
  256. Xgetpwent collection of code which may be useful instead.  This code
  257. Xdoes not support Sun Yellow Pages(tm?), which is a shame. -jfh ]
  258. X
  259. XThese changes are based on BSD4.3, not Tahoe
  260. X
  261. XToDo BSD:
  262. X
  263. XI'm working on this in my copious spare time (hah!); any help would
  264. Xbe appreciated.  If you decide to help, do these independantly rather
  265. Xthan rework BSD code!  Keep it redistributable!
  266. X
  267. XNo dbm functions have been put in place.  Dbm functionality is needed
  268. Xfor both /etc/password and /etc/shadow management.  [ It is now possible
  269. Xto create /etc/passwd.dir and /etc/passwd.pag using the new mkpasswd
  270. Xcommand.  getpwuid and getpwnam both use these files.   Also, the 
  271. Xcommands chfn, chsh, and chage all update the DBM files. -jfh ]
  272. X
  273. XThe BSD GECOS field gets used for lots more stuff than the USG.  At a
  274. Xminimum this functionality should be duplicated under BSD; better is to put
  275. Xit into USG as well; still better would be to make the chfn command for
  276. Xboth systems; best would be site-configurable data to be put into GECOS/chfn.
  277. X
  278. X[ this is now possible using chfn and the -o option.  i would appreciate
  279. X  anyone who is able to confirm that this code runs reasonably on some
  280. X  BSD release. - jfh ]
  281. END_OF_FILE
  282.   if test 9097 -ne `wc -c <'README'`; then
  283.     echo shar: \"'README'\" unpacked with wrong size!
  284.   fi
  285.   # end of 'README'
  286. fi
  287. if test -f 'lmain.c' -a "${1}" != "-c" ; then 
  288.   echo shar: Will not clobber existing file \"'lmain.c'\"
  289. else
  290.   echo shar: Extracting \"'lmain.c'\" \(14004 characters\)
  291.   sed "s/^X//" >'lmain.c' <<'END_OF_FILE'
  292. X/*
  293. X * Copyright 1989, 1990, 1991, John F. Haugh II
  294. X * All rights reserved.
  295. X *
  296. X * Permission is granted to copy and create derivative works for any
  297. X * non-commercial purpose, provided this copyright notice is preserved
  298. X * in all copies of source code, or included in human readable form
  299. X * and conspicuously displayed on all copies of object code or
  300. X * distribution media.
  301. X */
  302. X
  303. X#include <sys/types.h>
  304. X#include <sys/stat.h>
  305. X#include <stdio.h>
  306. X#include "pwd.h"
  307. X#include <utmp.h>
  308. X#include <time.h>
  309. X#include <signal.h>
  310. X#ifndef    BSD
  311. X#include <string.h>
  312. X#include <memory.h>
  313. X#else
  314. X#include <strings.h>
  315. X#define    strchr    index
  316. X#define    strrchr    rindex
  317. X#endif
  318. X#ifndef    BSD
  319. X#include <termio.h>
  320. X#else
  321. X#include <sgtty.h>
  322. X#endif
  323. X#include "config.h"
  324. X#include "lastlog.h"
  325. X#include "faillog.h"
  326. X#include "shadow.h"
  327. X
  328. X#if !defined(BSD) && !defined(SUN)
  329. X#define    bzero(a,n)    memset(a, 0, n);
  330. X#endif
  331. X
  332. X#ifdef    USE_SYSLOG
  333. X#include <syslog.h>
  334. X
  335. X#ifndef    LOG_WARN
  336. X#define    LOG_WARN    LOG_WARNING
  337. X#endif
  338. X#endif
  339. X
  340. X#ifndef    lint
  341. Xstatic    char    sccsid[] = "@(#)lmain.c    3.15    14:38:18    10/27/91";
  342. X#endif
  343. X
  344. X                    /* danger - side effects */
  345. X#define STRFCPY(A,B)    strncpy((A), (B), sizeof(A)), *((A)+sizeof(A)-1) = '\0'
  346. X
  347. X#if defined(RLOGIN) || defined(UT_HOST)
  348. Xchar    host[BUFSIZ];
  349. Xchar    term[128] = "TERM=";
  350. X#endif
  351. X
  352. Xstruct    passwd    pwent;
  353. Xstruct    utmp    utent;
  354. Xstruct    lastlog    lastlog;
  355. Xint    pflg;
  356. Xint    rflg;
  357. Xint    fflg;
  358. X#ifdef    RLOGIN
  359. Xint    hflg;
  360. X#endif
  361. Xint    preauth_flag;
  362. X#ifndef    BSD
  363. Xstruct    termio    termio;
  364. X#endif
  365. X
  366. X#ifndef    MAXENV
  367. X#define    MAXENV    64
  368. X#endif
  369. X
  370. X/*
  371. X * Global variables.
  372. X */
  373. X
  374. Xchar    *newenvp[MAXENV];
  375. Xchar    *Prog;
  376. Xint    newenvc = 0;
  377. Xint    maxenv = MAXENV;
  378. X
  379. X/*
  380. X * External identifiers.
  381. X */
  382. X
  383. Xextern    char    *getenv ();
  384. Xextern    char    *getpass ();
  385. Xextern    char    *tz ();
  386. Xextern    void    checkutmp ();
  387. Xextern    void    addenv ();
  388. Xextern    void    setenv ();
  389. Xextern    unsigned alarm ();
  390. Xextern    void    login ();
  391. Xextern    void    setutmp ();
  392. Xextern    void    subsystem ();
  393. Xextern    void    log ();
  394. Xextern    void    setup ();
  395. Xextern    int    expire ();
  396. Xextern    void    motd ();
  397. Xextern    void    mailcheck ();
  398. Xextern    void    shell ();
  399. Xextern    long    a64l ();
  400. Xextern    int    c64i ();
  401. Xextern    char    *getdef_str();
  402. Xextern    int    getdef_bool();
  403. Xextern    int    getdef_num();
  404. Xextern    long    getdef_long();
  405. Xextern    int    optind;
  406. Xextern    char    *optarg;
  407. Xextern    char    **environ;
  408. X
  409. X#ifdef HAVE_ULIMIT
  410. Xextern    long    ulimit();
  411. X#endif
  412. X
  413. X#ifndef    ALARM
  414. X#define    ALARM    60
  415. X#endif
  416. X
  417. X#ifndef    RETRIES
  418. X#define    RETRIES    3
  419. X#endif
  420. X
  421. Xstruct    faillog    faillog;
  422. X
  423. Xstruct    utmp    failent;
  424. X
  425. X#define    NO_SHADOW    "no shadow password for `%s' on `%s'\n"
  426. X#define    BAD_PASSWD    "invalid password for `%s' on `%s'\n"
  427. X#define    BAD_DIALUP    "invalid dialup password for `%s' on `%s'\n"
  428. X#define    BAD_TIME    "invalid login time for `%s' on `%s'\n"
  429. X#define    BAD_ROOT_LOGIN    "ILLEGAL ROOT LOGIN ON TTY `%s'\n"
  430. X#define    ROOT_LOGIN    "ROOT LOGIN ON TTY `%s'\n"
  431. X#define    FAILURE_CNT    "exceeded failure limit for `%s' on `%s'\n"
  432. X#define    NOT_A_TTY    "not a tty\n"
  433. X#define    NOT_ROOT    "-r or -f flag and not ROOT on `%s'\n"
  434. X
  435. X/*
  436. X * usage - print login command usage and exit
  437. X *
  438. X * login [ name ]
  439. X * login -r hostname    (for rlogind)
  440. X * login -h hostname    (for telnetd, etc.)
  441. X * login -f name    (for pre-authenticated login: datakit, xterm, etc.)
  442. X */
  443. X
  444. Xusage ()
  445. X{
  446. X    fprintf (stderr, "usage: login [ -p ] [ name ]\n");
  447. X#ifdef    RLOGIN
  448. X    fprintf (stderr, "       login [ -p ] -r name\n");
  449. X    fprintf (stderr, "       login [ -p ] [ -f name ] -h host\n");
  450. X#else
  451. X    fprintf (stderr, "       login [ -p ] -f name\n");
  452. X#endif    /* RLOGIN */
  453. X    exit (1);
  454. X}
  455. X
  456. X#ifdef    RLOGIN
  457. Xrlogin (remote_host, name, namelen)
  458. Xchar    *remote_host;
  459. Xchar    *name;
  460. Xint    namelen;
  461. X{
  462. X    struct    passwd    *pwd;
  463. X    char    remote_name[32];
  464. X    char    *cp;
  465. X
  466. X    get_remote_string (remote_name, sizeof remote_name);
  467. X    get_remote_string (name, namelen);
  468. X    get_remote_string (term + 5, sizeof term - 5);
  469. X    if (cp = strchr (term, '/'))
  470. X        *cp = '\0';
  471. X
  472. X    if (! (pwd = getpwnam (name)))
  473. X        return 0;
  474. X
  475. X    return ruserok (remote_host, pwd->pw_uid == 0, remote_name, name);
  476. X}
  477. X
  478. Xget_remote_string (buf, size)
  479. Xchar    *buf;
  480. Xint    size;
  481. X{
  482. X    for (;;) {
  483. X        if (read (0, buf, 1) != 1)
  484. X              exit (1);
  485. X        if (*buf == '\0')
  486. X            return;
  487. X        if (--size > 0)
  488. X            ++buf;
  489. X    }
  490. X    /*NOTREACHED*/
  491. X}
  492. X#endif
  493. X
  494. X/*
  495. X * login - create a new login session for a user
  496. X *
  497. X *    login is typically called by getty as the second step of a
  498. X *    new user session.  getty is responsible for setting the line
  499. X *    characteristics to a reasonable set of values and getting
  500. X *    the name of the user to be logged in.  login may also be
  501. X *    called to create a new user session on a pty for a variety
  502. X *    of reasons, such as X servers or network logins.
  503. X *
  504. X *    the flags which login supports are
  505. X *    
  506. X *    -p - preserve the environment
  507. X *    -r - perform autologin protocol for rlogin
  508. X *    -f - do not perform authentication, user is preauthenticated
  509. X *    -h - the name of the remote host
  510. X */
  511. X
  512. Xint
  513. Xmain (argc, argv, envp)
  514. Xint    argc;
  515. Xchar    **argv;
  516. Xchar    **envp;
  517. X{
  518. X    char    name[32];
  519. X    char    pass[32];
  520. X    char    hush[BUFSIZ];
  521. X    char    tty[BUFSIZ];
  522. X    int    retries;
  523. X    int    failed;
  524. X    int    flag;
  525. X    int    i;
  526. X    int    subroot = 0;
  527. X    char    *fname;
  528. X    char    *cp;
  529. X    struct    passwd    *pwd;
  530. X    struct    spwd    *spwd;
  531. X    struct    spwd    *getspnam();
  532. X
  533. X    /*
  534. X     * Some quick initialization.
  535. X     */
  536. X
  537. X    name[0] = '\0';
  538. X
  539. X    /*
  540. X     * Get the utmp file entry and get the tty name from it.  The
  541. X     * current process ID must match the process ID in the utmp
  542. X     * file if there are no additional flags on the command line.
  543. X     */
  544. X
  545. X    checkutmp (argc > 1 && argv[1][0] != '-');
  546. X    STRFCPY (tty, utent.ut_line);
  547. X
  548. X    if (Prog = strrchr (argv[0], '/'))
  549. X        Prog++;
  550. X    else
  551. X        Prog = argv[0];
  552. X
  553. X#ifdef    RLOGIN
  554. X    while ((flag = getopt (argc, argv, "pr:f:h:")) != EOF)
  555. X#else
  556. X    while ((flag = getopt (argc, argv, "pf:")) != EOF)
  557. X#endif
  558. X    {
  559. X        switch (flag) {
  560. X            case 'p': pflg++;
  561. X                break;
  562. X            case 'f':
  563. X                fflg++;
  564. X                preauth_flag++;
  565. X                STRFCPY (name, optarg);
  566. X                break;
  567. X#ifdef    RLOGIN
  568. X            case 'r':
  569. X                rflg++;
  570. X                STRFCPY (host, optarg);
  571. X#ifdef    UT_HOST
  572. X                STRFCPY (utent.ut_host, optarg);
  573. X#endif    /*UT_HOST*/
  574. X                if (rlogin (host, name, sizeof name))
  575. X                    preauth_flag++;
  576. X
  577. X                break;
  578. X            case 'h':
  579. X                hflg++;
  580. X                STRFCPY (host, optarg);
  581. X#ifdef    UT_HOST
  582. X                STRFCPY (utent.ut_host, optarg);
  583. X#endif    /*UT_HOST*/
  584. X                break;
  585. X#endif    /*RLOGIN*/
  586. X            default:
  587. X                usage ();
  588. X        }
  589. X    }
  590. X
  591. X#ifdef    RLOGIN
  592. X    /*
  593. X     * Neither -h nor -f should be combined with -r.
  594. X     */
  595. X
  596. X    if (rflg && (hflg || fflg))
  597. X        usage ();
  598. X#endif
  599. X
  600. X    /*
  601. X     * Allow authentication bypass only if real UID is zero.
  602. X     */
  603. X
  604. X    if ((rflg || fflg) && getuid () != 0) {
  605. X        fprintf(stderr, "%s: permission denied\n", Prog);
  606. X        exit (1);
  607. X    }
  608. X
  609. X#ifdef    USE_SYSLOG
  610. X    openlog (Prog, LOG_PID|LOG_CONS|LOG_NOWAIT, LOG_AUTH);
  611. X#endif
  612. X
  613. X    if (! isatty (0) || ! isatty (1) || ! isatty (2)) {
  614. X#ifdef    USE_SYSLOG
  615. X        closelog ();
  616. X#endif
  617. X        exit (1);        /* must be a terminal */
  618. X    }
  619. X#ifndef    BSD
  620. X    (void) ioctl (0, TCGETA, &termio); /* get terminal characteristics */
  621. X
  622. X    /*
  623. X     * Add your favorite terminal modes here ...
  624. X     */
  625. X
  626. X    termio.c_lflag |= ISIG;
  627. X
  628. X    termio.c_cc[VERASE] = getdef_num("ERASECHAR", '\b');
  629. X    termio.c_cc[VKILL] = getdef_num("KILLCHAR", '\025');
  630. X    (void) ioctl (0, TCSETAF, &termio);
  631. X#endif    /* !BSD */
  632. X    umask (getdef_num("UMASK", 0));
  633. X#ifdef HAVE_ULIMIT
  634. X    ulimit (2, getdef_long("ULIMIT", 2097152L));
  635. X#endif
  636. X
  637. X    /*
  638. X     * The entire environment will be preserved if the -p flag
  639. X     * is used.
  640. X     */
  641. X
  642. X    if (pflg)
  643. X        while (*envp)        /* add inherited environment, */
  644. X            addenv (*envp++); /* some variables change later */
  645. X
  646. X#ifdef    RLOGIN
  647. X    if (term[5] != '\0')        /* see if anything after "TERM=" */
  648. X        addenv (term);
  649. X#endif
  650. X    if (! getenv("TZ") && (cp = getdef_str("ENV_TZ")))
  651. X        addenv (*cp == '/' ? tz(cp) : cp);
  652. X    if (! getenv("HZ") && (cp = getdef_str("ENV_HZ")))
  653. X        addenv (cp);
  654. X
  655. X    if (optind < argc) {        /* get the user name */
  656. X        if (rflg || fflg)
  657. X            usage ();
  658. X
  659. X        STRFCPY (name, argv[optind]);
  660. X        ++optind;
  661. X    }
  662. X    if (optind < argc)        /* now set command line variables */
  663. X            setenv (argc - optind, &argv[optind]);
  664. X
  665. Xtop:
  666. X    (void) alarm (ALARM);        /* only allow ALARM sec. for login */
  667. X
  668. X    retries = RETRIES;
  669. X    while (1) {    /* repeatedly get login/password pairs */
  670. X        pass[0] = '\0';
  671. X
  672. X        if (! name[0]) {    /* need to get a login id */
  673. X            if (subroot) {
  674. X#ifdef    USE_SYSLOG
  675. X                closelog ();
  676. X#endif
  677. X                exit (1);
  678. X            }
  679. X#ifdef    RLOGIN
  680. X            preauth_flag = 0;
  681. X#endif
  682. X            login (name);
  683. X            continue;
  684. X        }
  685. X        if (! (pwd = getpwnam (name)))
  686. X            pwent.pw_name = (char *) 0;
  687. X        else
  688. X            pwent = *pwd;
  689. X
  690. X        if (pwent.pw_name) {
  691. X            if (! (spwd = getspnam (name)))
  692. X#ifdef    USE_SYSLOG
  693. X                syslog (LOG_WARN, NO_SHADOW, name, tty);
  694. X#else
  695. X                ;
  696. X#endif
  697. X            else
  698. X                pwent.pw_passwd = spwd->sp_pwdp;
  699. X            failed = 0;    /* hasn't failed validation yet */
  700. X        } else
  701. X            failed = 1;    /* will never pass validation */
  702. X
  703. X#ifdef    RLOGIN
  704. X        /*
  705. X         * The -r and -f flags provide a name which has already
  706. X         * been authenticated by some server.
  707. X         */
  708. X
  709. X        if (pwent.pw_name && preauth_flag)
  710. X            goto have_name;
  711. X#endif    /*RLOGIN*/
  712. X
  713. X    /*
  714. X     * Get the user's password.  One will only be prompted for
  715. X     * if the pw_passwd (or sp_passwd) field is non-blank.  It
  716. X     * will then be checked against the password entry, along
  717. X     * with other options which prevent logins.
  718. X     */
  719. X        cp = 0;
  720. X        if ((! pwent.pw_name || (strlen (pwent.pw_passwd) > 0))
  721. X                && ! (cp = getpass ("Password:")))
  722. X            continue;
  723. X
  724. X        if (cp) {
  725. X            STRFCPY (pass, cp);
  726. X            bzero (cp, strlen (cp));
  727. X        }
  728. X        if (! valid (pass, &pwent)) { /* check encrypted passwords */
  729. X#ifdef    USE_SYSLOG
  730. X            syslog (LOG_WARN, BAD_PASSWD, name, tty);
  731. X#endif
  732. X            failed = 1;
  733. X        }
  734. X        bzero (pass, sizeof pass);
  735. X
  736. X        /*
  737. X         * This is the point where password-authenticated users
  738. X         * wind up.  If you reach this far, your password has
  739. X         * been authenticated and so on.
  740. X         */
  741. X
  742. Xhave_name:
  743. X        if (getdef_bool("DIALUPS_CHECK_ENAB")) {
  744. X            alarm (30);
  745. X            if (pwent.pw_name &&
  746. X                    ! dialcheck (tty, pwent.pw_shell[0] ?
  747. X                    pwent.pw_shell:"/bin/sh")) {
  748. X#ifdef    USE_SYSLOG
  749. X                syslog (LOG_WARN, BAD_DIALUP, name, tty);
  750. X#endif
  751. X                failed = 1;
  752. X            }
  753. X        }
  754. X        if (getdef_bool("PORTTIME_CHECKS_ENAB") &&
  755. X            pwent.pw_name &&
  756. X            ! isttytime (pwent.pw_name, tty, time ((time_t *) 0))
  757. X        ) {
  758. X#ifdef    USE_SYSLOG
  759. X                syslog (LOG_WARN, BAD_TIME, name, tty);
  760. X#endif
  761. X                failed = 1;
  762. X        }
  763. X        if (! failed && pwent.pw_name && pwent.pw_uid == 0 &&
  764. X                ! console (tty)) {
  765. X#ifdef    USE_SYSLOG
  766. X            syslog (LOG_CRIT, BAD_ROOT_LOGIN, tty);
  767. X#endif
  768. X            failed = 1;
  769. X        }
  770. X        if (getdef_bool("FAILLOG_ENAB") && pwent.pw_name &&
  771. X                ! failcheck (pwent.pw_uid, &faillog, failed)) {
  772. X#ifdef    USE_SYSLOG
  773. X            syslog (LOG_CRIT, FAILURE_CNT, name, tty);
  774. X#endif
  775. X            failed = 1;
  776. X        }
  777. X        if (! failed)
  778. X            break;
  779. X
  780. X        puts ("Login incorrect");
  781. X#ifdef    RLOGIN
  782. X        if (rflg || fflg) {
  783. X#ifdef    USE_SYSLOG
  784. X            closelog ();
  785. X#endif
  786. X            exit (1);
  787. X        }
  788. X#endif    /*RLOGIN*/
  789. X
  790. X        /* don't log non-existent users */
  791. X        if (getdef_bool("FAILLOG_ENAB") && pwent.pw_name)
  792. X            failure (pwent.pw_uid, tty, &faillog);
  793. X        if (getdef_str("FTMP_FILE") != NULL) {
  794. X            failent = utent;
  795. X
  796. X            if (pwent.pw_name)
  797. X                STRFCPY (failent.ut_name, pwent.pw_name);
  798. X            else
  799. X                if (getdef_bool("LOG_UNKFAIL_ENAB"))
  800. X                    STRFCPY (failent.ut_name, name);
  801. X                else
  802. X                    STRFCPY (failent.ut_name, "UNKNOWN");
  803. X            time (&failent.ut_time);
  804. X            failent.ut_type = USER_PROCESS;
  805. X            failtmp (&failent);
  806. X        }
  807. X
  808. X        if (--retries <= 0) {    /* only allow so many failures */
  809. X#ifdef    USE_SYSLOG
  810. X            closelog ();
  811. X#endif
  812. X            exit (1);
  813. X        }
  814. X        bzero (name, sizeof name);
  815. X        bzero (pass, sizeof pass);
  816. X    }
  817. X    (void) alarm (0);        /* turn off alarm clock */
  818. X
  819. X    /*
  820. X     * Check to see if system is turned off for non-root users.
  821. X     * This would be useful to prevent users from logging in
  822. X     * during system maintenance.
  823. X     */
  824. X
  825. X    fname = getdef_str("NOLOGINS_FILE");
  826. X    if (pwent.pw_uid != 0 && fname != NULL && access (fname, 0) == 0) {
  827. X        FILE    *nlfp;
  828. X        int    c;
  829. X
  830. X        if (nlfp = fopen (fname, "r")) {
  831. X            while ((c = getc (nlfp)) != EOF) {
  832. X                if (c == '\n')
  833. X                    putchar ('\r');
  834. X
  835. X                putchar (c);
  836. X            }
  837. X            fflush (stdout);
  838. X            fclose (nlfp);
  839. X        } else
  840. X            printf ("\r\nSystem closed for routine maintenance\n");
  841. X
  842. X#ifdef    USE_SYSLOG
  843. X        closelog ();
  844. X#endif
  845. X        exit (0);
  846. X    }
  847. X
  848. X    environ = newenvp;        /* make new environment active */
  849. X
  850. X    if (getenv ("IFS"))        /* don't export user IFS ... */
  851. X        addenv ("IFS= \t\n");    /* ... instead, set a safe IFS */
  852. X
  853. X    setutmp (name, tty);        /* make entry in utmp & wtmp files */
  854. X    if (pwent.pw_shell[0] == '*') {    /* subsystem root */
  855. X        subsystem (&pwent);    /* figure out what to execute */
  856. X        subroot++;        /* say i was here again */
  857. X        endpwent ();        /* close all of the file which were */
  858. X        endgrent ();        /* open in the original rooted file */
  859. X        endspent ();        /* system.  they will be re-opened */
  860. X        endsgent ();        /* in the new rooted file system */
  861. X        goto top;        /* go do all this all over again */
  862. X    }
  863. X
  864. X    if (getdef_bool("LASTLOG_ENAB"))
  865. X        log ();            /* give last login and log this one */
  866. X    setup (&pwent);            /* set UID, GID, HOME, etc ... */
  867. X#ifdef    AGING
  868. X    if (spwd) {            /* check for age of password */
  869. X        if (expire (&pwent, spwd)) {
  870. X            spwd = getspnam (name);
  871. X            pwd = getpwnam (name);
  872. X            pwent = *pwd;
  873. X        }
  874. X    }
  875. X#ifdef    ATT_AGE
  876. X    else if (pwent.pw_age && pwent.pw_age[0]) {
  877. X        if (expire (&pwent, (void *) 0)) {
  878. X            pwd = getpwnam (name);
  879. X            pwent = *pwd;
  880. X        }
  881. X    }
  882. X#endif    /* ATT_AGE */
  883. X#endif    /* AGING */
  884. X    if (! hushed (&pwent)) {
  885. X        motd ();        /* print the message of the day */
  886. X        if (getdef_bool ("FAILLOG_ENAB") && faillog.fail_cnt != 0)
  887. X            failprint (&faillog);
  888. X        if (getdef_bool ("LASTLOG_ENAB") && lastlog.ll_time != 0)
  889. X            printf ("Last login: %.19s on %s\n",
  890. X                ctime (&lastlog.ll_time), lastlog.ll_line);
  891. X#ifdef    AGING
  892. X        agecheck (&pwent, spwd);
  893. X#endif    /* AGING */
  894. X        mailcheck ();    /* report on the status of mail */
  895. X    }
  896. X    if (getdef_str("TTYTYPE_FILE") != NULL && getenv("TERM") == NULL)
  897. X          ttytype (tty);
  898. X
  899. X    signal (SIGINT, SIG_DFL);    /* default interrupt signal */
  900. X    signal (SIGQUIT, SIG_DFL);    /* default quit signal */
  901. X    signal (SIGTERM, SIG_DFL);    /* default terminate signal */
  902. X    signal (SIGALRM, SIG_DFL);    /* default alarm signal */
  903. X
  904. X    endpwent ();            /* stop access to password file */
  905. X    endgrent ();            /* stop access to group file */
  906. X    endspent ();            /* stop access to shadow passwd file */
  907. X#ifdef    SHADOWGRP
  908. X    endsgent ();            /* stop access to shadow group file */
  909. X#endif
  910. X#ifdef    USE_SYSLOG
  911. X    if (pwent.pw_uid == 0)
  912. X        syslog (LOG_INFO, ROOT_LOGIN, tty);
  913. X
  914. X    closelog ();
  915. X#endif
  916. X    shell (pwent.pw_shell, (char *) 0); /* exec the shell finally. */
  917. X    /*NOTREACHED*/
  918. X}
  919. END_OF_FILE
  920.   if test 14004 -ne `wc -c <'lmain.c'`; then
  921.     echo shar: \"'lmain.c'\" unpacked with wrong size!
  922.   fi
  923.   # end of 'lmain.c'
  924. fi
  925. if test -f 'useradd.c' -a "${1}" != "-c" ; then 
  926.   echo shar: Will not clobber existing file \"'useradd.c'\"
  927. else
  928.   echo shar: Extracting \"'useradd.c'\" \(25685 characters\)
  929.   sed "s/^X//" >'useradd.c' <<'END_OF_FILE'
  930. X/*
  931. X * Copyright 1991, John F. Haugh II
  932. X * All rights reserved.
  933. X *
  934. X * Permission is granted to copy and create derivative works for any
  935. X * non-commercial purpose, provided this copyright notice is preserved
  936. X * in all copies of source code, or included in human readable form
  937. X * and conspicuously displayed on all copies of object code or
  938. X * distribution media.
  939. X */
  940. X
  941. X#ifndef lint
  942. Xstatic    char    sccsid[] = "@(#)useradd.c    3.6    14:38:26    10/27/91";
  943. X#endif
  944. X
  945. X#include <sys/types.h>
  946. X#include <sys/stat.h>
  947. X#include <stdio.h>
  948. X#include <errno.h>
  949. X#include "pwd.h"
  950. X#include <grp.h>
  951. X#include <ctype.h>
  952. X#include <fcntl.h>
  953. X#include <time.h>
  954. X
  955. X#ifdef    BSD
  956. X#include <strings.h>
  957. X#else
  958. X#include <string.h>
  959. X#endif
  960. X
  961. X#include "config.h"
  962. X#include "shadow.h"
  963. X
  964. X#ifdef    USE_SYSLOG
  965. X#include <syslog.h>
  966. X
  967. X#ifndef    LOG_WARN
  968. X#define    LOG_WARN LOG_WARNING
  969. X#endif
  970. X#endif
  971. X
  972. Xgid_t    def_group;
  973. Xchar    def_home[BUFSIZ];
  974. Xchar    def_shell[BUFSIZ];
  975. Xchar    def_template[BUFSIZ] = "/etc/skel";
  976. Xlong    def_inactive;
  977. Xlong    def_expire;
  978. Xchar    def_file[] = "/etc/default/useradd";
  979. X
  980. X#ifndef    NGROUPS_MAX
  981. X#define    NGROUPS_MAX    64
  982. X#endif
  983. X
  984. X#define    VALID(s)    (strcspn (s, ":\n") == strlen (s))
  985. X
  986. Xchar    user_name[BUFSIZ];
  987. Xuid_t    user_id;
  988. Xgid_t    user_gid;
  989. Xchar    user_comment[BUFSIZ];
  990. Xchar    user_home[BUFSIZ];
  991. Xchar    user_shell[BUFSIZ];
  992. Xlong    user_expire;
  993. Xint    user_ngroups;
  994. Xgid_t    user_groups[NGROUPS_MAX];
  995. X
  996. Xchar    *Prog;
  997. X
  998. Xint    uflg;    /* specify user ID for new account                            */
  999. Xint    oflg;    /* permit non-unique user ID to be specified with -u          */
  1000. Xint    gflg;    /* primary group ID  for new account                          */
  1001. Xint    Gflg;    /* secondary group set for new account                        */
  1002. Xint    dflg;    /* home directory for new account                             */
  1003. Xint    bflg;    /* new default root of home directory                         */
  1004. Xint    sflg;    /* shell program for new account                              */
  1005. Xint    cflg;    /* comment (GECOS) field for new account                      */
  1006. Xint    mflg;    /* create user's home directory if it doesn't exist           */
  1007. Xint    kflg;    /* specify a directory to fill new user directory             */
  1008. Xint    fflg;    /* days until account with expired password is locked         */
  1009. Xint    eflg;    /* days after password changed before it becomes expired      */
  1010. Xint    Dflg;    /* set/show new user default values                           */
  1011. X
  1012. X#ifdef NDBM
  1013. Xextern    int    pw_dbm_mode;
  1014. Xextern    int    sp_dbm_mode;
  1015. Xextern    int    gr_dbm_mode;
  1016. X#ifdef    SHADOWGRP
  1017. Xextern    int    sg_dbm_mode;
  1018. X#endif
  1019. X#endif
  1020. Xextern    FILE    *fopen();
  1021. Xextern    int    fclose();
  1022. Xextern    char    *malloc();
  1023. Xextern    char    *mktemp();
  1024. X
  1025. Xextern    struct    group    *getgrnam();
  1026. Xextern    struct    group    *getgrgid();
  1027. Xextern    struct    group    *gr_next();
  1028. Xextern    struct    group    *gr_locate();
  1029. Xextern    int    gr_lock();
  1030. Xextern    int    gr_unlock();
  1031. Xextern    int    gr_rewind();
  1032. Xextern    int    gr_open();
  1033. X
  1034. X#ifdef    SHADOWGRP
  1035. Xextern    struct    sgrp    *sgr_next();
  1036. Xextern    int    sgr_lock();
  1037. Xextern    int    sgr_unlock();
  1038. Xextern    int    sgr_rewind();
  1039. Xextern    int    sgr_open();
  1040. X#endif
  1041. X
  1042. Xextern    struct    passwd    *getpwnam();
  1043. Xextern    struct    passwd    *pw_next();
  1044. Xextern    int    pw_lock();
  1045. Xextern    int    pw_unlock();
  1046. Xextern    int    pw_rewind();
  1047. Xextern    int    pw_open();
  1048. X
  1049. Xextern    int    spw_lock();
  1050. Xextern    int    spw_unlock();
  1051. Xextern    int    spw_open();
  1052. X
  1053. X#define    DAY    (24L*3600L)
  1054. X#define    WEEK    (7*DAY)
  1055. X
  1056. X#ifdef    ITI_AGING
  1057. X#define    SCALE    (1)
  1058. X#else
  1059. X#define    SCALE    (DAY)
  1060. X#endif
  1061. X
  1062. X/*
  1063. X * days and juldays are used to compute the number of days in the
  1064. X * current month, and the cummulative number of days in the preceding
  1065. X * months.  they are declared so that january is 1, not 0.
  1066. X */
  1067. X
  1068. Xstatic    short    days[13] = { 0,
  1069. X    31,    28,    31,    30,    31,    30,    /* JAN - JUN */
  1070. X    31,    31,    30,    31,    30,    31 };    /* JUL - DEC */
  1071. X
  1072. Xstatic    short    juldays[13] = { 0,
  1073. X    0,    31,    59,    90,    120,    151,    /* JAN - JUN */
  1074. X    181,    212,    243,    273,    304,    334 };    /* JUL - DEC */
  1075. X
  1076. X#ifdef    NEED_RENAME
  1077. X/*
  1078. X * rename - rename a file to another name
  1079. X *
  1080. X *    rename is provided for systems which do not include the rename()
  1081. X *    system call.
  1082. X */
  1083. X
  1084. Xint
  1085. Xrename (begin, end)
  1086. Xchar    *begin;
  1087. Xchar    *end;
  1088. X{
  1089. X    struct    stat    s1, s2;
  1090. X    extern    int    errno;
  1091. X    int    orig_err = errno;
  1092. X
  1093. X    if (stat (begin, &s1))
  1094. X        return -1;
  1095. X
  1096. X    if (stat (end, &s2)) {
  1097. X        errno = orig_err;
  1098. X    } else {
  1099. X
  1100. X        /*
  1101. X         * See if this is a cross-device link.  We do this to
  1102. X         * insure that the link below has a chance of working.
  1103. X         */
  1104. X
  1105. X        if (s1.st_dev != s2.st_dev) {
  1106. X            errno = EXDEV;
  1107. X            return -1;
  1108. X        }
  1109. X
  1110. X        /*
  1111. X         * See if we can unlink the existing destination
  1112. X         * file.  If the unlink works the directory is writable,
  1113. X         * so there is no need here to figure that out.
  1114. X         */
  1115. X
  1116. X        if (unlink (end))
  1117. X            return -1;
  1118. X    }
  1119. X
  1120. X    /*
  1121. X     * Now just link the original name to the final name.  If there
  1122. X     * was no file previously, this link will fail if the target
  1123. X     * directory isn't writable.  The unlink will fail if the source
  1124. X     * directory isn't writable, but life stinks ...
  1125. X     */
  1126. X
  1127. X    if (link (begin, end) || unlink (begin))
  1128. X        return -1;
  1129. X
  1130. X    return 0;
  1131. X}
  1132. X#endif
  1133. X
  1134. X/*
  1135. X * strtoday - compute the number of days since 1970.
  1136. X *
  1137. X * the total number of days prior to the current date is
  1138. X * computed.  january 1, 1970 is used as the origin with
  1139. X * it having a day number of 0.
  1140. X */
  1141. X
  1142. Xlong
  1143. Xstrtoday (str)
  1144. Xchar    *str;
  1145. X{
  1146. X    char    slop[2];
  1147. X    int    month;
  1148. X    int    day;
  1149. X    int    year;
  1150. X    long    total;
  1151. X
  1152. X    /*
  1153. X     * start by separating the month, day and year.  this is
  1154. X     * a chauvanistic program - it only takes date input in
  1155. X     * the standard USA format.
  1156. X     */
  1157. X
  1158. X    if (sscanf (str, "%d/%d/%d%c", &month, &day, &year, slop) != 3)
  1159. X        return -1;
  1160. X
  1161. X    /*
  1162. X     * the month, day of the month, and year are checked for
  1163. X     * correctness and the year adjusted so it falls between
  1164. X     * 1970 and 2069.
  1165. X     */
  1166. X
  1167. X    if (month < 1 || month > 12)
  1168. X        return -1;
  1169. X
  1170. X    if (day < 1)
  1171. X        return -1;
  1172. X
  1173. X    if ((month != 2 || (year % 4) != 0) && day > days[month])
  1174. X        return -1;
  1175. X    else if ((month == 2 && (year % 4) == 0) && day > 29)
  1176. X        return -1;
  1177. X
  1178. X    if (year < 0)
  1179. X        return -1;
  1180. X    else if (year < 69)
  1181. X        year += 2000;
  1182. X    else if (year < 99)
  1183. X        year += 1900;
  1184. X
  1185. X    if (year < 1970 || year > 2069)
  1186. X        return -1;
  1187. X
  1188. X    /*
  1189. X     * the total number of days is the total number of days in all
  1190. X     * the whole years, plus the number of leap days, plus the
  1191. X     * number of days in the whole months preceding, plus the number
  1192. X     * of days so far in the month.
  1193. X     */
  1194. X
  1195. X    total = (long) ((year - 1970) * 365L) + (((year + 1) - 1970) / 4);
  1196. X    total += (long) juldays[month] + (month > 2 && (year % 4) == 0 ? 1:0);
  1197. X    total += (long) day - 1;
  1198. X
  1199. X    return total;
  1200. X}
  1201. X
  1202. X/*
  1203. X * add_list - add a member to a list of group members
  1204. X *
  1205. X *    the array of member names is searched for the new member
  1206. X *    name, and if not present it is added to a freshly allocated
  1207. X *    list of users.
  1208. X */
  1209. X
  1210. Xchar **
  1211. Xadd_list (list, member)
  1212. Xchar    **list;
  1213. Xchar    *member;
  1214. X{
  1215. X    int    i;
  1216. X    char    **tmp;
  1217. X
  1218. X    /*
  1219. X     * Scan the list for the new name.  Return the original list
  1220. X     * pointer if it is present.
  1221. X     */
  1222. X
  1223. X    for (i = 0;list[i] != (char *) 0;i++)
  1224. X        if (strcmp (list[i], member) == 0)
  1225. X            return list;
  1226. X
  1227. X    /*
  1228. X     * Allocate a new list pointer large enough to hold all the
  1229. X     * old entries, and the new entries as well.
  1230. X     */
  1231. X
  1232. X    if (! (tmp = (char **) malloc ((i + 2) * sizeof member)))
  1233. X        return 0;
  1234. X
  1235. X    /*
  1236. X     * Copy the original list to the new list, then append the
  1237. X     * new member and NULL terminate the result.  This new list
  1238. X     * is returned to the invoker.
  1239. X     */
  1240. X
  1241. X    for (i = 0;list[i] != (char *) 0;i++)
  1242. X        tmp[i] = list[i];
  1243. X
  1244. X    tmp[i++] = strdup (member);
  1245. X    tmp[i] = (char *) 0;
  1246. X
  1247. X    return tmp;
  1248. X}
  1249. X
  1250. X/*
  1251. X * get_defaults - read the defaults file
  1252. X *
  1253. X *    get_defaults() reads the defaults file for this command.  It sets
  1254. X *    the various values from the file, or uses built-in default values
  1255. X *    if the file does not exist.
  1256. X */
  1257. X
  1258. Xvoid
  1259. Xget_defaults ()
  1260. X{
  1261. X    FILE    *fp;
  1262. X    char    buf[BUFSIZ];
  1263. X    char    *cp;
  1264. X    struct    group    *grp;
  1265. X
  1266. X    /*
  1267. X     * Open the defaults file for reading.
  1268. X     */
  1269. X
  1270. X    if (! (fp = fopen (def_file, "r"))) {
  1271. X
  1272. X        /*
  1273. X         * No defaults file - set up the defaults that are given
  1274. X         * in the documentation.
  1275. X         */
  1276. X
  1277. X        def_group = 1;
  1278. X        strcpy (def_home, "/home");
  1279. X        def_inactive = 0;
  1280. X        def_expire = 0;
  1281. X        return;
  1282. X    }
  1283. X
  1284. X    /*
  1285. X     * Read the file a line at a time.  Only the lines that have
  1286. X     * relevant values are used, everything else can be ignored.
  1287. X     */
  1288. X
  1289. X    while (fgets (buf, BUFSIZ, fp)) {
  1290. X        if (cp = strrchr (buf, '\n'))
  1291. X            *cp = '\0';
  1292. X
  1293. X        /*
  1294. X         * Primary GROUP identifier
  1295. X         */
  1296. X
  1297. X        if (strncmp ("GROUP=", buf, 6) == 0) {
  1298. X            cp = buf + 6;
  1299. X            if (isdigit (*cp))
  1300. X                def_group = atoi (cp);
  1301. X            else if (grp = getgrnam (cp))
  1302. X                def_group = grp->gr_gid;
  1303. X            else
  1304. X                fprintf (stderr, "%s: unknown group %s\n",
  1305. X                    Prog, cp);
  1306. X        }
  1307. X        
  1308. X        /*
  1309. X         * Default HOME filesystem
  1310. X         */
  1311. X         
  1312. X        else if (strncmp ("HOME=", buf, 5) == 0) {
  1313. X            strncpy (def_home, buf + 5, BUFSIZ);
  1314. X        }
  1315. X
  1316. X        /*
  1317. X         * Default Login Shell command
  1318. X         */
  1319. X
  1320. X        else if (strncmp ("SHELL=", buf, 6) == 0) {
  1321. X            strncpy (def_shell, buf + 6, BUFSIZ);
  1322. X        }
  1323. X
  1324. X        /*
  1325. X         * Default Password Inactive value
  1326. X         */
  1327. X
  1328. X        else if (strncmp ("INACTIVE=", buf, 9) == 0) {
  1329. X            def_inactive = atoi (buf + 9);
  1330. X        }
  1331. X        
  1332. X        /*
  1333. X         * Default Password Expiration value
  1334. X         */
  1335. X
  1336. X        else if (strncmp ("EXPIRE=", buf, 7) == 0) {
  1337. X            def_expire = atoi (buf + 7);
  1338. X        }
  1339. X    }
  1340. X}
  1341. X
  1342. X/*
  1343. X * show_defaults - show the contents of the defaults file
  1344. X *
  1345. X *    show_defaults() displays the values that are used from the default
  1346. X *    file and the built-in values.
  1347. X */
  1348. X
  1349. Xvoid
  1350. Xshow_defaults ()
  1351. X{
  1352. X    printf ("GROUP=%d\n", def_group);
  1353. X    printf ("HOME=%s\n", def_home);
  1354. X    printf ("INACTIVE=%d\n", def_inactive);
  1355. X    printf ("EXPIRE=%d\n", def_expire);
  1356. X}
  1357. X
  1358. X/*
  1359. X * set_defaults - write new defaults file
  1360. X *
  1361. X *    set_defaults() re-writes the defaults file using the values that
  1362. X *    are currently set.  Duplicated lines are pruned, missing lines are
  1363. X *    added, and unrecognized lines are copied as is.
  1364. X */
  1365. X
  1366. Xint
  1367. Xset_defaults ()
  1368. X{
  1369. X    FILE    *ifp;
  1370. X    FILE    *ofp;
  1371. X    char    buf[BUFSIZ];
  1372. X    static    char    new_file[] = "/etc/default/nuaddXXXXXX";
  1373. X    char    *cp;
  1374. X    int    out_group = 0;
  1375. X    int    out_home = 0;
  1376. X    int    out_inactive = 0;
  1377. X    int    out_expire = 0;
  1378. X
  1379. X    /*
  1380. X     * Create a temporary file to copy the new output to.
  1381. X     */
  1382. X
  1383. X    mktemp (new_file);
  1384. X    if (! (ofp = fopen (new_file, "w"))) {
  1385. X        fprintf (stderr, "%s: cannot create new defaults file\n", Prog);
  1386. X        return -1;
  1387. X    }
  1388. X
  1389. X    /*
  1390. X     * Open the existing defaults file and copy the lines to the
  1391. X     * temporary files, using any new values.  Each line is checked
  1392. X     * to insure that it is not output more than once.
  1393. X     */
  1394. X
  1395. X    if (ifp = fopen (def_file, "r")) {
  1396. X        while (fgets (buf, BUFSIZ, ifp)) {
  1397. X            if (cp = strrchr (buf, '\n'))
  1398. X                *cp = '\0';
  1399. X
  1400. X            if (strncmp ("GROUP=", buf, 6) == 0) {
  1401. X                if (! out_group)
  1402. X                    fprintf (ofp, "GROUP=%d\n", def_group);
  1403. X
  1404. X                out_group++;
  1405. X            } else if (strncmp ("HOME=", buf, 5) == 0) {
  1406. X                if (! out_home)
  1407. X                    fprintf (ofp, "HOME=%s\n", def_home);
  1408. X
  1409. X                out_home++;
  1410. X            } else if (strncmp ("INACTIVE=", buf, 9) == 0) {
  1411. X                if (! out_inactive)
  1412. X                    fprintf (ofp, "INACTIVE=%d\n",
  1413. X                        def_inactive);
  1414. X
  1415. X                out_inactive++;
  1416. X            } else if (strncmp ("EXPIRE=", buf, 7) == 0) {
  1417. X                if (! out_expire)
  1418. X                    fprintf (ofp, "EXPIRE=%d\n",
  1419. X                        def_expire);
  1420. X
  1421. X                out_expire++;
  1422. X            } else
  1423. X                fprintf (ofp, "%s\n", buf);
  1424. X        }
  1425. X        fclose ((FILE *) ifp);
  1426. X    }
  1427. X
  1428. X    /*
  1429. X     * Check each line to insure that every line was output.  This
  1430. X     * causes new values to be added to a file which did not previously
  1431. X     * have an entry for that value.
  1432. X     */
  1433. X
  1434. X    if (! out_group)
  1435. X        fprintf (ofp, "GROUP=%d\n", def_group);
  1436. X
  1437. X    if (! out_home)
  1438. X        fprintf (ofp, "HOME=%s\n", def_home);
  1439. X
  1440. X    if (! out_inactive)
  1441. X        fprintf (ofp, "INACTIVE=%d\n", def_inactive);
  1442. X
  1443. X    if (! out_expire)
  1444. X        fprintf (ofp, "EXPIRE=%d\n", def_expire);
  1445. X
  1446. X    /*
  1447. X     * Flush and close the file.  Check for errors to make certain
  1448. X     * the new file is intact.
  1449. X     */
  1450. X
  1451. X    (void) fflush (ofp);
  1452. X    if (ferror (ofp) || fclose ((FILE *) ofp)) {
  1453. X        unlink (new_file);
  1454. X        return -1;
  1455. X    }
  1456. X
  1457. X    /*
  1458. X     * Rename the current default file to its backup name.
  1459. X     */
  1460. X
  1461. X    sprintf (buf, "%s-", def_file);
  1462. X    if (rename (def_file, buf) && errno != ENOENT) {
  1463. X        sprintf (buf, "%s: rename: %s", Prog, def_file);
  1464. X        perror (buf);
  1465. X        unlink (new_file);
  1466. X        return -1;
  1467. X    }
  1468. X
  1469. X    /*
  1470. X     * Rename the new default file to its correct name.
  1471. X     */
  1472. X
  1473. X    if (rename (new_file, def_file)) {
  1474. X        sprintf (buf, "%s: rename: %s", Prog, new_file);
  1475. X        perror (buf);
  1476. X        return -1;
  1477. X    }
  1478. X#ifdef    USE_SYSLOG
  1479. X    syslog (LOG_INFO, "defaults: group=%d, home=%s, inactive=%d, expire=%d",
  1480. X        def_group, def_home, def_inactive, def_expire);
  1481. X#endif
  1482. X    return 0;
  1483. X}
  1484. X
  1485. X/*
  1486. X * get_groups - convert a list of group names to an array of group IDs
  1487. X *
  1488. X *    get_groups() takes a comma-separated list of group names and
  1489. X *    converts it to an array of group ID values.  Any unknown group
  1490. X *    names are reported as errors.
  1491. X */
  1492. X
  1493. Xint
  1494. Xget_groups (list)
  1495. Xchar    *list;
  1496. X{
  1497. X    char    *cp;
  1498. X    struct    group    *grp;
  1499. X    int    errors = 0;
  1500. X
  1501. X    /*
  1502. X     * Initialize the list to be empty
  1503. X     */
  1504. X
  1505. X    user_ngroups = 0;
  1506. X
  1507. X    if (! *list)
  1508. X        return 0;
  1509. X
  1510. X    /*
  1511. X     * So long as there is some data to be converted, strip off
  1512. X     * each name and look it up.  A mix of numerical and string
  1513. X     * values for group identifiers is permitted.
  1514. X     */
  1515. X
  1516. X    do {
  1517. X        /*
  1518. X         * Strip off a single name from the list
  1519. X         */
  1520. X
  1521. X        if (cp = strchr (list, ','))
  1522. X            *cp++ = '\0';
  1523. X
  1524. X        /*
  1525. X         * Names starting with digits are treated as numerical
  1526. X         * GID values, otherwise the string is looked up as is.
  1527. X         */
  1528. X
  1529. X        if (isdigit (*list))
  1530. X            grp = getgrgid (atoi (list));
  1531. X        else
  1532. X            grp = getgrnam (list);
  1533. X
  1534. X        /*
  1535. X         * There must be a match, either by GID value or by
  1536. X         * string name.
  1537. X         */
  1538. X
  1539. X        if (! grp) {
  1540. X            fprintf (stderr, "%s: unknown group %s\n", Prog, list);
  1541. X            errors++;
  1542. X        }
  1543. X
  1544. X        /*
  1545. X         * Add the GID value from the group file to the user's
  1546. X         * list of groups.
  1547. X         */
  1548. X
  1549. X        user_groups[user_ngroups++] = grp->gr_gid;
  1550. X
  1551. X        list = cp;
  1552. X    } while (list);
  1553. X
  1554. X    /*
  1555. X     * Any errors in finding group names are fatal
  1556. X     */
  1557. X
  1558. X    if (errors)
  1559. X        return -1;
  1560. X
  1561. X    return 0;
  1562. X}
  1563. X
  1564. X/*
  1565. X * usage - display usage message and exit
  1566. X */
  1567. X
  1568. Xusage ()
  1569. X{
  1570. X    fprintf (stderr,
  1571. X        "usage:\t%s [-u uid [-o]] [-g group] [-G group,...] \n", Prog);
  1572. X    fprintf (stderr,
  1573. X        "\t\t[-d home] [-s shell] [-c comment] [-m [-k template]]\n");
  1574. X    fprintf (stderr,
  1575. X        "\t\t[-f inactive] [-e expire] name\n");
  1576. X
  1577. X    fprintf (stderr,
  1578. X        "\t%s -D [-g group] [-b base] [-f inactive] [-e expire]\n",
  1579. X            Prog);
  1580. X
  1581. X    exit (1);
  1582. X}
  1583. X
  1584. X/*
  1585. X * new_pwent - initialize the values in a password file entry
  1586. X *
  1587. X *    new_pwent() takes all of the values that have been entered and
  1588. X *    fills in a (struct passwd) with them.
  1589. X */
  1590. X
  1591. Xvoid
  1592. Xnew_pwent (pwent)
  1593. Xstruct    passwd    *pwent;
  1594. X{
  1595. X    memset (pwent, 0, sizeof *pwent);
  1596. X    pwent->pw_name = user_name;
  1597. X    pwent->pw_passwd = "*";
  1598. X    pwent->pw_age = "";
  1599. X    pwent->pw_uid = user_id;
  1600. X    pwent->pw_gid = user_gid;
  1601. X    pwent->pw_gecos = user_comment;
  1602. X    pwent->pw_comment = "";
  1603. X    pwent->pw_dir = user_home;
  1604. X    pwent->pw_shell = user_shell;
  1605. X}
  1606. X
  1607. X/*
  1608. X * new_spent - initialize the values in a shadow password file entry
  1609. X *
  1610. X *    new_spent() takes all of the values that have been entered and
  1611. X *    fills in a (struct spwd) with them.
  1612. X */
  1613. X
  1614. Xvoid
  1615. Xnew_spent (spent)
  1616. Xstruct    spwd    *spent;
  1617. X{
  1618. X    memset (spent, 0, sizeof *spent);
  1619. X    spent->sp_namp = user_name;
  1620. X    spent->sp_pwdp = "!";
  1621. X    spent->sp_lstchg = 0;
  1622. X    spent->sp_min = 0;
  1623. X    spent->sp_max = def_expire;
  1624. X    spent->sp_warn = 0;
  1625. X    spent->sp_inact = def_inactive;
  1626. X    spent->sp_expire = user_expire;
  1627. X}
  1628. X
  1629. X/*
  1630. X * grp_update - add user to secondary group set
  1631. X *
  1632. X *    grp_update() takes the secondary group set given in user_groups
  1633. X *    and adds the user to each group given by that set.
  1634. X */
  1635. X
  1636. Xvoid
  1637. Xgrp_update ()
  1638. X{
  1639. X    int    i;
  1640. X    struct    group    *grp;
  1641. X    struct    sgrp    *sgrp;
  1642. X
  1643. X    /*
  1644. X     * Lock and open the group file.  This will load all of the group
  1645. X     * entries.
  1646. X     */
  1647. X
  1648. X    if (! gr_lock ()) {
  1649. X        fprintf (stderr, "%s: error locking group file\n", Prog);
  1650. X        exit (1);
  1651. X    }
  1652. X    if (! gr_open (O_RDWR)) {
  1653. X        fprintf (stderr, "%s: error opening group file\n", Prog);
  1654. X        exit (1);
  1655. X    }
  1656. X#ifdef    SHADOWGRP
  1657. X    if (! sgr_lock ()) {
  1658. X        fprintf (stderr, "%s: error locking shadow group file\n", Prog);
  1659. X        exit (1);
  1660. X    }
  1661. X    if (! sgr_open (O_RDWR)) {
  1662. X        fprintf (stderr, "%s: error opening shadow group file\n", Prog);
  1663. X        exit (1);
  1664. X    }
  1665. X#endif
  1666. X
  1667. X    /*
  1668. X     * Scan through the entire group file looking for the groups that
  1669. X     * the user is a member of.
  1670. X     */
  1671. X
  1672. X    for (gr_rewind (), grp = gr_next ();grp;grp = gr_next ()) {
  1673. X
  1674. X        /*
  1675. X         * See if the user specified this group as one of their
  1676. X         * concurrent groups.
  1677. X         */
  1678. X
  1679. X        for (i = 0;i < user_ngroups;i++)
  1680. X            if (grp->gr_gid == user_groups[i])
  1681. X                break;
  1682. X
  1683. X        if (i == user_ngroups)
  1684. X            continue;
  1685. X
  1686. X        /* 
  1687. X         * Add the username to the list of group members and
  1688. X         * update the group entry to reflect the change.
  1689. X         */
  1690. X
  1691. X        grp->gr_mem = add_list (grp->gr_mem, user_name);
  1692. X        if (! gr_update (grp)) {
  1693. X            fprintf (stderr, "%s: error adding new group entry\n",
  1694. X                Prog);
  1695. X            exit (1);
  1696. X        }
  1697. X#ifdef    NDBM
  1698. X        /*
  1699. X         * Update the DBM group file with the new entry as well.
  1700. X         */
  1701. X
  1702. X        if (! gr_dbm_update (grp)) {
  1703. X            fprintf (stderr, "%s: cannot add new dbm group entry\n",
  1704. X                Prog);
  1705. X            exit (1);
  1706. X        }
  1707. X        endgrent ();
  1708. X#endif
  1709. X#ifdef    USE_SYSLOG
  1710. X        syslog (LOG_INFO, "add `%s' to group `%s'\n",
  1711. X            user_name, grp->gr_name);
  1712. X#endif
  1713. X    }
  1714. X
  1715. X#ifdef    SHADOWGRP
  1716. X    /*
  1717. X     * Scan through the entire shadow group file looking for the groups
  1718. X     * that the user is a member of.  The administrative list isn't
  1719. X     * modified.
  1720. X     */
  1721. X
  1722. X    for (sgr_rewind (), sgrp = sgr_next ();sgrp;sgrp = sgr_next ()) {
  1723. X
  1724. X        /*
  1725. X         * See if the user specified this group as one of their
  1726. X         * concurrent groups.
  1727. X         */
  1728. X
  1729. X        for (i = 0;i < user_ngroups;i++) {
  1730. X            if (! (grp = gr_locate (sgrp->sg_name)))
  1731. X                continue;
  1732. X
  1733. X            if (grp->gr_gid == user_groups[i])
  1734. X                break;
  1735. X        }
  1736. X        if (i == user_ngroups)
  1737. X            continue;
  1738. X
  1739. X        /* 
  1740. X         * Add the username to the list of group members and
  1741. X         * update the group entry to reflect the change.
  1742. X         */
  1743. X
  1744. X        sgrp->sg_mem = add_list (sgrp->sg_mem, user_name);
  1745. X        if (! sgr_update (sgrp)) {
  1746. X            fprintf (stderr, "%s: error adding new group entry\n",
  1747. X                Prog);
  1748. X            exit (1);
  1749. X        }
  1750. X#ifdef    NDBM
  1751. X        /*
  1752. X         * Update the DBM group file with the new entry as well.
  1753. X         */
  1754. X
  1755. X        if (! sgr_dbm_update (sgrp)) {
  1756. X            fprintf (stderr, "%s: cannot add new dbm group entry\n",
  1757. X                Prog);
  1758. X            exit (1);
  1759. X        }
  1760. X        endsgent ();
  1761. X#endif
  1762. X#ifdef    USE_SYSLOG
  1763. X        syslog (LOG_INFO, "add `%s' to shadow group `%s'\n",
  1764. X            user_name, sgrp->sg_name);
  1765. X#endif
  1766. X    }
  1767. X#endif
  1768. X}
  1769. X
  1770. X/*
  1771. X * find_new_uid - find the next available UID
  1772. X *
  1773. X *    find_new_uid() locates the next highest unused UID in the password
  1774. X *    file, or checks the given user ID against the existing ones for
  1775. X *    uniqueness.
  1776. X */
  1777. X
  1778. Xint
  1779. Xfind_new_uid ()
  1780. X{
  1781. X    struct    passwd    *pwd;
  1782. X
  1783. X    /*
  1784. X     * Start with some UID value if the user didn't provide us with
  1785. X     * one already.
  1786. X     */
  1787. X
  1788. X    if (! uflg)
  1789. X        user_id = 100;
  1790. X
  1791. X    /*
  1792. X     * Search the entire password file, either looking for this
  1793. X     * UID (if the user specified one with -u) or looking for the
  1794. X     * largest unused value.
  1795. X     */
  1796. X
  1797. X    for (pw_rewind (), pwd = pw_next ();pwd;pwd = pw_next ()) {
  1798. X        if (strcmp (user_name, pwd->pw_name) == 0) {
  1799. X            fprintf (stderr, "%s: name %s is not unique\n",
  1800. X                Prog, user_name);
  1801. X            exit (1);
  1802. X        }
  1803. X        if (uflg && user_id == pwd->pw_uid) {
  1804. X            fprintf (stderr, "%s: uid %d is not unique\n",
  1805. X                Prog, user_id);
  1806. X            exit (1);
  1807. X        }
  1808. X        if (! uflg && pwd->pw_uid >= user_id)
  1809. X            user_id = pwd->pw_uid + 1;
  1810. X    }
  1811. X}
  1812. X
  1813. X/*
  1814. X * process_flags - perform command line argument setting
  1815. X *
  1816. X *    process_flags() interprets the command line arguments and sets
  1817. X *    the values that the user will be created with accordingly.  The
  1818. X *    values are checked for sanity.
  1819. X */
  1820. X
  1821. Xvoid
  1822. Xprocess_flags (argc, argv)
  1823. Xint    argc;
  1824. Xchar    **argv;
  1825. X{
  1826. X    extern    int    optind;
  1827. X    extern    char    *optarg;
  1828. X    struct    group    *grp;
  1829. X    int    anyflag = 0;
  1830. X    int    arg;
  1831. X
  1832. X    while ((arg = getopt (argc, argv, "Du:og:G:d:s:c:mk:f:e:b:")) != EOF) {
  1833. X        switch (arg) {
  1834. X            case 'b':
  1835. X                if (! VALID (optarg)) {
  1836. X                    fprintf (stderr,
  1837. X                        "%s: invalid field `%s'\n",
  1838. X                        Prog, optarg);
  1839. X                    exit (3);
  1840. X                }
  1841. X                bflg++;
  1842. X                if (! Dflg)
  1843. X                    usage ();
  1844. X
  1845. X                strncpy (def_home, optarg, BUFSIZ);
  1846. X                break;
  1847. X            case 'c':
  1848. X                if (! VALID (optarg)) {
  1849. X                    fprintf (stderr,
  1850. X                        "%s: invalid field `%s'\n",
  1851. X                        Prog, optarg);
  1852. X                    exit (3);
  1853. X                }
  1854. X                cflg++;
  1855. X                strncpy (user_comment, optarg, BUFSIZ);
  1856. X                break;
  1857. X            case 'd':
  1858. X                if (! VALID (optarg)) {
  1859. X                    fprintf (stderr,
  1860. X                        "%s: invalid field `%s'\n",
  1861. X                        Prog, optarg);
  1862. X                    exit (3);
  1863. X                }
  1864. X                dflg++;
  1865. X                strncpy (user_home, optarg, BUFSIZ);
  1866. X                break;
  1867. X            case 'D':
  1868. X                if (anyflag)
  1869. X                    usage ();
  1870. X
  1871. X                Dflg++;
  1872. X                break;
  1873. X            case 'e':
  1874. X                eflg++;
  1875. X                if (Dflg)
  1876. X                    def_expire = atoi (optarg);
  1877. X                else {
  1878. X                    user_expire = strtoday (optarg);
  1879. X#ifdef    ITI_AGING
  1880. X                    user_expire *= DAY;
  1881. X#endif
  1882. X                }
  1883. X                break;
  1884. X            case 'f':
  1885. X                fflg++;
  1886. X                def_inactive = atoi (optarg);
  1887. X                break;
  1888. X            case 'g':
  1889. X                gflg++;
  1890. X                if (isdigit (optarg[0]))
  1891. X                    grp = getgrgid (atoi (optarg));
  1892. X                else
  1893. X                    grp = getgrnam (optarg);
  1894. X
  1895. X                if (! grp) {
  1896. X                    fprintf (stderr,
  1897. X                        "%s: unknown group %s\n",
  1898. X                        Prog, optarg);
  1899. X                    exit (1);
  1900. X                }
  1901. X                user_gid = grp->gr_gid;
  1902. X                break;
  1903. X            case 'G':
  1904. X                Gflg++;
  1905. X                if (get_groups (optarg))
  1906. X                    exit (1);
  1907. X
  1908. X                break;
  1909. X            case 'k':
  1910. X                if (! mflg)
  1911. X                    usage ();
  1912. X
  1913. X                strncpy (def_template, optarg, BUFSIZ);
  1914. X                kflg++;
  1915. X                break;
  1916. X            case 'm':
  1917. X                mflg++;
  1918. X                break;
  1919. X            case 'o':
  1920. X                if (! uflg)
  1921. X                    usage ();
  1922. X
  1923. X                oflg++;
  1924. X                break;
  1925. X            case 's':
  1926. X                if (! VALID (optarg)) {
  1927. X                    fprintf (stderr,
  1928. X                        "%s: invalid field `%s'\n",
  1929. X                        Prog, optarg);
  1930. X                    exit (3);
  1931. X                }
  1932. X                sflg++;
  1933. X                strncpy (user_shell, optarg, BUFSIZ);
  1934. X                break;
  1935. X            case 'u':
  1936. X                uflg++;
  1937. X                user_id = atoi (optarg);
  1938. X                break;
  1939. X            default:
  1940. X                usage ();
  1941. X        }
  1942. X        anyflag++;
  1943. X    }
  1944. X    if (! Dflg && optind == argc - 1)
  1945. X        strcpy (user_name, argv[argc - 1]);
  1946. X
  1947. X    if (! dflg)
  1948. X        sprintf (user_home, "%s/%s", def_home, user_name);
  1949. X
  1950. X    if (! gflg)
  1951. X        user_gid = def_group;
  1952. X
  1953. X    if (Dflg) {
  1954. X        if (optind != argc)
  1955. X            usage ();
  1956. X
  1957. X        if (uflg || oflg || Gflg || dflg ||
  1958. X                sflg || cflg || mflg || kflg)
  1959. X            usage ();
  1960. X    } else {
  1961. X        if (optind != argc - 1)
  1962. X            usage ();
  1963. X    }
  1964. X}
  1965. X
  1966. X/*
  1967. X * close_files - close all of the files that were opened
  1968. X *
  1969. X *    close_files() closes all of the files that were opened for this
  1970. X *    new user.  This causes any modified entries to be written out.
  1971. X */
  1972. X
  1973. Xclose_files ()
  1974. X{
  1975. X    if (! pw_close ()) {
  1976. X        fprintf (stderr, "%s: cannot rewrite password file\n", Prog);
  1977. X        exit (1);
  1978. X    }
  1979. X    if (! spw_close ()) {
  1980. X        fprintf (stderr, "%s: cannot rewrite shadow password file\n",    
  1981. X            Prog);
  1982. X        exit (1);
  1983. X    }
  1984. X    if (user_ngroups > 0) {
  1985. X        if (! gr_close ()) {
  1986. X            fprintf (stderr, "%s: cannot rewrite group file\n",
  1987. X                Prog);
  1988. X            exit (1);
  1989. X        }
  1990. X        (void) gr_unlock ();
  1991. X#ifdef    SHADOWGRP
  1992. X        if (! sgr_close ()) {
  1993. X            fprintf (stderr, "%s: cannot rewrite shadow group file\n",
  1994. X                Prog);
  1995. X            exit (1);
  1996. X        }
  1997. X        (void) sgr_unlock ();
  1998. X#endif
  1999. X    }
  2000. X    (void) spw_unlock ();
  2001. X    (void) pw_unlock ();
  2002. X}
  2003. X
  2004. X/*
  2005. X * open_files - lock and open the password files
  2006. X *
  2007. X *    open_files() opens the two password files.
  2008. X */
  2009. X
  2010. Xopen_files ()
  2011. X{
  2012. X    if (! pw_lock ()) {
  2013. X        fprintf (stderr, "%s: unable to lock password file\n", Prog);
  2014. X        exit (1);
  2015. X    }
  2016. X    if (! pw_open (O_RDWR)) {
  2017. X        fprintf (stderr, "%s: unable to open password file\n", Prog);
  2018. X        exit (1);
  2019. X    }
  2020. X    if (! spw_lock ()) {
  2021. X        fprintf (stderr, "%s: cannot lock shadow password file\n", Prog);
  2022. X        exit (1);
  2023. X    }
  2024. X    if (! spw_open (O_RDWR)) {
  2025. X        fprintf (stderr, "%s: cannot open shadow password file\n", Prog);
  2026. X        exit (1);
  2027. X    }
  2028. X}
  2029. X
  2030. X/*
  2031. X * usr_update - create the user entries
  2032. X *
  2033. X *    usr_update() creates the password file entries for this user
  2034. X *    and will update the group entries if required.
  2035. X */
  2036. X
  2037. Xusr_update ()
  2038. X{
  2039. X    struct    passwd    pwent;
  2040. X    struct    spwd    spent;
  2041. X
  2042. X    if (! oflg)
  2043. X        find_new_uid ();
  2044. X
  2045. X    new_pwent (&pwent);
  2046. X    if (! pw_update (&pwent)) {
  2047. X        fprintf (stderr, "%s: error adding new password entry\n", Prog);
  2048. X        exit (1);
  2049. X    }
  2050. X    new_spent (&spent);
  2051. X    if (! spw_update (&spent)) {
  2052. X        fprintf (stderr, "%s: error adding new shadow password entry\n",
  2053. X            Prog);
  2054. X        exit (1);
  2055. X    }
  2056. X#if defined(DBM) || defined(NDBM)
  2057. X    if (access ("/etc/passwd.pag", 0) == 0 && ! pw_dbm_update (&pwent)) {
  2058. X        fprintf (stderr, "%s: error updating password dbm entry\n",
  2059. X            Prog);
  2060. X        exit (1);
  2061. X    }
  2062. X    endpwent ();
  2063. X#endif
  2064. X#ifdef    NDBM
  2065. X    if (access ("/etc/shadow.pag", 0) == 0 && ! sp_dbm_update (&spent)) {
  2066. X        fprintf (stderr, "%s: error updating shadow passwd dbm entry\n",
  2067. X            Prog);
  2068. X        exit (1);
  2069. X    }
  2070. X    endspent ();
  2071. X#endif
  2072. X#ifdef    USE_SYSLOG
  2073. X    syslog (LOG_INFO,
  2074. X        "new user: name=%s, uid=%d, gid=%d, home=%s, shell=%s\n",
  2075. X        user_name, user_id, user_gid, user_home, user_shell);
  2076. X#endif
  2077. X    if (user_ngroups > 0)
  2078. X        grp_update ();
  2079. X}
  2080. X
  2081. X/*
  2082. X * create_home - create the user's home directory
  2083. X *
  2084. X *    create_home() creates the user's home directory if it does not
  2085. X *    already exist.  It will be created mode 755 owned by the user
  2086. X *    with the user's default group.
  2087. X */
  2088. X
  2089. Xcreate_home ()
  2090. X{
  2091. X    if (access (user_home, 0)) {
  2092. X        if (mkdir (user_home, 0755)) {
  2093. X            fprintf (stderr, "%s: cannot create directory %s\n",
  2094. X                Prog, user_home);
  2095. X            exit (1);
  2096. X        }
  2097. X        chown (user_home, user_id, user_gid);
  2098. X        chmod (user_home, 0755);
  2099. X    }
  2100. X}
  2101. X
  2102. X/*
  2103. X * main - useradd command
  2104. X */
  2105. X
  2106. Xmain (argc, argv)
  2107. Xint    argc;
  2108. Xchar    **argv;
  2109. X{
  2110. X    /*
  2111. X     * Get my name so that I can use it to report errors.
  2112. X     */
  2113. X
  2114. X    if (Prog = strrchr (argv[0], '/'))
  2115. X        Prog++;
  2116. X    else
  2117. X        Prog = argv[0];
  2118. X
  2119. X#ifdef    USE_SYSLOG
  2120. X    openlog (Prog, LOG_PID|LOG_CONS|LOG_NOWAIT, LOG_AUTH);
  2121. X#endif
  2122. X
  2123. X    /*
  2124. X     * The open routines for the DBM files don't use read-write
  2125. X     * as the mode, so we have to clue them in.
  2126. X     */
  2127. X
  2128. X#if defined(DBM) || defined(NDBM)
  2129. X    pw_dbm_mode = O_RDWR;
  2130. X#endif
  2131. X#ifdef    NDBM
  2132. X    sp_dbm_mode = O_RDWR;
  2133. X    gr_dbm_mode = O_RDWR;
  2134. X#ifdef    SHADOWGRP
  2135. X    sg_dbm_mode = O_RDWR;
  2136. X#endif
  2137. X#endif
  2138. X    get_defaults ();
  2139. X
  2140. X    process_flags (argc, argv);
  2141. X
  2142. X    /*
  2143. X     * See if we are messing with the defaults file, or creating
  2144. X     * a new user.
  2145. X     */
  2146. X
  2147. X    if (Dflg) {
  2148. X        if (gflg || bflg || fflg || eflg)
  2149. X            exit (set_defaults () ? 1:0);
  2150. X
  2151. X        show_defaults ();
  2152. X        exit (0);
  2153. X    }
  2154. X
  2155. X    /*
  2156. X     * Start with a quick check to see if the user exists.
  2157. X     */
  2158. X
  2159. X    if (getpwnam (user_name)) {
  2160. X        fprintf (stderr, "%s: user %s exists\n", Prog, user_name);
  2161. X        exit (1);
  2162. X    }
  2163. X
  2164. X    /*
  2165. X     * Do the hard stuff - open the files, create the user entries,
  2166. X     * create the home directory, then close and update the files.
  2167. X     */
  2168. X
  2169. X    open_files ();
  2170. X
  2171. X    usr_update ();
  2172. X
  2173. X    if (mflg) {
  2174. X        create_home ();
  2175. X        copy_tree (def_template, user_home, user_id, user_gid);
  2176. X    }
  2177. X    close_files ();
  2178. X    exit (0);
  2179. X    /*NOTREACHED*/
  2180. X}
  2181. END_OF_FILE
  2182.   if test 25685 -ne `wc -c <'useradd.c'`; then
  2183.     echo shar: \"'useradd.c'\" unpacked with wrong size!
  2184.   fi
  2185.   # end of 'useradd.c'
  2186. fi
  2187. echo shar: End of archive 1 \(of 11\).
  2188. cp /dev/null ark1isdone
  2189. MISSING=""
  2190. for I in 1 2 3 4 5 6 7 8 9 10 11 ; do
  2191.     if test ! -f ark${I}isdone ; then
  2192.     MISSING="${MISSING} ${I}"
  2193.     fi
  2194. done
  2195. if test "${MISSING}" = "" ; then
  2196.     echo You have unpacked all 11 archives.
  2197.     rm -f ark[1-9]isdone ark[1-9][0-9]isdone
  2198. else
  2199.     echo You still must unpack the following archives:
  2200.     echo "        " ${MISSING}
  2201. fi
  2202. exit 0
  2203. exit 0 # Just in case...
  2204.