summaryrefslogtreecommitdiffstats
path: root/contrib
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-06 02:22:06 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-06 02:22:06 +0000
commit741c1ef7a4f2ac316ad6e557ddbe03023413478d (patch)
tree38890f681daa26c57e865b4feca10d0ca53e1046 /contrib
parentInitial commit. (diff)
downloadshadow-741c1ef7a4f2ac316ad6e557ddbe03023413478d.tar.xz
shadow-741c1ef7a4f2ac316ad6e557ddbe03023413478d.zip
Adding upstream version 1:4.5.upstream/1%4.5upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'contrib')
-rw-r--r--contrib/Makefile.am6
-rw-r--r--contrib/README10
-rw-r--r--contrib/adduser-old.c300
-rw-r--r--contrib/adduser.c502
-rwxr-xr-xcontrib/adduser.sh90
-rwxr-xr-xcontrib/adduser2.sh743
-rwxr-xr-xcontrib/atudel85
-rw-r--r--contrib/groupmems.shar546
-rw-r--r--contrib/pwdauth.c308
-rw-r--r--contrib/shadow-anonftp.patch147
-rw-r--r--contrib/udbachk.tgzbin0 -> 17571 bytes
11 files changed, 2737 insertions, 0 deletions
diff --git a/contrib/Makefile.am b/contrib/Makefile.am
new file mode 100644
index 0000000..2c3160e
--- /dev/null
+++ b/contrib/Makefile.am
@@ -0,0 +1,6 @@
+# This is a dummy Makefile.am to get automake work flawlessly,
+# and also cooperate to make a distribution for `make dist'
+
+EXTRA_DIST = README adduser.c adduser-old.c adduser.sh adduser2.sh \
+ atudel groupmems.shar pwdauth.c shadow-anonftp.patch \
+ udbachk.tgz
diff --git a/contrib/README b/contrib/README
new file mode 100644
index 0000000..c4d1bc0
--- /dev/null
+++ b/contrib/README
@@ -0,0 +1,10 @@
+People keep sending various adduser programs and scripts... They are
+all in this directory. I haven't tested them, use at your own risk.
+Anyway, the best one I've seen so far is adduser-3.x from Debian.
+
+atudel is a perl script to remove at jobs owned by the specified user
+(atrm in at-2.9 for Linux can't do that).
+
+udbachk.tgz is a passwd/group/shadow file integrity checker.
+
+--marekm
diff --git a/contrib/adduser-old.c b/contrib/adduser-old.c
new file mode 100644
index 0000000..0b2080e
--- /dev/null
+++ b/contrib/adduser-old.c
@@ -0,0 +1,300 @@
+/****
+** 03/17/96
+** hacked a bit more, removed unused code, cleaned up for gcc -Wall.
+** --marekm
+**
+** 02/26/96
+** modified to call shadow utils (useradd,chage,passwd) on shadowed
+** systems - Cristian Gafton, gafton@sorosis.ro
+**
+** 6/27/95
+** shadow-adduser 1.4:
+**
+** now it copies the /etc/skel dir into the person's dir,
+** makes the mail folders, changed some defaults and made a 'make
+** install' just for the hell of it.
+**
+** Greg Gallagher
+** CIN.Net
+**
+** 1/28/95
+** shadow-adduser 1.3:
+**
+** Basically a bug-fix on my additions in 1.2. Thanx to Terry Stewart
+** (stew@texas.net) for pointing out one of the many idiotic bugs I introduced.
+** It was such a stupid bug that I would have never seen it myself.
+**
+** Brandon
+*****
+** 01/27/95
+**
+** shadow-adduser 1.2:
+** I took the C source from adduser-shadow (credits are below) and made
+** it a little more worthwhile. Many small changes... Here's
+** the ones I can remember:
+**
+** Removed support for non-shadowed systems (if you don't have shadow,
+** use the original adduser, don't get this shadow version!)
+** Added support for the correct /etc/shadow fields (Min days before
+** password change, max days before password change, Warning days,
+** and how many days from expiry date does the account go invalid)
+** The previous version just left all of those fields blank.
+** There is still one field left (expiry date for the account, period)
+** which I have left blank because I do not use it and didn't want to
+** spend any more time on this. I'm sure someone will put it in and
+** tack another plethora of credits on here. :)
+** Added in the password date field, which should always reflect the last
+** date the password was changed, for expiry purposes. "passwd" always
+** updates this field, so the adduser program should set it up right
+** initially (or a user could keep thier initial password forever ;)
+** The number is in days since Jan 1st, 1970.
+**
+** Have fun with it, and someone please make
+** a real version(this is still just a hack)
+** for us all to use (and Email it to me???)
+**
+** Brandon
+** photon@usis.com
+**
+*****
+** adduser 1.0: add a new user account (For systems not using shadow)
+** With a nice little interface and a will to do all the work for you.
+**
+** Craig Hagan
+** hagan@opine.cs.umass.edu
+**
+** Modified to really work, look clean, and find unused uid by Chris Cappuccio
+** chris@slinky.cs.umass.edu
+**
+*****
+**
+** 01/19/95
+**
+** FURTHER modifications to enable shadow passwd support (kludged, but
+** no more so than the original) by Dan Crowson - dcrowson@mo.net
+**
+** Search on DAN for all changes...
+**
+*****
+**
+** cc -O -o adduser adduser.c
+** Use gcc if you have it... (political reasons beyond my control) (chris)
+**
+** I've gotten this program to work with success under Linux (without
+** shadow) and SunOS 4.1.3. I would assume it should work pretty well
+** on any system that uses no shadow. (chris)
+**
+** If you have no crypt() then try
+** cc -DNO_CRYPT -O -o adduser adduser.c xfdes.c
+** I'm not sure how login operates with no crypt()... I guess
+** the same way we're doing it here.
+*/
+
+#include <pwd.h>
+#include <grp.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <time.h>
+#include <sys/types.h>
+#include <sys/timeb.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+
+#define DEFAULT_SHELL "/bin/bash" /* because BASH is your friend */
+#define DEFAULT_HOME "/home"
+#define USERADD_PATH "/usr/sbin/useradd"
+#define CHAGE_PATH "/usr/sbin/chage"
+#define PASSWD_PATH "/usr/bin/passwd"
+#define DEFAULT_GROUP 100
+
+#define DEFAULT_MAX_PASS 60
+#define DEFAULT_WARN_PASS 10
+/* if you use this feature, you will get a lot of complaints from users
+ who rarely use their accounts :) (something like 3 months would be
+ more reasonable) --marekm */
+#define DEFAULT_USER_DIE /* 10 */ 0
+
+void main()
+{
+ char foo[32];
+ char uname[9],person[32],dir[32],shell[32];
+ unsigned int group,min_pass,max_pass,warn_pass,user_die;
+ /* the group and uid of the new user */
+ int bad=0,done=0,correct=0,gets_warning=0;
+ char cmd[255];
+ struct group *grp;
+
+ /* flags, in order:
+ * bad to see if the username is in /etc/passwd, or if strange stuff has
+ * been typed if the user might be put in group 0
+ * done allows the program to exit when a user has been added
+ * correct loops until a password is found that isn't in /etc/passwd
+ * gets_warning allows the fflush to be skipped for the first gets
+ * so that output is still legible
+ */
+
+ /* The real program starts HERE! */
+
+ if(geteuid()!=0)
+ {
+ printf("It seems you don't have access to add a new user. Try\n");
+ printf("logging in as root or su root to gain super-user access.\n");
+ exit(1);
+ }
+
+ /* Sanity checks
+ */
+
+ if (!(grp=getgrgid(DEFAULT_GROUP))){
+ printf("Error: the default group %d does not exist on this system!\n",
+ DEFAULT_GROUP);
+ printf("adduser must be recompiled.\n");
+ exit(1);
+ };
+
+ while(!correct) { /* loop until a "good" uname is chosen */
+ while(!done) {
+ printf("\nLogin to add (^C to quit): ");
+ if(gets_warning) /* if the warning was already shown */
+ fflush(stdout); /* fflush stdout, otherwise set the flag */
+ else
+ gets_warning=1;
+
+ gets(uname);
+ if(!strlen(uname)) {
+ printf("Empty input.\n");
+ done=0;
+ continue;
+ };
+
+ /* what I saw here before made me think maybe I was running DOS */
+ /* might this be a solution? (chris) */
+ if (getpwnam(uname) != NULL) {
+ printf("That name is in use, choose another.\n");
+ done=0;
+ } else
+ done=1;
+ }; /* done, we have a valid new user name */
+
+ /* all set, get the rest of the stuff */
+ printf("\nEditing information for new user [%s]\n",uname);
+
+ printf("\nFull Name [%s]: ",uname);
+ gets(person);
+ if (!strlen(person)) {
+ bzero(person,sizeof(person));
+ strcpy(person,uname);
+ };
+
+ do {
+ bad=0;
+ printf("GID [%d]: ",DEFAULT_GROUP);
+ gets(foo);
+ if (!strlen(foo))
+ group=DEFAULT_GROUP;
+ else
+ if (isdigit (*foo)) {
+ group = atoi(foo);
+ if (! (grp = getgrgid (group))) {
+ printf("unknown gid %s\n",foo);
+ group=DEFAULT_GROUP;
+ bad=1;
+ };
+ } else
+ if ((grp = getgrnam (foo)))
+ group = grp->gr_gid;
+ else {
+ printf("unknown group %s\n",foo);
+ group=DEFAULT_GROUP;
+ bad=1;
+ }
+ if (group==0){ /* You're not allowed to make root group users! */
+ printf("Creation of root group users not allowed (must be done by hand)\n");
+ group=DEFAULT_GROUP;
+ bad=1;
+ };
+ } while(bad);
+
+
+ fflush(stdin);
+
+ printf("\nIf home dir ends with a / then [%s] will be appended to it\n",uname);
+ printf("Home Directory [%s/%s]: ",DEFAULT_HOME,uname);
+ fflush(stdout);
+ gets(dir);
+ if (!strlen(dir)) { /* hit return */
+ sprintf(dir,"%s/%s",DEFAULT_HOME,uname);
+ fflush(stdin);
+ } else
+ if (dir[strlen(dir)-1]=='/')
+ sprintf(dir+strlen(dir),"%s",uname);
+
+ printf("\nShell [%s]: ",DEFAULT_SHELL);
+ fflush(stdout);
+ gets(shell);
+ if (!strlen(shell))
+ sprintf(shell,"%s",DEFAULT_SHELL);
+
+ printf("\nMin. Password Change Days [0]: ");
+ gets(foo);
+ min_pass=atoi(foo);
+
+ printf("Max. Password Change Days [%d]: ",DEFAULT_MAX_PASS);
+ gets(foo);
+ if (strlen(foo) > 1)
+ max_pass = atoi(foo);
+ else
+ max_pass = DEFAULT_MAX_PASS;
+
+ printf("Password Warning Days [%d]: ",DEFAULT_WARN_PASS);
+ gets(foo);
+ warn_pass = atoi(foo);
+ if (warn_pass==0)
+ warn_pass = DEFAULT_WARN_PASS;
+
+ printf("Days after Password Expiry for Account Locking [%d]: ",DEFAULT_USER_DIE);
+ gets(foo);
+ user_die = atoi(foo);
+ if (user_die == 0)
+ user_die = DEFAULT_USER_DIE;
+
+ printf("\nInformation for new user [%s] [%s]:\n",uname,person);
+ printf("Home directory: [%s] Shell: [%s]\n",dir,shell);
+ printf("GID: [%d]\n",group);
+ printf("MinPass: [%d] MaxPass: [%d] WarnPass: [%d] UserExpire: [%d]\n",
+ min_pass,max_pass,warn_pass,user_die);
+ printf("\nIs this correct? [y/N]: ");
+ fflush(stdout);
+ gets(foo);
+
+ done=bad=correct=(foo[0]=='y'||foo[0]=='Y');
+
+ if(bad!=1)
+ printf("\nUser [%s] not added\n",uname);
+ }
+
+ bzero(cmd,sizeof(cmd));
+ sprintf(cmd,"%s -g %d -d %s -s %s -c \"%s\" -m -k /etc/skel %s",
+ USERADD_PATH,group,dir,shell,person,uname);
+ printf("Calling useradd to add new user:\n%s\n",cmd);
+ if(system(cmd)){
+ printf("User add failed!\n");
+ exit(errno);
+ };
+ bzero(cmd,sizeof(cmd));
+ sprintf(cmd,"%s -m %d -M %d -W %d -I %d %s", CHAGE_PATH,
+ min_pass,max_pass,warn_pass,user_die,uname);
+ printf("%s\n",cmd);
+ if(system(cmd)){
+ printf("There was an error setting password expire values\n");
+ exit(errno);
+ };
+ bzero(cmd,sizeof(cmd));
+ sprintf(cmd,"%s %s",PASSWD_PATH,uname);
+ system(cmd);
+ printf("\nDone.\n");
+}
+
diff --git a/contrib/adduser.c b/contrib/adduser.c
new file mode 100644
index 0000000..88596b0
--- /dev/null
+++ b/contrib/adduser.c
@@ -0,0 +1,502 @@
+/****
+** 04/21/96
+** hacked even more, replaced gets() with something slightly harder to buffer
+** overflow. Added support for setting a default quota on new account, with
+** edquota -p. Other cleanups for security, I let some users run adduser suid
+** root to add new accounts. (overflow checks, clobber environment, valid
+** shell checks, restrictions on gid + home dir settings).
+
+** Added max. username length. Used syslog() a bit for important events.
+** Support to immediately expire account with passwd -e.
+
+** Called it version 2.0! Because I felt like it!
+
+** -- Chris, chris@ferret.lmh.ox.ac.uk
+
+** 03/17/96
+** hacked a bit more, removed unused code, cleaned up for gcc -Wall.
+** --marekm
+**
+** 02/26/96
+** modified to call shadow utils (useradd,chage,passwd) on shadowed
+** systems - Cristian Gafton, gafton@sorosis.ro
+**
+** 6/27/95
+** shadow-adduser 1.4:
+**
+** now it copies the /etc/skel dir into the person's dir,
+** makes the mail folders, changed some defaults and made a 'make
+** install' just for the hell of it.
+**
+** Greg Gallagher
+** CIN.Net
+**
+** 1/28/95
+** shadow-adduser 1.3:
+**
+** Basically a bug-fix on my additions in 1.2. Thanx to Terry Stewart
+** (stew@texas.net) for pointing out one of the many idiotic bugs I introduced.
+** It was such a stupid bug that I would have never seen it myself.
+**
+** Brandon
+*****
+** 01/27/95
+**
+** shadow-adduser 1.2:
+** I took the C source from adduser-shadow (credits are below) and made
+** it a little more worthwhile. Many small changes... Here's
+** the ones I can remember:
+**
+** Removed support for non-shadowed systems (if you don't have shadow,
+** use the original adduser, don't get this shadow version!)
+** Added support for the correct /etc/shadow fields (Min days before
+** password change, max days before password change, Warning days,
+** and how many days from expiry date does the account go invalid)
+** The previous version just left all of those fields blank.
+** There is still one field left (expiry date for the account, period)
+** which I have left blank because I do not use it and didn't want to
+** spend any more time on this. I'm sure someone will put it in and
+** tack another plethora of credits on here. :)
+** Added in the password date field, which should always reflect the last
+** date the password was changed, for expiry purposes. "passwd" always
+** updates this field, so the adduser program should set it up right
+** initially (or a user could keep thier initial password forever ;)
+** The number is in days since Jan 1st, 1970.
+**
+** Have fun with it, and someone please make
+** a real version(this is still just a hack)
+** for us all to use (and Email it to me???)
+**
+** Brandon
+** photon@usis.com
+**
+*****
+** adduser 1.0: add a new user account (For systems not using shadow)
+** With a nice little interface and a will to do all the work for you.
+**
+** Craig Hagan
+** hagan@opine.cs.umass.edu
+**
+** Modified to really work, look clean, and find unused uid by Chris Cappuccio
+** chris@slinky.cs.umass.edu
+**
+*****
+**
+** 01/19/95
+**
+** FURTHER modifications to enable shadow passwd support (kludged, but
+** no more so than the original) by Dan Crowson - dcrowson@mo.net
+**
+** Search on DAN for all changes...
+**
+*****
+**
+** cc -O -o adduser adduser.c
+** Use gcc if you have it... (political reasons beyond my control) (chris)
+**
+** I've gotten this program to work with success under Linux (without
+** shadow) and SunOS 4.1.3. I would assume it should work pretty well
+** on any system that uses no shadow. (chris)
+**
+** If you have no crypt() then try
+** cc -DNO_CRYPT -O -o adduser adduser.c xfdes.c
+** I'm not sure how login operates with no crypt()... I guess
+** the same way we're doing it here.
+*/
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <pwd.h>
+#include <grp.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <string.h>
+#include <time.h>
+#include <sys/types.h>
+#include <sys/timeb.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+#include <syslog.h>
+
+#define IMMEDIATE_CHANGE /* Expire newly created password, must be changed
+ * immediately upon next login */
+#define HAVE_QUOTAS /* Obvious */
+#define EXPIRE_VALS_SET /* If defined, 'normal' users can't change
+ * password expiry values (if running suid root) */
+
+#define HAVE_GETUSERSHELL /* FIXME: Isn't this defined in config.h too? */
+#define LOGGING /* If we want to log various things to syslog */
+#define MAX_USRNAME 8 /* Longer usernames seem to work on my system....
+ * But they're probably a poor idea */
+
+
+#define DEFAULT_SHELL "/bin/bash" /* because BASH is your friend */
+#define DEFAULT_HOME "/home"
+#define USERADD_PATH "/usr/sbin/useradd"
+#define CHAGE_PATH "/usr/bin/chage"
+#define PASSWD_PATH "/usr/bin/passwd"
+#define EDQUOTA_PATH "/usr/sbin/edquota"
+#define QUOTA_DEFAULT "defuser"
+#define DEFAULT_GROUP 100
+
+#define DEFAULT_MIN_PASS 0
+#define DEFAULT_MAX_PASS 100
+#define DEFAULT_WARN_PASS 14
+#define DEFAULT_USER_DIE 366
+
+void safeget (char *, int);
+
+void
+main (void)
+{
+ char foo[32];
+ char usrname[32], person[32], dir[32], shell[32];
+ unsigned int group, min_pass, max_pass, warn_pass, user_die;
+ /* the group and uid of the new user */
+ int bad = 0, done = 0, correct = 0, olduid;
+ char cmd[255];
+ struct group *grp;
+
+ /* flags, in order:
+ * bad to see if the username is in /etc/passwd, or if strange stuff has
+ * been typed if the user might be put in group 0
+ * done allows the program to exit when a user has been added
+ * correct loops until a username is found that isn't in /etc/passwd
+ */
+
+ /* The real program starts HERE! */
+
+ if (geteuid () != 0)
+ {
+ printf ("It seems you don't have access to add a new user. Try\n");
+ printf ("logging in as root or su root to gain superuser access.\n");
+ exit (1);
+ }
+
+ /* Sanity checks
+ */
+
+#ifdef LOGGING
+ openlog ("adduser", LOG_PID | LOG_CONS | LOG_NOWAIT, LOG_AUTH);
+ syslog (LOG_INFO, "invoked by user %s\n", getpwuid (getuid ())->pw_name);
+#endif
+
+ if (!(grp = getgrgid (DEFAULT_GROUP)))
+ {
+ printf ("Error: the default group %d does not exist on this system!\n",
+ DEFAULT_GROUP);
+ printf ("adduser must be recompiled.\n");
+#ifdef LOGGING
+ syslog (LOG_ERR, "warning: failed. no such default group\n");
+ closelog ();
+#endif
+ exit (1);
+ };
+
+ while (!correct)
+ { /* loop until a "good" usrname is chosen */
+ while (!done)
+ {
+ printf ("\nLogin to add (^C to quit): ");
+ fflush (stdout);
+
+ safeget (usrname, sizeof (usrname));
+
+ if (!strlen (usrname))
+ {
+ printf ("Empty input.\n");
+ done = 0;
+ continue;
+ };
+
+ /* what I saw here before made me think maybe I was running DOS */
+ /* might this be a solution? (chris) */
+ if (strlen (usrname) > MAX_USRNAME)
+ {
+ printf ("That name is longer than the maximum of %d characters. Choose another.\n", MAX_USRNAME);
+ done = 0;
+ }
+ else if (getpwnam (usrname) != NULL)
+ {
+ printf ("That name is in use, choose another.\n");
+ done = 0;
+ }
+ else if (strchr (usrname, ' ') != NULL)
+ {
+ printf ("No spaces in username!!\n");
+ done = 0;
+ }
+ else
+ done = 1;
+ }; /* done, we have a valid new user name */
+
+ /* all set, get the rest of the stuff */
+ printf ("\nEditing information for new user [%s]\n", usrname);
+
+ printf ("\nFull Name [%s]: ", usrname);
+ fflush (stdout);
+ safeget (person, sizeof (person));
+ if (!strlen (person))
+ {
+ bzero (person, sizeof (person));
+ strcpy (person, usrname);
+ };
+
+ if (getuid () == 0)
+ {
+ do
+ {
+ bad = 0;
+ printf ("GID [%d]: ", DEFAULT_GROUP);
+ fflush (stdout);
+ safeget (foo, sizeof (foo));
+ if (!strlen (foo))
+ group = DEFAULT_GROUP;
+ else if (isdigit (*foo))
+ {
+ group = atoi (foo);
+ if (!(grp = getgrgid (group)))
+ {
+ printf ("unknown gid %s\n", foo);
+ group = DEFAULT_GROUP;
+ bad = 1;
+ };
+ }
+ else if ((grp = getgrnam (foo)))
+ group = grp->gr_gid;
+ else
+ {
+ printf ("unknown group %s\n", foo);
+ group = DEFAULT_GROUP;
+ bad = 1;
+ }
+ if (group == 0)
+ { /* You're not allowed to make root group users! */
+ printf ("Creation of root group users not allowed (must be done by hand)\n");
+ group = DEFAULT_GROUP;
+ bad = 1;
+ };
+ }
+ while (bad);
+ }
+ else
+ {
+ printf ("Group will be default of: %d\n", DEFAULT_GROUP);
+ group = DEFAULT_GROUP;
+ }
+
+ if (getuid () == 0)
+ {
+ printf ("\nIf home dir ends with a / then '%s' will be appended to it\n", usrname);
+ printf ("Home Directory [%s/%s]: ", DEFAULT_HOME, usrname);
+ fflush (stdout);
+ safeget (dir, sizeof (dir));
+ if (!strlen (dir))
+ { /* hit return */
+ sprintf (dir, "%s/%s", DEFAULT_HOME, usrname);
+ }
+ else if (dir[strlen (dir) - 1] == '/')
+ sprintf (dir+strlen(dir), "%s", usrname);
+ }
+ else
+ {
+ printf ("\nHome directory will be %s/%s\n", DEFAULT_HOME, usrname);
+ sprintf (dir, "%s/%s", DEFAULT_HOME, usrname);
+ }
+
+ printf ("\nShell [%s]: ", DEFAULT_SHELL);
+ fflush (stdout);
+ safeget (shell, sizeof (shell));
+ if (!strlen (shell))
+ sprintf (shell, "%s", DEFAULT_SHELL);
+ else
+ {
+ char *sh;
+ int ok = 0;
+#ifdef HAVE_GETUSERSHELL
+ setusershell ();
+ while ((sh = getusershell ()) != NULL)
+ if (!strcmp (shell, sh))
+ ok = 1;
+ endusershell ();
+#endif
+ if (!ok)
+ {
+ if (getuid () == 0)
+ printf ("Warning: root allowed non standard shell\n");
+ else
+ {
+ printf ("Shell NOT in /etc/shells, DEFAULT used\n");
+ sprintf (shell, "%s", DEFAULT_SHELL);
+ }
+ }
+ }
+
+#ifdef EXPIRE_VALS_SET
+ if (getuid () == 0)
+ {
+#endif
+ printf ("\nMin. Password Change Days [%d]: ", DEFAULT_MIN_PASS);
+ fflush (stdout);
+ safeget (foo, sizeof (foo));
+ if (strlen (foo) > 1)
+ min_pass = DEFAULT_MIN_PASS;
+ else
+ min_pass = atoi (foo);
+
+ printf ("Max. Password Change Days [%d]: ", DEFAULT_MAX_PASS);
+ fflush (stdout);
+ safeget (foo, sizeof (foo));
+ if (strlen (foo) > 1)
+ max_pass = atoi (foo);
+ else
+ max_pass = DEFAULT_MAX_PASS;
+
+ printf ("Password Warning Days [%d]: ", DEFAULT_WARN_PASS);
+ fflush (stdout);
+ safeget (foo, sizeof (foo));
+ warn_pass = atoi (foo);
+ if (warn_pass == 0)
+
+ warn_pass = DEFAULT_WARN_PASS;
+
+ printf ("Days after Password Expiry for Account Locking [%d]: ", DEFAULT_USER_DIE);
+ fflush (stdout);
+ safeget (foo, sizeof (foo));
+ user_die = atoi (foo);
+ if (user_die == 0)
+ user_die = DEFAULT_USER_DIE;
+
+#ifdef EXPIRE_VALS_SET
+ }
+ else
+ {
+ printf ("\nSorry, account expiry values are set.\n");
+ user_die = DEFAULT_USER_DIE;
+ warn_pass = DEFAULT_WARN_PASS;
+ max_pass = DEFAULT_MAX_PASS;
+ min_pass = DEFAULT_MIN_PASS;
+ }
+#endif
+
+ printf ("\nInformation for new user [%s] [%s]:\n", usrname, person);
+ printf ("Home directory: [%s] Shell: [%s]\n", dir, shell);
+ printf ("GID: [%d]\n", group);
+ printf ("MinPass: [%d] MaxPass: [%d] WarnPass: [%d] UserExpire: [%d]\n",
+ min_pass, max_pass, warn_pass, user_die);
+ printf ("\nIs this correct? [y/N]: ");
+ fflush (stdout);
+ safeget (foo, sizeof (foo));
+
+ done = bad = correct = (foo[0] == 'y' || foo[0] == 'Y');
+
+ if (bad != 1)
+ printf ("\nUser [%s] not added\n", usrname);
+ }
+
+ /* Clobber the environment, I run this suid root sometimes to let
+ * non root privileged accounts add users --chris */
+
+ *environ = NULL;
+
+ bzero (cmd, sizeof (cmd));
+ sprintf (cmd, "%s -g %d -d %s -s %s -c \"%s\" -m -k /etc/skel %s",
+ USERADD_PATH, group, dir, shell, person, usrname);
+ printf ("Calling useradd to add new user:\n%s\n", cmd);
+ if (system (cmd))
+ {
+ printf ("User add failed!\n");
+#ifdef LOGGING
+ syslog (LOG_ERR, "could not add new user\n");
+ closelog ();
+#endif
+ exit (errno);
+ };
+
+ olduid = getuid (); /* chage, passwd, edquota etc. require ruid = root
+ */
+ setuid (0);
+
+ bzero (cmd, sizeof (cmd));
+
+ /* Chage runs suid root. => we need ruid root to run it with
+ * anything other than chage -l
+ */
+
+ sprintf (cmd, "%s -m %d -M %d -W %d -I %d %s", CHAGE_PATH,
+ min_pass, max_pass, warn_pass, user_die, usrname);
+ printf ("%s\n", cmd);
+ if (system (cmd))
+ {
+ printf ("There was an error setting password expire values\n");
+#ifdef LOGGING
+ syslog (LOG_ERR, "password expire values could not be set\n");
+#endif
+ };
+
+ /* I want to add a user completely with one easy command --chris */
+
+#ifdef HAVE_QUOTAS
+ bzero (cmd, sizeof (cmd));
+ sprintf (cmd, "%s -p %s -u %s", EDQUOTA_PATH, QUOTA_DEFAULT, usrname);
+ printf ("%s\n", cmd);
+ if (system (cmd))
+ {
+ printf ("\nWarning: error setting quota\n");
+#ifdef LOGGING
+ syslog (LOG_ERR, "warning: account created but NO quotas set!\n");
+#endif /* LOGGING */
+ }
+ else
+ printf ("\nDefault quota set.\n");
+#endif /* HAVE_QUOTAS */
+
+ bzero (cmd, sizeof (cmd));
+ sprintf (cmd, "%s %s", PASSWD_PATH, usrname);
+ if (system (cmd))
+ {
+ printf ("\nWarning: error setting password\n");
+#ifdef LOGGING
+ syslog (LOG_ERR, "warning: password set failed!\n");
+#endif
+ }
+#ifdef IMMEDIATE_CHANGE
+ bzero (cmd, sizeof (cmd));
+ sprintf (cmd, "%s -e %s", PASSWD_PATH, usrname);
+ if (system (cmd))
+ {
+ printf ("\nWarning: error expiring password\n");
+#ifdef LOGGING
+ syslog (LOG_ERR, "warning: password expire failed!\n");
+#endif /* LOGGING */
+ }
+#endif /* IMMEDIATE_CHANGE */
+
+ setuid (olduid);
+
+#ifdef LOGGING
+ closelog ();
+#endif
+
+ printf ("\nDone.\n");
+}
+
+void
+safeget (char *buf, int maxlen)
+{
+ int c, i = 0, bad = 0;
+ char *bstart = buf;
+ while ((c = getc (stdin)) != EOF && (c != '\n') && (++i < maxlen))
+ {
+ bad = (!isalnum (c) && (c != '_') && (c != ' '));
+ *(buf++) = (char) c;
+ }
+ *buf = '\0';
+
+ if (bad)
+ {
+ printf ("\nString contained banned character. Please stick to alphanumerics.\n");
+ *bstart = '\0';
+ }
+}
+
diff --git a/contrib/adduser.sh b/contrib/adduser.sh
new file mode 100755
index 0000000..0efb27a
--- /dev/null
+++ b/contrib/adduser.sh
@@ -0,0 +1,90 @@
+#!/bin/sh
+# adduser script for use with shadow passwords and useradd command.
+# by Hrvoje Dogan <hdogan@student.math.hr>, Dec 1995.
+
+echo -n "Login name for new user []:"
+read LOGIN
+if [ -z $LOGIN ]
+then echo "Come on, man, you can't leave the login field empty...";exit
+fi
+echo
+echo -n "User id for $LOGIN [ defaults to next available]:"
+read ID
+GUID="-u $ID"
+if [ -z $ID ]
+then GUID=""
+fi
+
+echo
+echo -n "Initial group for $LOGIN [users]:"
+read GID
+GGID="-g $GID"
+if [ -z $GID ]
+then GGID=""
+fi
+
+echo
+echo -n "Additional groups for $LOGIN []:"
+read AGID
+GAGID="-G $AGID"
+if [ -z $AGID ]
+then GAGID=""
+fi
+
+echo
+echo -n "$LOGIN's home directory [/home/$LOGIN]:"
+read HME
+GHME="-d $HME"
+if [ -z $HME ]
+then GHME=""
+fi
+
+echo
+echo -n "$LOGIN's shell [/bin/bash]:"
+read SHL
+GSHL="-s $SHL"
+if [ -z $SHL ]
+then GSHL=""
+fi
+
+echo
+echo -n "$LOGIN's account expiry date (MM/DD/YY) []:"
+read EXP
+GEXP="-e $EXP"
+if [ -z $EXP ]
+then GEXP=""
+fi
+echo
+echo OK, I'm about to make a new account. Here's what you entered so far:
+echo New login name: $LOGIN
+if [ -z $GUID ]
+then echo New UID: [Next available]
+else echo New UID: $UID
+fi
+if [ -z $GGID ]
+then echo Initial group: users
+else echo Initial group: $GID
+fi
+if [ -z $GAGID ]
+then echo Additional groups: [none]
+else echo Additional groups: $AGID
+fi
+if [ -z $GHME ]
+then echo Home directory: /home/$LOGIN
+else echo Home directory: $HME
+fi
+if [ -z $GSHL ]
+then echo Shell: /bin/bash
+else echo Shell: $SHL
+fi
+if [ -z $GEXP ]
+then echo Expiry date: [no expiration]
+else echo Expiry date: $EXP
+fi
+echo "This is it... if you want to bail out, you'd better do it now."
+read FOO
+echo Making new account...
+/usr/sbin/useradd $GHME -m $GEXP $GGID $GAGID $GSHL $GUID $LOGIN
+/usr/bin/chfn $LOGIN
+/usr/bin/passwd $LOGIN
+echo "Done..."
diff --git a/contrib/adduser2.sh b/contrib/adduser2.sh
new file mode 100755
index 0000000..a2b36b2
--- /dev/null
+++ b/contrib/adduser2.sh
@@ -0,0 +1,743 @@
+#!/bin/bash
+#
+# adduser Interactive user adding program.
+#
+# Copyright (C) 1996 Petri Mattila, Prihateam Networks
+# petri@prihateam.fi
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2, or (at your option)
+# any later version.
+#
+# Changes:
+# 220496 v0.01 Initial version
+# 230496 v0.02 More checks, embolden summary
+# 240496 Even more checks
+# 250496 Help with ?
+# 040596 v0.03 Cleanups
+# 050596 v0.04 Bug fixes, expire date checks
+# 070596 v0.05 Iso-latin-1 names
+#
+
+## Defaults
+
+# default groups
+def_group="users"
+def_other_groups=""
+
+# default home directory
+def_home_dir=/home/users
+
+# default shell
+def_shell=/bin/tcsh
+
+# Defaul expiration date (mm/dd/yy)
+def_expire=""
+
+# default dates
+def_pwd_min=0
+def_pwd_max=90
+def_pwd_warn=14
+def_pwd_iact=14
+
+
+# possible UIDs
+uid_low=1000
+uid_high=64000
+
+# skel directory
+skel=/etc/skel
+
+# default mode for home directory
+def_mode=711
+
+# Regex, that the login name must meet, only ANSI characters
+login_regex='^[0-9a-zA-Z_-]*$'
+
+# Regex, that the user name must meet
+# ANSI version
+##name_regex='^[0-9a-zA-Z_-\ ]*$'
+# ISO-LATIN-1 version
+name_regex='^[0-9a-zA-ZÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõöùúûüýþÿ_-\ ]*$'
+
+# set PATH
+export PATH="/bin:/sbin:/usr/bin:/usr/sbin"
+
+# Some special characters
+case "$TERM" in
+ vt*|ansi*|con*|xterm*|linux*)
+ S='' # start embolden
+ E='' # end embolden
+ ;;
+ *)
+ S=''
+ E=''
+ ;;
+esac
+
+
+## Functions
+
+check_root() {
+ if test "$EUID" -ne 0
+ then
+ echo "You must be root to run this program."
+ exit 1
+ fi
+}
+
+check_user() {
+ local usr pwd uid gid name home sh
+
+ cat /etc/passwd | (
+ while IFS=":" read usr pwd uid gid name home sh
+ do
+ if test "$1" = "${usr}"
+ then
+ return 1
+ fi
+ done
+ return 0
+ )
+}
+
+check_group() {
+ local read grp pwd gid members
+
+ cat /etc/group | (
+ while IFS=":" read grp pwd gid members
+ do
+ if test "$1" = "${grp}"
+ then
+ return 1
+ fi
+ done
+ return 0
+ )
+}
+
+check_other_groups() {
+ local grp check IFS
+
+ check="$1"
+ IFS=","
+
+ set ${check}
+ for grp
+ do
+ if check_group "${grp}"
+ then
+ echo "Group ${grp} does not exist."
+ return 1
+ fi
+ done
+ return 0
+}
+
+check_uid() {
+ local usr pwd uid gid name home sh
+
+ cat /etc/passwd | (
+ while IFS=":" read usr pwd uid gid name home sh
+ do
+ if test "$1" = "${uid}"
+ then
+ return 1
+ fi
+ done
+ return 0
+ )
+}
+
+read_yn() {
+ local ans ynd
+
+ ynd="$1"
+
+ while :
+ do
+ read ans
+ case "${ans}" in
+ "") return ${ynd} ;;
+ [nN]) return 1 ;;
+ [yY]) return 0 ;;
+ *) echo -n "Y or N, please ? " ;;
+ esac
+ done
+}
+
+read_login() {
+ echo
+ while :
+ do
+ echo -n "Login: ${def_login:+[${def_login}] }"
+ read login
+
+ if test "${login}" = '?'
+ then
+ less /etc/passwd
+ echo
+ continue
+ fi
+
+ if test -z "${login}" -a -n "${def_login}"
+ then
+ login="${def_login}"
+ echo "Using ${login}"
+ return
+ fi
+
+ if test "${#login}" -gt 8
+ then
+ echo "Login must be at most 8 characters long"
+ continue
+ fi
+
+ if test "${#login}" -lt 2
+ then
+ echo "Login must be at least 2 characters long"
+ continue
+ fi
+
+ if ! expr "${login}" : "${login_regex}" &> /dev/null
+ then
+ echo "Please use letters, numbers and special characters _-,."
+ continue
+ fi
+
+ if ! check_user "${login}"
+ then
+ echo "Username ${login} is already in use"
+ continue
+ fi
+
+ def_login="${login}"
+ return
+ done
+}
+
+read_name () {
+ echo
+ while :
+ do
+ echo -n "Real name: ${def_name:+[${def_name}] }"
+ read name
+
+ if test "${name}" = '?'
+ then
+ less /etc/passwd
+ echo
+ continue
+ fi
+
+ if test -z "${name}" -a -n "${def_name}"
+ then
+ name="${def_name}"
+ echo "Using ${name}"
+ fi
+
+ if test "${#name}" -gt 32
+ then
+ echo "Name should be at most 32 characters long"
+ continue
+ fi
+
+ if ! expr "${name}" : "${name_regex}" &> /dev/null
+ then
+ echo "Please use letters, numbers, spaces and special characters ,._-"
+ continue
+ fi
+
+ def_name="${name}"
+ return
+ done
+}
+
+read_home() {
+ local x
+
+ echo
+ while :
+ do
+ echo -n "Home Directory: [${def_home_dir}/${login}] "
+ read home
+
+ if test -z "${home}"
+ then
+ home="${def_home_dir}/${login}"
+ echo "Using ${home}"
+ fi
+
+ if ! expr "${home}" : '^[0-9a-zA-Z,._-\/]*$' &> /dev/null
+ then
+ echo "Please use letters, numbers, spaces and special characters ,._-/"
+ continue
+ fi
+
+ x="$(basename ${home})"
+ if test "${x}" != "${login}"
+ then
+ echo "Warning: you are about to use different login name and home directory."
+ fi
+
+ x="$(dirname ${home})"
+ if ! test -d "${x}"
+ then
+ echo "Directory ${x} does not exist."
+ echo "If you still want to use it, please make it manually."
+ continue
+ fi
+
+ def_home_dir="${x}"
+ return
+ done
+}
+
+read_shell () {
+ local x
+
+ echo
+ while :
+ do
+ echo -n "Shell: [${def_shell}] "
+ read shell
+
+ if test -z "${shell}"
+ then
+ shell="${def_shell}"
+ echo "Using ${shell}"
+ fi
+
+ for x in $(cat /etc/shells)
+ do
+ if test "${x}" = "${shell}"
+ then
+ def_shell="${shell}"
+ return
+ fi
+ done
+
+ echo "Possible shells are:"
+ cat /etc/shells
+ done
+}
+
+read_group () {
+ echo
+ while :
+ do
+ echo -n "Group: [${def_group}] "
+ read group
+
+ if test -z "${group}"
+ then
+ group="${def_group}"
+ echo "Using ${group}"
+ fi
+
+ if test "${group}" = '?'
+ then
+ less /etc/group
+ echo
+ continue
+ fi
+
+ if check_group "${group}"
+ then
+ echo "Group ${group} does not exist."
+ continue
+ fi
+
+ def_group="${group}"
+ return
+ done
+}
+
+read_other_groups () {
+ echo
+ while :
+ do
+ echo -n "Other groups: [${def_og:-none}] "
+ read other_groups
+
+ if test "${other_groups}" = '?'
+ then
+ less /etc/group
+ echo
+ continue
+ fi
+
+ if test -z "${other_groups}"
+ then
+ if test -n "${def_og}"
+ then
+ other_groups="${def_og}"
+ echo "Using ${other_groups}"
+ else
+ echo "No other groups"
+ return
+ fi
+ fi
+
+
+ if ! check_other_groups "${other_groups}"
+ then
+ continue
+ fi
+
+ def_og="${other_groups}"
+ return
+ done
+}
+
+read_uid () {
+ echo
+ while :
+ do
+ echo -n "uid: [first free] "
+ read uid
+
+ if test -z "${uid}"
+ then
+ echo "Using first free UID."
+ return
+ fi
+
+ if test "${uid}" = '?'
+ then
+ less /etc/passwd
+ echo
+ continue
+ fi
+
+ if ! expr "${uid}" : '^[0-9]+$' &> /dev/null
+ then
+ echo "Please use numbers only."
+ continue
+ fi
+ if test "${uid}" -lt "${uid_low}"
+ then
+ echo "UID must be greater than ${uid_low}"
+ continue
+ fi
+ if test "${uid}" -gt "${uid_high}"
+ then
+ echo "UID must be smaller than ${uid_high}"
+ continue
+ fi
+ if ! check_uid "${uid}"
+ then
+ echo "UID ${uid} is already in use"
+ continue
+ fi
+
+ return
+ done
+}
+
+read_max_valid_days() {
+ echo
+ while :
+ do
+ echo -en "Maximum days between password changes: [${def_pwd_max}] "
+ read max_days
+
+ if test -z "${max_days}"
+ then
+ max_days="${def_pwd_max}"
+ echo "Using ${max_days}"
+ return
+ fi
+
+ if ! expr "${max_days}" : '^[0-9]+$' &> /dev/null
+ then
+ echo "Please use numbers only."
+ continue
+ fi
+ if test "${max_days}" -lt 7
+ then
+ echo "Warning: you are using a value shorter than a week."
+ fi
+
+ def_pwd_max="${max_days}"
+ return
+ done
+}
+
+read_min_valid_days() {
+ echo
+ while :
+ do
+ echo -en "Minimum days between password changes: [${def_pwd_min}] "
+ read min_days
+
+ if test -z "${min_days}"
+ then
+ min_days="${def_pwd_min}"
+ echo "Using ${min_days}"
+ return
+ fi
+
+ if ! expr "${min_days}" : '^[0-9]+$' &> /dev/null
+ then
+ echo "Please use numbers only."
+ continue
+ fi
+ if test "${min_days}" -gt 7
+ then
+ echo "Warning: you are using a value longer than a week."
+ fi
+
+ def_pwd_min="${min_days}"
+ return
+ done
+}
+
+read_warning_days() {
+ echo
+ while :
+ do
+ echo -en "Number of warning days before password expires: [${def_pwd_warn}] "
+ read warn_days
+
+ if test -z "${warn_days}"
+ then
+ warn_days="${def_pwd_warn}"
+ echo "Using ${warn_days}"
+ fi
+
+ if ! expr "${warn_days}" : '^[0-9]+$' &> /dev/null
+ then
+ echo "Please use numbers only."
+ continue
+ fi
+ if test "${warn_days}" -gt 14
+ then
+ echo "Warning: you are using a value longer than two week."
+ fi
+
+ def_pwd_warn="${warn_days}"
+ return
+ done
+}
+
+
+read_inactive_days() {
+ echo
+ while :
+ do
+ echo -en "Number of usable days after expiration: [${def_pwd_iact}] "
+ read iact_days
+
+ if test -z "${iact_days}"
+ then
+ iact_days="${def_pwd_iact}"
+ echo "Using ${iact_days}"
+ return
+ fi
+ if ! expr "${iact_days}" : '^[0-9]+$' &> /dev/null
+ then
+ echo "Please use numbers only."
+ continue
+ fi
+ if test "${iact_days}" -gt 14
+ then
+ echo "Warning: you are using a value that is more than two weeks."
+ fi
+
+ def_pwd_iact="${iact_days}"
+ return
+ done
+}
+
+read_expire_date() {
+ local ans
+
+ echo
+ while :
+ do
+ echo -en "Expire date of this account (mm/dd/yy): [${def_expire:-never}] "
+ read ans
+
+ if test -z "${ans}"
+ then
+ if test -z "${def_expire}"
+ then
+ ans="never"
+ else
+ ans="${def_expire}"
+ echo "Using ${def_expire}"
+ fi
+ fi
+
+ if test "${ans}" = "never"
+ then
+ echo "Account will never expire."
+ def_expire=""
+ expire=""
+ return
+ fi
+
+ if ! expr "${ans}" : '^[0-9][0-9]/[0-9][0-9]/[0-9][0-9]$' &> /dev/null
+ then
+ echo "Please use format mm/dd/yy"
+ continue
+ fi
+
+ if ! expire_date="$(date -d ${ans} '+%A, %B %d %Y')"
+ then
+ continue
+ fi
+
+ def_expire="${expire}"
+ return
+ done
+}
+
+read_passwd_yn() {
+ echo -en "\nDo you want to set password [Y/n] ? "
+ if read_yn 0
+ then
+ set_pwd="YES"
+ else
+ set_pwd=""
+ fi
+}
+
+
+print_values() {
+
+clear
+cat << EOM
+
+Login: ${S}${login}${E}
+Group: ${S}${group}${E}
+Other groups: ${S}${other_groups:-[none]}${E}
+
+Real Name: ${S}${name}${E}
+
+uid: ${S}${uid:-[first free]}${E}
+home: ${S}${home}${E}
+shell: ${S}${shell}${E}
+
+Account expiration date: ${S}${expire_date:-never}${E}
+Minimum days between password changes: ${S}${min_days}${E}
+Maximum days between password changes: ${S}${max_days}${E}
+Number of usable days after expiration: ${S}${iact_days}${E}
+Number of warning days before expiration: ${S}${warn_days}${E}
+
+${S}${set_pwd:+Set password for this account.}${E}
+
+EOM
+}
+
+set_user() {
+ if ! useradd \
+ -c "${name}" \
+ -d "${home}" \
+ -g "${group}" \
+ -s "${shell}" \
+ ${expire:+-e ${expire}} \
+ ${uid:+-u ${uid}} \
+ ${other_groups:+-G ${other_groups}} \
+ ${login}
+ then
+ echo "Error ($?) in useradd...exiting..."
+ exit 1
+ fi
+}
+
+set_aging() {
+ if ! passwd \
+ -x ${max_days} \
+ -n ${min_days} \
+ -w ${warn_days} \
+ -i ${iact_days} \
+ ${login}
+ then
+ echo "Error ($?) in setting password aging...exiting..."
+ exit 1
+ fi
+}
+
+set_password() {
+ if test -n "${set_pwd}"
+ then
+ echo
+ passwd ${login}
+ echo
+ fi
+}
+
+set_system() {
+ if test -d "${home}"
+ then
+ echo "Directory ${home} already exists."
+ echo "Skeleton files not copied."
+ return
+ fi
+
+ echo -n "Copying skeleton files..."
+ (
+ mkdir ${home}
+ cd ${skel} && cp -af . ${home}
+ chmod ${def_mode} ${home}
+ chown -R ${login}:${group} ${home}
+ )
+ echo "done."
+
+ ## Add your own stuff here:
+ echo -n "Setting up other files..."
+ (
+ mailbox="/var/spool/mail/${login}"
+ touch ${mailbox}
+ chown "${login}:mail" ${mailbox}
+ chmod 600 ${mailbox}
+ )
+ echo "done."
+}
+
+
+read_values() {
+ clear
+ echo -e "\nPlease answer the following questions about the new user to be added."
+
+ while :
+ do
+ read_login
+ read_name
+ read_group
+ read_other_groups
+ read_home
+ read_shell
+ read_uid
+ read_expire_date
+ read_max_valid_days
+ read_min_valid_days
+ read_warning_days
+ read_inactive_days
+ read_passwd_yn
+
+ print_values
+
+ echo -n "Is this correct [N/y] ? "
+ read_yn 1 && return
+ done
+}
+
+
+main() {
+ check_root
+ read_values
+ set_user
+ set_aging
+ set_system
+ set_password
+}
+
+
+## Run it 8-)
+main
+
+# End.
diff --git a/contrib/atudel b/contrib/atudel
new file mode 100755
index 0000000..0ca8783
--- /dev/null
+++ b/contrib/atudel
@@ -0,0 +1,85 @@
+#!/usr/bin/perl
+#
+# Copyright (c) 1996 Brian R. Gaeke
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+# 3. All advertising materials mentioning features or use of this software
+# must display the following acknowledgement:
+# This product includes software developed by Brian R. Gaeke.
+# 4. The name of the author, Brian R. Gaeke, may not be used to endorse
+# or promote products derived from this software without specific
+# prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY BRIAN R. GAEKE ``AS IS'' AND ANY EXPRESS
+# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+# DISCLAIMED. IN NO EVENT SHALL BRIAN R. GAEKE BE LIABLE FOR ANY DIRECT,
+# INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+# IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+#
+# Additionally:
+#
+# This software is provided without support and without any obligation
+# on the part of Brian R. Gaeke to assist in its use, correction,
+# modification or enhancement.
+#
+#######################################################################
+#
+# this is atudel, version 2, by Brian R. Gaeke <brg@dgate.org>
+#
+
+require "getopts.pl";
+&Getopts('v');
+$username = shift(@ARGV);
+&usage unless $username;
+
+sub usage
+{
+ print STDERR "atudel - remove all at jobs owned by a user\n";
+ print STDERR "usage: $0 [-v] username\n";
+ exit(1);
+}
+
+# odd. unless getpwnam($uname) doesn't seem to work for $uname eq "root" on
+# my linux system. but this does.
+die "user $username does not exist; stopping"
+ unless defined(getpwnam($username));
+
+print "searching for at jobs owned by user $username ..." if $opt_v;
+
+chdir "/var/spool/atjobs" ||
+ die "can't chdir to /var/spool/atjobs: $!\nstopping";
+opendir(DIR,".") || die "can't opendir(/var/spool/atjobs): $!\nstopping";
+@files = grep(!/^\./,grep(-f,readdir(DIR)));
+closedir DIR;
+
+foreach $x (@files)
+{
+ $owner = (getpwuid((stat($x))[4]))[0];
+ push(@nuke_bait,$x) if $owner eq $username;
+}
+
+if (@nuke_bait)
+{
+ print "removed jobIDs: @{nuke_bait}.\n" if $opt_v;
+ unlink @nuke_bait;
+}
+elsif ($opt_v)
+{
+ print "\n";
+}
+
+exit 0;
diff --git a/contrib/groupmems.shar b/contrib/groupmems.shar
new file mode 100644
index 0000000..b2d2547
--- /dev/null
+++ b/contrib/groupmems.shar
@@ -0,0 +1,546 @@
+#!/bin/sh
+# This is a shell archive (produced by GNU sharutils 4.2.1).
+# To extract the files from this archive, save it to some FILE, remove
+# everything before the `!/bin/sh' line above, then type `sh FILE'.
+#
+# Made on 2000-05-25 14:41 CDT by <gk4@gnu.austin.ibm.com>.
+# Source directory was `/home/gk4/src/groupmem'.
+#
+# Existing files will *not* be overwritten unless `-c' is specified.
+#
+# This shar contains:
+# length mode name
+# ------ ---------- ------------------------------------------
+# 1960 -rw-r--r-- Makefile
+# 6348 -rw-r--r-- groupmems.c
+# 3372 -rw------- groupmems.8
+#
+save_IFS="${IFS}"
+IFS="${IFS}:"
+gettext_dir=FAILED
+locale_dir=FAILED
+first_param="$1"
+for dir in $PATH
+do
+ if test "$gettext_dir" = FAILED && test -f $dir/gettext \
+ && ($dir/gettext --version >/dev/null 2>&1)
+ then
+ set `$dir/gettext --version 2>&1`
+ if test "$3" = GNU
+ then
+ gettext_dir=$dir
+ fi
+ fi
+ if test "$locale_dir" = FAILED && test -f $dir/shar \
+ && ($dir/shar --print-text-domain-dir >/dev/null 2>&1)
+ then
+ locale_dir=`$dir/shar --print-text-domain-dir`
+ fi
+done
+IFS="$save_IFS"
+if test "$locale_dir" = FAILED || test "$gettext_dir" = FAILED
+then
+ echo=echo
+else
+ TEXTDOMAINDIR=$locale_dir
+ export TEXTDOMAINDIR
+ TEXTDOMAIN=sharutils
+ export TEXTDOMAIN
+ echo="$gettext_dir/gettext -s"
+fi
+if touch -am -t 200112312359.59 $$.touch >/dev/null 2>&1 && test ! -f 200112312359.59 -a -f $$.touch; then
+ shar_touch='touch -am -t $1$2$3$4$5$6.$7 "$8"'
+elif touch -am 123123592001.59 $$.touch >/dev/null 2>&1 && test ! -f 123123592001.59 -a ! -f 123123592001.5 -a -f $$.touch; then
+ shar_touch='touch -am $3$4$5$6$1$2.$7 "$8"'
+elif touch -am 1231235901 $$.touch >/dev/null 2>&1 && test ! -f 1231235901 -a -f $$.touch; then
+ shar_touch='touch -am $3$4$5$6$2 "$8"'
+else
+ shar_touch=:
+ echo
+ $echo 'WARNING: not restoring timestamps. Consider getting and'
+ $echo "installing GNU \`touch', distributed in GNU File Utilities..."
+ echo
+fi
+rm -f 200112312359.59 123123592001.59 123123592001.5 1231235901 $$.touch
+#
+if mkdir _sh10937; then
+ $echo 'x -' 'creating lock directory'
+else
+ $echo 'failed to create lock directory'
+ exit 1
+fi
+# ============= Makefile ==============
+if test -f 'Makefile' && test "$first_param" != -c; then
+ $echo 'x -' SKIPPING 'Makefile' '(file already exists)'
+else
+ $echo 'x -' extracting 'Makefile' '(text)'
+ sed 's/^X//' << 'SHAR_EOF' > 'Makefile' &&
+/*
+# Copyright 2000, International Business Machines, Inc.
+# All rights reserved.
+#
+# original author: George Kraft IV, gk4@us.ibm.com
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+# 3. Neither the name of International Business Machines, Inc., nor the
+# names of its contributors may be used to endorse or promote products
+# derived from this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY INTERNATIONAL BUSINESS MACHINES, INC. AND
+# CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
+# BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+# INTERNATIONAL BUSINESS MACHINES, INC. OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+X
+all: groupmems
+X
+groupmems: groupmems.c
+X cc -g -o groupmems groupmems.c -L. -lshadow
+X
+install: groupmems
+X -/usr/sbin/groupadd groups
+X install -o root -g groups -m 4770 groupmems /usr/bin
+X
+install.man: groupmems.8
+X install -o root -g root -m 644 groupmems.8 /usr/man/man8
+X
+SHAR_EOF
+ (set 20 00 05 25 14 40 28 'Makefile'; eval "$shar_touch") &&
+ chmod 0644 'Makefile' ||
+ $echo 'restore of' 'Makefile' 'failed'
+ if ( md5sum --help 2>&1 | grep 'sage: md5sum \[' ) >/dev/null 2>&1 \
+ && ( md5sum --version 2>&1 | grep -v 'textutils 1.12' ) >/dev/null; then
+ md5sum -c << SHAR_EOF >/dev/null 2>&1 \
+ || $echo 'Makefile:' 'MD5 check failed'
+b46cf7ef8d59149093c011ced3f3103c Makefile
+SHAR_EOF
+ else
+ shar_count="`LC_ALL= LC_CTYPE= LANG= wc -c < 'Makefile'`"
+ test 1960 -eq "$shar_count" ||
+ $echo 'Makefile:' 'original size' '1960,' 'current size' "$shar_count!"
+ fi
+fi
+# ============= groupmems.c ==============
+if test -f 'groupmems.c' && test "$first_param" != -c; then
+ $echo 'x -' SKIPPING 'groupmems.c' '(file already exists)'
+else
+ $echo 'x -' extracting 'groupmems.c' '(text)'
+ sed 's/^X//' << 'SHAR_EOF' > 'groupmems.c' &&
+/*
+X * Copyright 2000, International Business Machines, Inc.
+X * All rights reserved.
+X *
+X * original author: George Kraft IV, gk4@us.ibm.com
+X *
+X * Redistribution and use in source and binary forms, with or without
+X * modification, are permitted provided that the following conditions
+X * are met:
+X *
+X * 1. Redistributions of source code must retain the above copyright
+X * notice, this list of conditions and the following disclaimer.
+X * 2. Redistributions in binary form must reproduce the above copyright
+X * notice, this list of conditions and the following disclaimer in the
+X * documentation and/or other materials provided with the distribution.
+X * 3. Neither the name of International Business Machines, Inc., nor the
+X * names of its contributors may be used to endorse or promote products
+X * derived from this software without specific prior written permission.
+X *
+X * THIS SOFTWARE IS PROVIDED BY INTERNATIONAL BUSINESS MACHINES, INC. AND
+X * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
+X * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+X * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+X * INTERNATIONAL BUSINESS MACHINES, INC. OR CONTRIBUTORS BE LIABLE
+X * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+X * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+X * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+X * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+X * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+X * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+X * SUCH DAMAGE.
+X */
+/*
+**
+** Utility "groupmem" adds and deletes members from a user's group.
+**
+** Setup (as "root"):
+**
+** groupadd -r groups
+** chmod 2770 groupmems
+** chown root.groups groupmems
+** groupmems -g groups -a gk4
+**
+** Usage (as "gk4"):
+**
+** groupmems -a olive
+** groupmems -a jordan
+** groupmems -a meghan
+** groupmems -a morgan
+** groupmems -a jake
+** groupmems -l
+** groupmems -d jake
+** groupmems -l
+*/
+X
+#include <stdio.h>
+#include <pwd.h>
+#include <grp.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include "defines.h"
+#include "groupio.h"
+X
+/* Exit Status Values */
+X
+#define EXIT_SUCCESS 0 /* success */
+#define EXIT_USAGE 1 /* invalid command syntax */
+#define EXIT_GROUP_FILE 2 /* group file access problems */
+#define EXIT_NOT_ROOT 3 /* not superuser */
+#define EXIT_NOT_EROOT 4 /* not effective superuser */
+#define EXIT_NOT_PRIMARY 5 /* not primary owner of group */
+#define EXIT_NOT_MEMBER 6 /* member of group does not exist */
+#define EXIT_MEMBER_EXISTS 7 /* member of group already exists */
+X
+#define TRUE 1
+#define FALSE 0
+X
+/* Globals */
+X
+extern int optind;
+extern char *optarg;
+static char *adduser = NULL;
+static char *deluser = NULL;
+static char *thisgroup = NULL;
+static int purge = FALSE;
+static int list = FALSE;
+static int exclusive = 0;
+X
+static int isroot(void) {
+X return getuid() ? FALSE : TRUE;
+}
+X
+static int isgroup(void) {
+X gid_t g = getgid();
+X struct group *grp = getgrgid(g);
+X
+X return TRUE;
+}
+X
+static char *whoami(void) {
+X struct group *grp = getgrgid(getgid());
+X struct passwd *usr = getpwuid(getuid());
+X
+X if (0 == strcmp(usr->pw_name, grp->gr_name)) {
+X return (char *)strdup(usr->pw_name);
+X } else {
+X return NULL;
+X }
+}
+X
+static void
+addtogroup(char *user, char **members) {
+X int i;
+X char **pmembers;
+X
+X for (i = 0; NULL != members[i]; i++ ) {
+X if (0 == strcmp(user, members[i])) {
+X fprintf(stderr, "Member already exists\n");
+X exit(EXIT_MEMBER_EXISTS);
+X }
+X }
+X
+X if (0 == i) {
+X pmembers = (char **)calloc(2, sizeof(char *));
+X } else {
+X pmembers = (char **)realloc(members, sizeof(char *)*(i+1));
+X }
+X
+X *members = *pmembers;
+X members[i] = user;
+X members[i+1] = NULL;
+}
+X
+static void
+rmfromgroup(char *user, char **members) {
+X int i;
+X int found = FALSE;
+X
+X i = 0;
+X while (!found && NULL != members[i]) {
+X if (0 == strcmp(user, members[i])) {
+X found = TRUE;
+X } else {
+X i++;
+X }
+X }
+X
+X while (found && NULL != members[i]) {
+X members[i] = members[++i];
+X }
+X
+X if (!found) {
+X fprintf(stderr, "Member to remove could not be found\n");
+X exit(EXIT_NOT_MEMBER);
+X }
+}
+X
+static void
+nomembers(char **members) {
+X int i;
+X
+X for (i = 0; NULL != members[i]; i++ ) {
+X members[i] = NULL;
+X }
+}
+X
+static void
+members(char **members) {
+X int i;
+X
+X for (i = 0; NULL != members[i]; i++ ) {
+X printf("%s ", members[i]);
+X
+X if (NULL == members[i+1]) {
+X printf("\n");
+X } else {
+X printf(" ");
+X }
+X }
+}
+X
+static void usage(void) {
+X fprintf(stderr, "usage: groupmems -a username | -d username | -D | -l [-g groupname]\n");
+X exit(EXIT_USAGE);
+}
+X
+main(int argc, char **argv) {
+X int arg, i;
+X char *name;
+X struct group *grp;
+X
+X while ((arg = getopt(argc, argv, "a:d:g:Dl")) != EOF) {
+X switch (arg) {
+X case 'a':
+X adduser = strdup(optarg);
+X ++exclusive;
+X break;
+X case 'd':
+X deluser = strdup(optarg);
+X ++exclusive;
+X break;
+X case 'g':
+X thisgroup = strdup(optarg);
+X break;
+X case 'D':
+X purge = TRUE;
+X ++exclusive;
+X break;
+X case 'l':
+X list = TRUE;
+X ++exclusive;
+X break;
+X default:
+X usage();
+X }
+X }
+X
+X if (exclusive > 1 || optind < argc) {
+X usage();
+X }
+X
+X if (!isroot() && NULL != thisgroup) {
+X fprintf(stderr, "Only root can add members to different groups\n");
+X exit(EXIT_NOT_ROOT);
+X } else if (isroot() && NULL != thisgroup) {
+X name = thisgroup;
+X } else if (!isgroup()) {
+X fprintf(stderr, "Group access is required\n");
+X exit(EXIT_NOT_EROOT);
+X } else if (NULL == (name = whoami())) {
+X fprintf(stderr, "Not primary owner of current group\n");
+X exit(EXIT_NOT_PRIMARY);
+X }
+X
+X if (!gr_lock()) {
+X fprintf(stderr, "Unable to lock group file\n");
+X exit(EXIT_GROUP_FILE);
+X }
+X
+X if (!gr_open(O_RDWR)) {
+X fprintf(stderr, "Unable to open group file\n");
+X exit(EXIT_GROUP_FILE);
+X }
+X
+X grp = (struct group *)gr_locate(name);
+X
+X if (NULL != adduser) {
+X addtogroup(adduser, grp->gr_mem);
+X gr_update(grp);
+X } else if (NULL != deluser) {
+X rmfromgroup(deluser, grp->gr_mem);
+X gr_update(grp);
+X } else if (purge) {
+X nomembers(grp->gr_mem);
+X gr_update(grp);
+X } else if (list) {
+X members(grp->gr_mem);
+X }
+X
+X if (!gr_close()) {
+X fprintf(stderr, "Cannot close group file\n");
+X exit(EXIT_GROUP_FILE);
+X }
+X
+X gr_unlock();
+X
+X exit(EXIT_SUCCESS);
+}
+X
+/* EOF */
+SHAR_EOF
+ (set 20 00 05 25 14 36 38 'groupmems.c'; eval "$shar_touch") &&
+ chmod 0644 'groupmems.c' ||
+ $echo 'restore of' 'groupmems.c' 'failed'
+ if ( md5sum --help 2>&1 | grep 'sage: md5sum \[' ) >/dev/null 2>&1 \
+ && ( md5sum --version 2>&1 | grep -v 'textutils 1.12' ) >/dev/null; then
+ md5sum -c << SHAR_EOF >/dev/null 2>&1 \
+ || $echo 'groupmems.c:' 'MD5 check failed'
+f0dd68f8d762d89d24d3ce1f4141f981 groupmems.c
+SHAR_EOF
+ else
+ shar_count="`LC_ALL= LC_CTYPE= LANG= wc -c < 'groupmems.c'`"
+ test 6348 -eq "$shar_count" ||
+ $echo 'groupmems.c:' 'original size' '6348,' 'current size' "$shar_count!"
+ fi
+fi
+# ============= groupmems.8 ==============
+if test -f 'groupmems.8' && test "$first_param" != -c; then
+ $echo 'x -' SKIPPING 'groupmems.8' '(file already exists)'
+else
+ $echo 'x -' extracting 'groupmems.8' '(text)'
+ sed 's/^X//' << 'SHAR_EOF' > 'groupmems.8' &&
+X.\"
+X.\" Copyright 2000, International Business Machines, Inc.
+X.\" All rights reserved.
+X.\"
+X.\" original author: George Kraft IV, gk4@us.ibm.com
+X.\"
+X.\" Redistribution and use in source and binary forms, with or without
+X.\" modification, are permitted provided that the following conditions
+X.\" are met:
+X.\"
+X.\" 1. Redistributions of source code must retain the above copyright
+X.\" notice, this list of conditions and the following disclaimer.
+X.\" 2. Redistributions in binary form must reproduce the above copyright
+X.\" notice, this list of conditions and the following disclaimer in the
+X.\" documentation and/or other materials provided with the distribution.
+X.\" 3. Neither the name of International Business Machines, Inc., nor the
+X.\" names of its contributors may be used to endorse or promote products
+X.\" derived from this software without specific prior written permission.
+X.\"
+X.\" THIS SOFTWARE IS PROVIDED BY INTERNATIONAL BUSINESS MACHINES, INC. AND
+X.\" CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
+X.\" BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+X.\" FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+X.\" INTERNATIONAL BUSINESS MACHINES, INC. OR CONTRIBUTORS BE LIABLE
+X.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+X.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+X.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+X.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+X.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+X.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+X.\" SUCH DAMAGE.
+X.\"
+X.\" $Id$
+X.\"
+X.TH GROUPMEMS 8
+X.SH NAME
+groupmems \- Administer members of a user's primary group
+X.SH SYNOPSIS
+X.B groupmems
+\fB-a\fI user_name \fR |
+\fB-d\fI user_name \fR |
+\fB-l\fR |
+\fB-D\fR |
+[\fB-g\fI group_name \fR]
+X.SH DESCRIPTION
+The \fBgroupmems\fR utility allows a user to administer his/her own
+group membership list without the requirement of superuser privileges.
+The \fBgroupmems\fR utility is for systems that configure its users to
+be in their own name sake primary group (i.e., guest / guest).
+X.P
+Only the superuser, as administrator, can use \fBgroupmems\fR to alter
+the memberships of other groups.
+X.IP "\fB-a \fIuser_name\fR"
+Add a new user to the group membership list.
+X.IP "\fB-d \fIuser_name\fR"
+Delete a user from the group membership list.
+X.IP "\fB-l\fR"
+List the group membership list.
+X.IP "\fB-D\fR"
+Delete all users from the group membership list.
+X.IP "\fB-g \fIgroup_name\fR"
+The superuser can specify which group membership list to modify.
+X.SH SETUP
+The \fBgroupmems\fR executable should be in mode \fB2770\fR as user \fBroot\fR
+and in group \fBgroups\fR. The system administrator can add users to
+group groups to allow or disallow them using the \fBgroupmems\fR utility
+to manager their own group membership list.
+X.P
+X $ groupadd -r groups
+X.br
+X $ chmod 2770 groupmems
+X.br
+X $ chown root.groups groupmems
+X.br
+X $ groupmems -g groups -a gk4
+X.SH FILES
+/etc/group
+X.br
+/etc/gshadow
+X.SH SEE ALSO
+X.BR chfn (1),
+X.BR chsh (1),
+X.BR useradd (8),
+X.BR userdel (8),
+X.BR usermod (8),
+X.BR passwd (1),
+X.BR groupadd (8),
+X.BR groupdel (8)
+X.SH AUTHOR
+George Kraft IV (gk4@us.ibm.com)
+X.\" EOF
+SHAR_EOF
+ (set 20 00 05 25 14 38 23 'groupmems.8'; eval "$shar_touch") &&
+ chmod 0600 'groupmems.8' ||
+ $echo 'restore of' 'groupmems.8' 'failed'
+ if ( md5sum --help 2>&1 | grep 'sage: md5sum \[' ) >/dev/null 2>&1 \
+ && ( md5sum --version 2>&1 | grep -v 'textutils 1.12' ) >/dev/null; then
+ md5sum -c << SHAR_EOF >/dev/null 2>&1 \
+ || $echo 'groupmems.8:' 'MD5 check failed'
+181e6cd3a3c9d3df320197fa2cde2b4a groupmems.8
+SHAR_EOF
+ else
+ shar_count="`LC_ALL= LC_CTYPE= LANG= wc -c < 'groupmems.8'`"
+ test 3372 -eq "$shar_count" ||
+ $echo 'groupmems.8:' 'original size' '3372,' 'current size' "$shar_count!"
+ fi
+fi
+rm -fr _sh10937
+exit 0
diff --git a/contrib/pwdauth.c b/contrib/pwdauth.c
new file mode 100644
index 0000000..ca15495
--- /dev/null
+++ b/contrib/pwdauth.c
@@ -0,0 +1,308 @@
+/*
+ * pwdauth.c - program to verify a given username/password pair.
+ *
+ * Run it with username in argv[1] (may be omitted - default is the
+ * current user), and send it the password over a pipe on stdin.
+ * Exit status: 0 - correct password, 1 - wrong password, >1 - other
+ * errors. For use with shadow passwords, this program should be
+ * installed setuid root.
+ *
+ * This can be used, for example, by xlock - you don't have to install
+ * this large and complex (== possibly insecure) program setuid root,
+ * just modify it to run this simple program to do the authentication.
+ *
+ * Recent versions (xlockmore-3.9) are cleaner, and drop privileges as
+ * soon as possible after getting the user's encrypted password.
+ * Using this program probably doesn't make it more secure, and has one
+ * disadvantage: since we don't get the encrypted user's password at
+ * startup (but at the time the user is authenticated), it is not clear
+ * how we should handle errors (like getpwnam() returning NULL).
+ * - fail the authentication? Problem: no way to unlock (other than kill
+ * the process from somewhere else) if the NIS server stops responding.
+ * - succeed and unlock? Problem: it's too easy to unlock by unplugging
+ * the box from the network and waiting until NIS times out...
+ *
+ * This program is Copyright (C) 1996 Marek Michalkiewicz
+ * <marekm@i17linuxb.ists.pwr.wroc.pl>.
+ *
+ * It may be used and distributed freely for any purposes. There is no
+ * warranty - use at your own risk. I am not liable for any damages etc.
+ * If you improve it, please send me your changes.
+ */
+
+static char rcsid[] = "$Id$";
+
+/*
+ * Define USE_SYSLOG to use syslog() to log successful and failed
+ * authentication. This should be safe even if your system has
+ * the infamous syslog buffer overrun security problem...
+ */
+#define USE_SYSLOG
+
+/*
+ * Define HAVE_GETSPNAM to get shadow passwords using getspnam().
+ * Some systems don't have getspnam(), but getpwnam() returns
+ * encrypted passwords only if running as root.
+ *
+ * According to the xlock source (not tested, except Linux) -
+ * define: Linux, Solaris 2.x, SVR4, ...
+ * undef: HP-UX with Secured Passwords, FreeBSD, NetBSD, QNX.
+ * Known not supported (yet): Ultrix, OSF/1, SCO.
+ */
+#define HAVE_GETSPNAM
+
+/*
+ * Define HAVE_PW_ENCRYPT to use pw_encrypt() instead of crypt().
+ * pw_encrypt() is like the standard crypt(), except that it may
+ * support better password hashing algorithms.
+ *
+ * Define if linking with libshadow.a from the shadow password
+ * suite (Linux, SunOS 4.x?).
+ */
+#undef HAVE_PW_ENCRYPT
+
+/*
+ * Define HAVE_AUTH_METHODS to support the shadow suite specific
+ * extension: the encrypted password field contains a list of
+ * administrator defined authentication methods, separated by
+ * semicolons. This program only supports the standard password
+ * authentication method (a string that doesn't start with '@').
+ */
+#undef HAVE_AUTH_METHODS
+
+/*
+ * FAIL_DELAY - number of seconds to sleep before exiting if the
+ * password was wrong, to slow down password guessing attempts.
+ */
+#define FAIL_DELAY 2
+
+/* No user-serviceable parts below :-). */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <pwd.h>
+
+#ifdef USE_SYSLOG
+#include <syslog.h>
+#ifndef LOG_AUTHPRIV
+#define LOG_AUTHPRIV LOG_AUTH
+#endif
+#endif
+
+#ifdef HAVE_GETSPNAM
+#include <shadow.h>
+#endif
+
+#ifdef HAVE_PW_ENCRYPT
+extern char *pw_encrypt();
+#define crypt pw_encrypt
+#endif
+
+/*
+ * Read the password (one line) from fp. We don't turn off echo
+ * because we expect input from a pipe.
+ */
+static char *
+get_line(fp)
+ FILE *fp;
+{
+ static char buf[128];
+ char *cp;
+ int ch;
+
+ cp = buf;
+ while ((ch = getc(fp)) != EOF && ch != '\0' && ch != '\n') {
+ if (cp >= buf + sizeof buf - 1)
+ break;
+ *cp++ = ch;
+ }
+ *cp = '\0';
+ return buf;
+}
+
+/*
+ * Get the password file entry for the current user. If the name
+ * returned by getlogin() is correct (matches the current real uid),
+ * return the entry for that user. Otherwise, return the entry (if
+ * any) matching the current real uid. Return NULL on failure.
+ */
+static struct passwd *
+get_my_pwent()
+{
+ uid_t uid = getuid();
+ char *name = getlogin();
+
+ if (name && *name) {
+ struct passwd *pw = getpwnam(name);
+
+ if (pw && pw->pw_uid == uid)
+ return pw;
+ }
+ return getpwuid(uid);
+}
+
+/*
+ * Verify the password. The system-dependent shadow support is here.
+ */
+static int
+password_auth_ok(pw, pass)
+ const struct passwd *pw;
+ const char *pass;
+{
+ int result;
+ char *cp;
+#ifdef HAVE_AUTH_METHODS
+ char *buf;
+#endif
+#ifdef HAVE_GETSPNAM
+ struct spwd *sp;
+#endif
+
+ if (pw) {
+#ifdef HAVE_GETSPNAM
+ sp = getspnam(pw->pw_name);
+ if (sp)
+ cp = sp->sp_pwdp;
+ else
+#endif
+ cp = pw->pw_passwd;
+ } else
+ cp = "xx";
+
+#ifdef HAVE_AUTH_METHODS
+ buf = strdup(cp); /* will be modified by strtok() */
+ if (!buf) {
+ fprintf(stderr, "Out of memory.\n");
+ exit(13);
+ }
+ cp = strtok(buf, ";");
+ while (cp && *cp == '@')
+ cp = strtok(NULL, ";");
+
+ /* fail if no password authentication for this user */
+ if (!cp)
+ cp = "xx";
+#endif
+
+ if (*pass || *cp)
+ result = (strcmp(crypt(pass, cp), cp) == 0);
+ else
+ result = 1; /* user with no password */
+
+#ifdef HAVE_AUTH_METHODS
+ free(buf);
+#endif
+ return result;
+}
+
+/*
+ * Main program.
+ */
+int
+main(argc, argv)
+ int argc;
+ char **argv;
+{
+ struct passwd *pw;
+ char *pass, *name;
+ char myname[32];
+
+#ifdef USE_SYSLOG
+ openlog("pwdauth", LOG_PID | LOG_CONS, LOG_AUTHPRIV);
+#endif
+ pw = get_my_pwent();
+ if (!pw) {
+#ifdef USE_SYSLOG
+ syslog(LOG_ERR, "can't get login name for uid %d.\n",
+ (int) getuid());
+#endif
+ fprintf(stderr, "Who are you?\n");
+ exit(2);
+ }
+ strncpy(myname, pw->pw_name, sizeof myname - 1);
+ myname[sizeof myname - 1] = '\0';
+ name = myname;
+
+ if (argc > 1) {
+ name = argv[1];
+ pw = getpwnam(name);
+ }
+
+ pass = get_line(stdin);
+ if (password_auth_ok(pw, pass)) {
+#ifdef USE_SYSLOG
+ syslog(pw->pw_uid ? LOG_INFO : LOG_NOTICE,
+ "user `%s' entered correct password for `%.32s'.\n",
+ myname, name);
+#endif
+ exit(0);
+ }
+#ifdef USE_SYSLOG
+ /* be careful not to overrun the syslog buffer */
+ syslog((!pw || pw->pw_uid) ? LOG_NOTICE : LOG_WARNING,
+ "user `%s' entered incorrect password for `%.32s'.\n",
+ myname, name);
+#endif
+#ifdef FAIL_DELAY
+ sleep(FAIL_DELAY);
+#endif
+ fprintf(stderr, "Wrong password.\n");
+ exit(1);
+}
+
+#if 0
+/*
+ * You can use code similar to the following to run this program.
+ * Return values: >=0 - program exit status (use the <sys/wait.h>
+ * macros to get the exit code, it is shifted left by 8 bits),
+ * -1 - check errno.
+ */
+int
+verify_password(const char *username, const char *password)
+{
+ int pipe_fd[2];
+ int pid, wpid, status;
+
+ if (pipe(pipe_fd))
+ return -1;
+
+ if ((pid = fork()) == 0) {
+ char *arg[3];
+ char *env[1];
+
+ /* child */
+ close(pipe_fd[1]);
+ if (pipe_fd[0] != 0) {
+ if (dup2(pipe_fd[0], 0) != 0)
+ _exit(127);
+ close(pipe_fd[0]);
+ }
+ arg[0] = "/usr/bin/pwdauth";
+ arg[1] = username;
+ arg[2] = NULL;
+ env[0] = NULL;
+ execve(arg[0], arg, env);
+ _exit(127);
+ } else if (pid == -1) {
+ /* error */
+ close(pipe_fd[0]);
+ close(pipe_fd[1]);
+ return -1;
+ }
+ /* parent */
+ close(pipe_fd[0]);
+ write(pipe_fd[1], password, strlen(password));
+ write(pipe_fd[1], "\n", 1);
+ close(pipe_fd[1]);
+
+ while ((wpid = wait(&status)) != pid) {
+ if (wpid == -1)
+ return -1;
+ }
+ return status;
+}
+#endif
diff --git a/contrib/shadow-anonftp.patch b/contrib/shadow-anonftp.patch
new file mode 100644
index 0000000..6938fe4
--- /dev/null
+++ b/contrib/shadow-anonftp.patch
@@ -0,0 +1,147 @@
+Hello Marek,
+
+I have created a diffile against the 980403 release that adds
+functionality to newusers for automatic handling of users with only
+anonomous ftp login (using the guestgroup feature in ftpaccess, which
+means that the users home directory looks like '/home/user/./'). It also
+adds a commandline argument to specify an initial directory structure
+for such users, with a tarball normally containing the bin,lib,etc
+directories used in the chrooted environment.
+
+I am using it to automatically create chunks of users with only ftp
+access for a webserver.
+
+I have tried to follow your coding standards and I believe it is bug
+free but.. well, who knows. :) It's not much code however.
+
+I hope you find it useful. Do what you like with it, feel free to ask if
+anything is unclear.
+
+Best rgds,
+ Calle Karlsson
+ ckn@kash.se
+
+diff -uNr shadow-980403.orig/src/newusers.c shadow-980403/src/newusers.c
+--- shadow-980403.orig/src/newusers.c Fri Jan 30 00:22:43 1998
++++ shadow-980403/src/newusers.c Fri Apr 17 16:55:33 1998
+@@ -76,11 +76,35 @@
+ static void
+ usage(void)
+ {
+- fprintf(stderr, "Usage: %s [ input ]\n", Prog);
++ fprintf (stderr, "Usage: %s [-p prototype tarfile] [ input ]\n", Prog);
++ fprintf (stderr, "The prototype tarfile is only used for users\n");
++ fprintf (stderr, "marked as anonymous ftp users. It must be a full pathname.\n");
+ exit(1);
+ }
+
+ /*
++ * createuserdir - create a directory and chmod it
++ */
++
++static int
++createuserdir (char * dir, int uid, int gid, int line)
++{
++ if (mkdir (dir, 0777 & ~getdef_num("UMASK", 077))) {
++ fprintf (stderr, "%s: line %d: mkdir %s failed\n",
++ Prog, line, dir);
++ return -1;
++ }
++
++ if (chown (dir, uid, gid)) {
++ fprintf (stderr, "%s: line %d: chown %s failed\n",
++ Prog, line, dir);
++ return -1;
++ }
++
++ return 0;
++}
++
++/*
+ * add_group - create a new group or add a user to an existing group
+ */
+
+@@ -328,6 +352,8 @@
+ main(int argc, char **argv)
+ {
+ char buf[BUFSIZ];
++ char anonproto[BUFSIZ];
++ int flag;
+ char *fields[8];
+ int nfields;
+ char *cp;
+@@ -340,12 +366,23 @@
+
+ Prog = Basename(argv[0]);
+
+- if (argc > 1 && argv[1][0] == '-')
+- usage ();
++ * anonproto = '\0';
++
++ while ((flag = getopt (argc, argv, "p:h")) != EOF) {
++ switch (flag) {
++ case 'p':
++ STRFCPY(anonproto, optarg);
++ break;
++ case 'h':
++ default:
++ usage ();
++ break;
++ }
++ }
+
+- if (argc == 2) {
+- if (! freopen (argv[1], "r", stdin)) {
+- snprintf(buf, sizeof buf, "%s: %s", Prog, argv[1]);
++ if (optind < argc) {
++ if (! freopen (argv[optind], "r", stdin)) {
++ snprintf(buf, sizeof buf, "%s: %s", Prog, argv[optind]);
+ perror (buf);
+ exit (1);
+ }
+@@ -499,15 +536,36 @@
+ if (fields[6][0])
+ newpw.pw_shell = fields[6];
+
+- if (newpw.pw_dir[0] && access(newpw.pw_dir, F_OK)) {
+- if (mkdir (newpw.pw_dir,
+- 0777 & ~getdef_num("UMASK", 077)))
+- fprintf (stderr, "%s: line %d: mkdir failed\n",
+- Prog, line);
+- else if (chown (newpw.pw_dir,
+- newpw.pw_uid, newpw.pw_gid))
+- fprintf (stderr, "%s: line %d: chown failed\n",
+- Prog, line);
++ if (newpw.pw_dir[0]) {
++ char * userdir = strdup (newpw.pw_dir);
++ char * anonpart;
++ int rc;
++
++ if ((anonpart = strstr (userdir, "/./"))) {
++ * anonpart = '\0';
++ anonpart += 2;
++ }
++
++ if (access(userdir, F_OK))
++ rc = createuserdir (userdir, newpw.pw_uid, newpw.pw_gid, line);
++ else
++ rc = 0;
++
++ if (rc == 0 && anonpart) {
++ if (* anonproto) {
++ char cmdbuf [BUFSIZ];
++ snprintf(cmdbuf, sizeof cmdbuf,
++ "cd %s; tar xf %s",
++ userdir, anonproto);
++ system (cmdbuf);
++ }
++ if (strlen (anonpart) > 1) {
++ strcat (userdir, anonpart);
++ if (access (userdir, F_OK))
++ createuserdir (userdir, newpw.pw_uid, newpw.pw_gid, line);
++ }
++ }
++ free (userdir);
+ }
+
+ /*
diff --git a/contrib/udbachk.tgz b/contrib/udbachk.tgz
new file mode 100644
index 0000000..605ad63
--- /dev/null
+++ b/contrib/udbachk.tgz
Binary files differ