summaryrefslogtreecommitdiffstats
path: root/lib
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 /lib
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 'lib')
-rw-r--r--lib/.indent.pro5
-rw-r--r--lib/Makefile.am69
-rw-r--r--lib/commonio.c1271
-rw-r--r--lib/commonio.h170
-rw-r--r--lib/defines.h385
-rw-r--r--lib/encrypt.c95
-rw-r--r--lib/exitcodes.h49
-rw-r--r--lib/faillog.h57
-rw-r--r--lib/fields.c130
-rw-r--r--lib/fputsx.c90
-rw-r--r--lib/get_gid.c54
-rw-r--r--lib/get_pid.c54
-rw-r--r--lib/get_uid.c54
-rw-r--r--lib/getdef.c539
-rw-r--r--lib/getdef.h47
-rw-r--r--lib/getlong.c59
-rw-r--r--lib/getulong.c62
-rw-r--r--lib/groupio.c462
-rw-r--r--lib/groupio.h55
-rw-r--r--lib/groupmem.c106
-rw-r--r--lib/gshadow.c529
-rw-r--r--lib/gshadow_.h75
-rw-r--r--lib/lockpw.c108
-rw-r--r--lib/nscd.c57
-rw-r--r--lib/nscd.h13
-rw-r--r--lib/pam_defs.h58
-rw-r--r--lib/port.c477
-rw-r--r--lib/port.h83
-rw-r--r--lib/prototypes.h440
-rw-r--r--lib/pwauth.c234
-rw-r--r--lib/pwauth.h66
-rw-r--r--lib/pwio.c226
-rw-r--r--lib/pwio.h55
-rw-r--r--lib/pwmem.c106
-rw-r--r--lib/selinux.c103
-rw-r--r--lib/semanage.c378
-rw-r--r--lib/sgetgrent.c154
-rw-r--r--lib/sgetpwent.c122
-rw-r--r--lib/sgetspent.c208
-rw-r--r--lib/sgroupio.c328
-rw-r--r--lib/sgroupio.h52
-rw-r--r--lib/shadow.c557
-rw-r--r--lib/shadowio.c267
-rw-r--r--lib/shadowio.h53
-rw-r--r--lib/shadowmem.c89
-rw-r--r--lib/spawn.c82
-rw-r--r--lib/subordinateio.c701
-rw-r--r--lib/subordinateio.h41
-rw-r--r--lib/tcbfuncs.c613
-rw-r--r--lib/tcbfuncs.h19
-rw-r--r--lib/utent.c93
51 files changed, 10200 insertions, 0 deletions
diff --git a/lib/.indent.pro b/lib/.indent.pro
new file mode 100644
index 0000000..fe572bb
--- /dev/null
+++ b/lib/.indent.pro
@@ -0,0 +1,5 @@
+-kr
+-i8
+-bad
+-pcs
+-l80
diff --git a/lib/Makefile.am b/lib/Makefile.am
new file mode 100644
index 0000000..6db86cd
--- /dev/null
+++ b/lib/Makefile.am
@@ -0,0 +1,69 @@
+
+AUTOMAKE_OPTIONS = 1.0 foreign
+
+DEFS =
+
+noinst_LTLIBRARIES = libshadow.la
+
+libshadow_la_LDFLAGS = -version-info 0:0:0
+
+libshadow_la_SOURCES = \
+ commonio.c \
+ commonio.h \
+ defines.h \
+ encrypt.c \
+ exitcodes.h \
+ faillog.h \
+ fields.c \
+ fputsx.c \
+ getdef.c \
+ getdef.h \
+ get_gid.c \
+ getlong.c \
+ get_pid.c \
+ get_uid.c \
+ getulong.c \
+ groupio.c \
+ groupmem.c \
+ groupio.h \
+ gshadow.c \
+ lockpw.c \
+ nscd.c \
+ nscd.h \
+ pam_defs.h \
+ port.c \
+ port.h \
+ prototypes.h \
+ pwauth.c \
+ pwauth.h \
+ pwio.c \
+ pwio.h \
+ pwmem.c \
+ subordinateio.h \
+ subordinateio.c \
+ selinux.c \
+ semanage.c \
+ sgetgrent.c \
+ sgetpwent.c \
+ sgetspent.c \
+ sgroupio.c \
+ sgroupio.h\
+ shadow.c \
+ shadowio.c \
+ shadowio.h \
+ shadowmem.c \
+ spawn.c \
+ utent.c
+
+if WITH_TCB
+libshadow_la_SOURCES += tcbfuncs.c tcbfuncs.h
+endif
+
+# These files are unneeded for some reason, listed in
+# order of appearance:
+#
+# sources for dbm support (not yet used)
+
+EXTRA_DIST = \
+ .indent.pro \
+ gshadow_.h
diff --git a/lib/commonio.c b/lib/commonio.c
new file mode 100644
index 0000000..31edbaa
--- /dev/null
+++ b/lib/commonio.c
@@ -0,0 +1,1271 @@
+/*
+ * Copyright (c) 1990 - 1994, Julianne Frances Haugh
+ * Copyright (c) 1996 - 2001, Marek Michałkiewicz
+ * Copyright (c) 2001 - 2006, Tomasz Kłoczko
+ * Copyright (c) 2007 - 2011, Nicolas François
+ * 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. The name of the copyright holders or contributors may not be used to
+ * endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 THE COPYRIGHT
+ * HOLDERS 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.
+ */
+
+#include <config.h>
+
+#ident "$Id$"
+
+#include "defines.h"
+#include <assert.h>
+#include <sys/stat.h>
+#include <stdlib.h>
+#include <limits.h>
+#include <utime.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <stdio.h>
+#include <signal.h>
+#include "nscd.h"
+#ifdef WITH_TCB
+#include <tcb.h>
+#endif /* WITH_TCB */
+#include "prototypes.h"
+#include "commonio.h"
+
+/* local function prototypes */
+static int lrename (const char *, const char *);
+static int check_link_count (const char *file);
+static int do_lock_file (const char *file, const char *lock, bool log);
+static /*@null@*/ /*@dependent@*/FILE *fopen_set_perms (
+ const char *name,
+ const char *mode,
+ const struct stat *sb);
+static int create_backup (const char *, FILE *);
+static void free_linked_list (struct commonio_db *);
+static void add_one_entry (
+ struct commonio_db *db,
+ /*@owned@*/struct commonio_entry *p);
+static bool name_is_nis (const char *name);
+static int write_all (const struct commonio_db *);
+static /*@dependent@*/ /*@null@*/struct commonio_entry *find_entry_by_name (
+ struct commonio_db *,
+ const char *);
+static /*@dependent@*/ /*@null@*/struct commonio_entry *next_entry_by_name (
+ struct commonio_db *,
+ /*@null@*/struct commonio_entry *pos,
+ const char *);
+
+static int lock_count = 0;
+static bool nscd_need_reload = false;
+
+/*
+ * Simple rename(P) alternative that attempts to rename to symlink
+ * target.
+ */
+int lrename (const char *old, const char *new)
+{
+ int res;
+ char *r = NULL;
+
+#if defined(S_ISLNK)
+#ifndef __GLIBC__
+ char resolved_path[PATH_MAX];
+#endif /* !__GLIBC__ */
+ struct stat sb;
+ if (lstat (new, &sb) == 0 && S_ISLNK (sb.st_mode)) {
+#ifdef __GLIBC__ /* now a POSIX.1-2008 feature */
+ r = realpath (new, NULL);
+#else /* !__GLIBC__ */
+ r = realpath (new, resolved_path);
+#endif /* !__GLIBC__ */
+ if (NULL == r) {
+ perror ("realpath in lrename()");
+ } else {
+ new = r;
+ }
+ }
+#endif /* S_ISLNK */
+
+ res = rename (old, new);
+
+#ifdef __GLIBC__
+ if (NULL != r) {
+ free (r);
+ }
+#endif /* __GLIBC__ */
+
+ return res;
+}
+
+static int check_link_count (const char *file)
+{
+ struct stat sb;
+
+ if (stat (file, &sb) != 0) {
+ return 0;
+ }
+
+ if (sb.st_nlink != 2) {
+ return 0;
+ }
+
+ return 1;
+}
+
+
+static int do_lock_file (const char *file, const char *lock, bool log)
+{
+ int fd;
+ pid_t pid;
+ ssize_t len;
+ int retval;
+ char buf[32];
+
+ fd = open (file, O_CREAT | O_EXCL | O_WRONLY, 0600);
+ if (-1 == fd) {
+ if (log) {
+ (void) fprintf (stderr,
+ "%s: %s: %s\n",
+ Prog, file, strerror (errno));
+ }
+ return 0;
+ }
+
+ pid = getpid ();
+ snprintf (buf, sizeof buf, "%lu", (unsigned long) pid);
+ len = (ssize_t) strlen (buf) + 1;
+ if (write (fd, buf, (size_t) len) != len) {
+ if (log) {
+ (void) fprintf (stderr,
+ "%s: %s: %s\n",
+ Prog, file, strerror (errno));
+ }
+ (void) close (fd);
+ unlink (file);
+ return 0;
+ }
+ close (fd);
+
+ if (link (file, lock) == 0) {
+ retval = check_link_count (file);
+ if ((0==retval) && log) {
+ (void) fprintf (stderr,
+ "%s: %s: lock file already used\n",
+ Prog, file);
+ }
+ unlink (file);
+ return retval;
+ }
+
+ fd = open (lock, O_RDWR);
+ if (-1 == fd) {
+ if (log) {
+ (void) fprintf (stderr,
+ "%s: %s: %s\n",
+ Prog, lock, strerror (errno));
+ }
+ unlink (file);
+ errno = EINVAL;
+ return 0;
+ }
+ len = read (fd, buf, sizeof (buf) - 1);
+ close (fd);
+ if (len <= 0) {
+ if (log) {
+ (void) fprintf (stderr,
+ "%s: existing lock file %s without a PID\n",
+ Prog, lock);
+ }
+ unlink (file);
+ errno = EINVAL;
+ return 0;
+ }
+ buf[len] = '\0';
+ if (get_pid (buf, &pid) == 0) {
+ if (log) {
+ (void) fprintf (stderr,
+ "%s: existing lock file %s with an invalid PID '%s'\n",
+ Prog, lock, buf);
+ }
+ unlink (file);
+ errno = EINVAL;
+ return 0;
+ }
+ if (kill (pid, 0) == 0) {
+ if (log) {
+ (void) fprintf (stderr,
+ "%s: lock %s already used by PID %lu\n",
+ Prog, lock, (unsigned long) pid);
+ }
+ unlink (file);
+ errno = EEXIST;
+ return 0;
+ }
+ if (unlink (lock) != 0) {
+ if (log) {
+ (void) fprintf (stderr,
+ "%s: cannot get lock %s: %s\n",
+ Prog, lock, strerror (errno));
+ }
+ unlink (file);
+ return 0;
+ }
+
+ retval = 0;
+ if (link (file, lock) == 0) {
+ retval = check_link_count (file);
+ if ((0==retval) && log) {
+ (void) fprintf (stderr,
+ "%s: %s: lock file already used\n",
+ Prog, file);
+ }
+ } else {
+ if (log) {
+ (void) fprintf (stderr,
+ "%s: cannot get lock %s: %s\n",
+ Prog, lock, strerror (errno));
+ }
+ }
+
+ unlink (file);
+ return retval;
+}
+
+
+static /*@null@*/ /*@dependent@*/FILE *fopen_set_perms (
+ const char *name,
+ const char *mode,
+ const struct stat *sb)
+{
+ FILE *fp;
+ mode_t mask;
+
+ mask = umask (0777);
+ fp = fopen (name, mode);
+ (void) umask (mask);
+ if (NULL == fp) {
+ return NULL;
+ }
+
+#ifdef HAVE_FCHOWN
+ if (fchown (fileno (fp), sb->st_uid, sb->st_gid) != 0) {
+ goto fail;
+ }
+#else /* !HAVE_FCHOWN */
+ if (chown (name, sb->st_mode) != 0) {
+ goto fail;
+ }
+#endif /* !HAVE_FCHOWN */
+
+#ifdef HAVE_FCHMOD
+ if (fchmod (fileno (fp), sb->st_mode & 0664) != 0) {
+ goto fail;
+ }
+#else /* !HAVE_FCHMOD */
+ if (chmod (name, sb->st_mode & 0664) != 0) {
+ goto fail;
+ }
+#endif /* !HAVE_FCHMOD */
+ return fp;
+
+ fail:
+ (void) fclose (fp);
+ /* fopen_set_perms is used for intermediate files */
+ (void) unlink (name);
+ return NULL;
+}
+
+
+static int create_backup (const char *backup, FILE * fp)
+{
+ struct stat sb;
+ struct utimbuf ub;
+ FILE *bkfp;
+ int c;
+
+ if (fstat (fileno (fp), &sb) != 0) {
+ return -1;
+ }
+
+ bkfp = fopen_set_perms (backup, "w", &sb);
+ if (NULL == bkfp) {
+ return -1;
+ }
+
+ /* TODO: faster copy, not one-char-at-a-time. --marekm */
+ c = 0;
+ if (fseek (fp, 0, SEEK_SET) == 0) {
+ while ((c = getc (fp)) != EOF) {
+ if (putc (c, bkfp) == EOF) {
+ break;
+ }
+ }
+ }
+ if ((c != EOF) || (ferror (fp) != 0) || (fflush (bkfp) != 0)) {
+ (void) fclose (bkfp);
+ /* FIXME: unlink the backup file? */
+ return -1;
+ }
+ if ( (fsync (fileno (bkfp)) != 0)
+ || (fclose (bkfp) != 0)) {
+ /* FIXME: unlink the backup file? */
+ return -1;
+ }
+
+ ub.actime = sb.st_atime;
+ ub.modtime = sb.st_mtime;
+ (void) utime (backup, &ub);
+ return 0;
+}
+
+
+static void free_linked_list (struct commonio_db *db)
+{
+ struct commonio_entry *p;
+
+ while (NULL != db->head) {
+ p = db->head;
+ db->head = p->next;
+
+ if (NULL != p->line) {
+ free (p->line);
+ }
+
+ if (NULL != p->eptr) {
+ db->ops->free (p->eptr);
+ }
+
+ free (p);
+ }
+ db->tail = NULL;
+}
+
+
+int commonio_setname (struct commonio_db *db, const char *name)
+{
+ snprintf (db->filename, sizeof (db->filename), "%s", name);
+ return 1;
+}
+
+
+bool commonio_present (const struct commonio_db *db)
+{
+ return (access (db->filename, F_OK) == 0);
+}
+
+
+int commonio_lock_nowait (struct commonio_db *db, bool log)
+{
+ char file[1024];
+ char lock[1024];
+
+ if (db->locked) {
+ return 1;
+ }
+
+ snprintf (file, sizeof file, "%s.%lu",
+ db->filename, (unsigned long) getpid ());
+ snprintf (lock, sizeof lock, "%s.lock", db->filename);
+ if (do_lock_file (file, lock, log) != 0) {
+ db->locked = true;
+ lock_count++;
+ return 1;
+ }
+ return 0;
+}
+
+
+int commonio_lock (struct commonio_db *db)
+{
+#ifdef HAVE_LCKPWDF
+ /*
+ * only if the system libc has a real lckpwdf() - the one from
+ * lockpw.c calls us and would cause infinite recursion!
+ */
+
+ /*
+ * Call lckpwdf() on the first lock.
+ * If it succeeds, call *_lock() only once
+ * (no retries, it should always succeed).
+ */
+ if (0 == lock_count) {
+ if (lckpwdf () == -1) {
+ if (geteuid () != 0) {
+ (void) fprintf (stderr,
+ "%s: Permission denied.\n",
+ Prog);
+ }
+ return 0; /* failure */
+ }
+ }
+
+ if (commonio_lock_nowait (db, true) != 0) {
+ return 1; /* success */
+ }
+
+ ulckpwdf ();
+ return 0; /* failure */
+#else /* !HAVE_LCKPWDF */
+ int i;
+
+ /*
+ * lckpwdf() not used - do it the old way.
+ */
+#ifndef LOCK_TRIES
+#define LOCK_TRIES 15
+#endif
+
+#ifndef LOCK_SLEEP
+#define LOCK_SLEEP 1
+#endif
+ for (i = 0; i < LOCK_TRIES; i++) {
+ if (i > 0) {
+ sleep (LOCK_SLEEP); /* delay between retries */
+ }
+ if (commonio_lock_nowait (db, i==LOCK_TRIES-1) != 0) {
+ return 1; /* success */
+ }
+ /* no unnecessary retries on "permission denied" errors */
+ if (geteuid () != 0) {
+ (void) fprintf (stderr, "%s: Permission denied.\n",
+ Prog);
+ return 0;
+ }
+ }
+ return 0; /* failure */
+#endif /* !HAVE_LCKPWDF */
+}
+
+static void dec_lock_count (void)
+{
+ if (lock_count > 0) {
+ lock_count--;
+ if (lock_count == 0) {
+ /* Tell nscd when lock count goes to zero,
+ if any of the files were changed. */
+ if (nscd_need_reload) {
+ nscd_flush_cache ("passwd");
+ nscd_flush_cache ("group");
+ nscd_need_reload = false;
+ }
+#ifdef HAVE_LCKPWDF
+ ulckpwdf ();
+#endif /* HAVE_LCKPWDF */
+ }
+ }
+}
+
+
+int commonio_unlock (struct commonio_db *db)
+{
+ char lock[1024];
+
+ if (db->isopen) {
+ db->readonly = true;
+ if (commonio_close (db) == 0) {
+ if (db->locked) {
+ dec_lock_count ();
+ }
+ return 0;
+ }
+ }
+ if (db->locked) {
+ /*
+ * Unlock in reverse order: remove the lock file,
+ * then call ulckpwdf() (if used) on last unlock.
+ */
+ db->locked = false;
+ snprintf (lock, sizeof lock, "%s.lock", db->filename);
+ unlink (lock);
+ dec_lock_count ();
+ return 1;
+ }
+ return 0;
+}
+
+
+/*
+ * Add an entry at the end.
+ *
+ * defines p->next, p->prev
+ * (unfortunately, owned special are not supported)
+ */
+static void add_one_entry (struct commonio_db *db,
+ /*@owned@*/struct commonio_entry *p)
+{
+ /*@-mustfreeonly@*/
+ p->next = NULL;
+ p->prev = db->tail;
+ /*@=mustfreeonly@*/
+ if (NULL == db->head) {
+ db->head = p;
+ }
+ if (NULL != db->tail) {
+ db->tail->next = p;
+ }
+ db->tail = p;
+}
+
+
+static bool name_is_nis (const char *name)
+{
+ return (('+' == name[0]) || ('-' == name[0]));
+}
+
+
+/*
+ * New entries are inserted before the first NIS entry. Order is preserved
+ * when db is written out.
+ */
+#ifndef KEEP_NIS_AT_END
+#define KEEP_NIS_AT_END 1
+#endif
+
+#if KEEP_NIS_AT_END
+static void add_one_entry_nis (struct commonio_db *db,
+ /*@owned@*/struct commonio_entry *newp);
+
+/*
+ * Insert an entry between the regular entries, and the NIS entries.
+ *
+ * defines newp->next, newp->prev
+ * (unfortunately, owned special are not supported)
+ */
+static void add_one_entry_nis (struct commonio_db *db,
+ /*@owned@*/struct commonio_entry *newp)
+{
+ struct commonio_entry *p;
+
+ for (p = db->head; NULL != p; p = p->next) {
+ if (name_is_nis (p->eptr ? db->ops->getname (p->eptr)
+ : p->line)) {
+ /*@-mustfreeonly@*/
+ newp->next = p;
+ newp->prev = p->prev;
+ /*@=mustfreeonly@*/
+ if (NULL != p->prev) {
+ p->prev->next = newp;
+ } else {
+ db->head = newp;
+ }
+ p->prev = newp;
+ return;
+ }
+ }
+ add_one_entry (db, newp);
+}
+#endif /* KEEP_NIS_AT_END */
+
+/* Initial buffer size, as well as increment if not sufficient
+ (for reading very long lines in group files). */
+#define BUFLEN 4096
+
+int commonio_open (struct commonio_db *db, int mode)
+{
+ char *buf;
+ char *cp;
+ char *line;
+ struct commonio_entry *p;
+ void *eptr = NULL;
+ int flags = mode;
+ size_t buflen;
+ int fd;
+ int saved_errno;
+
+ mode &= ~O_CREAT;
+
+ if ( db->isopen
+ || ( (O_RDONLY != mode)
+ && (O_RDWR != mode))) {
+ errno = EINVAL;
+ return 0;
+ }
+ db->readonly = (mode == O_RDONLY);
+ if (!db->readonly && !db->locked) {
+ errno = EACCES;
+ return 0;
+ }
+
+ db->head = NULL;
+ db->tail = NULL;
+ db->cursor = NULL;
+ db->changed = false;
+
+ fd = open (db->filename,
+ (db->readonly ? O_RDONLY : O_RDWR)
+ | O_NOCTTY | O_NONBLOCK | O_NOFOLLOW);
+ saved_errno = errno;
+ db->fp = NULL;
+ if (fd >= 0) {
+#ifdef WITH_TCB
+ if (tcb_is_suspect (fd) != 0) {
+ (void) close (fd);
+ errno = EINVAL;
+ return 0;
+ }
+#endif /* WITH_TCB */
+ db->fp = fdopen (fd, db->readonly ? "r" : "r+");
+ saved_errno = errno;
+ if (NULL == db->fp) {
+ (void) close (fd);
+ }
+ }
+ errno = saved_errno;
+
+ /*
+ * If O_CREAT was specified and the file didn't exist, it will be
+ * created by commonio_close(). We have no entries to read yet. --marekm
+ */
+ if (NULL == db->fp) {
+ if (((flags & O_CREAT) != 0) && (ENOENT == errno)) {
+ db->isopen = true;
+ return 1;
+ }
+ return 0;
+ }
+
+ /* Do not inherit fd in spawned processes (e.g. nscd) */
+ fcntl (fileno (db->fp), F_SETFD, FD_CLOEXEC);
+
+ buflen = BUFLEN;
+ buf = (char *) malloc (buflen);
+ if (NULL == buf) {
+ goto cleanup_ENOMEM;
+ }
+
+ while (db->ops->fgets (buf, (int) buflen, db->fp) == buf) {
+ while ( ((cp = strrchr (buf, '\n')) == NULL)
+ && (feof (db->fp) == 0)) {
+ size_t len;
+
+ buflen += BUFLEN;
+ cp = (char *) realloc (buf, buflen);
+ if (NULL == cp) {
+ goto cleanup_buf;
+ }
+ buf = cp;
+ len = strlen (buf);
+ if (db->ops->fgets (buf + len,
+ (int) (buflen - len),
+ db->fp) == NULL) {
+ goto cleanup_buf;
+ }
+ }
+ cp = strrchr (buf, '\n');
+ if (NULL != cp) {
+ *cp = '\0';
+ }
+
+ line = strdup (buf);
+ if (NULL == line) {
+ goto cleanup_buf;
+ }
+
+ if (name_is_nis (line)) {
+ eptr = NULL;
+ } else {
+ eptr = db->ops->parse (line);
+ if (NULL != eptr) {
+ eptr = db->ops->dup (eptr);
+ if (NULL == eptr) {
+ goto cleanup_line;
+ }
+ }
+ }
+
+ p = (struct commonio_entry *) malloc (sizeof *p);
+ if (NULL == p) {
+ goto cleanup_entry;
+ }
+
+ p->eptr = eptr;
+ p->line = line;
+ p->changed = false;
+
+ add_one_entry (db, p);
+ }
+
+ free (buf);
+
+ if (ferror (db->fp) != 0) {
+ goto cleanup_errno;
+ }
+
+ if ((NULL != db->ops->open_hook) && (db->ops->open_hook () == 0)) {
+ goto cleanup_errno;
+ }
+
+ db->isopen = true;
+ return 1;
+
+ cleanup_entry:
+ if (NULL != eptr) {
+ db->ops->free (eptr);
+ }
+ cleanup_line:
+ free (line);
+ cleanup_buf:
+ free (buf);
+ cleanup_ENOMEM:
+ errno = ENOMEM;
+ cleanup_errno:
+ saved_errno = errno;
+ free_linked_list (db);
+ fclose (db->fp);
+ db->fp = NULL;
+ errno = saved_errno;
+ return 0;
+}
+
+/*
+ * Sort given db according to cmp function (usually compares uids)
+ */
+int
+commonio_sort (struct commonio_db *db, int (*cmp) (const void *, const void *))
+{
+ struct commonio_entry **entries, *ptr;
+ size_t n = 0, i;
+#if KEEP_NIS_AT_END
+ struct commonio_entry *nis = NULL;
+#endif
+
+ for (ptr = db->head;
+ (NULL != ptr)
+#if KEEP_NIS_AT_END
+ && ((NULL == ptr->line)
+ || (('+' != ptr->line[0])
+ && ('-' != ptr->line[0])))
+#endif
+ ;
+ ptr = ptr->next) {
+ n++;
+ }
+#if KEEP_NIS_AT_END
+ if (NULL != ptr) {
+ nis = ptr;
+ }
+#endif
+
+ if (n <= 1) {
+ return 0;
+ }
+
+ entries = malloc (n * sizeof (struct commonio_entry *));
+ if (entries == NULL) {
+ return -1;
+ }
+
+ n = 0;
+ for (ptr = db->head;
+#if KEEP_NIS_AT_END
+ nis != ptr;
+#else
+ NULL != ptr;
+#endif
+/*@ -nullderef @*/
+ ptr = ptr->next
+/*@ +nullderef @*/
+ ) {
+ entries[n] = ptr;
+ n++;
+ }
+ qsort (entries, n, sizeof (struct commonio_entry *), cmp);
+
+ /* Take care of the head and tail separately */
+ db->head = entries[0];
+ n--;
+#if KEEP_NIS_AT_END
+ if (NULL == nis)
+#endif
+ {
+ db->tail = entries[n];
+ }
+ db->head->prev = NULL;
+ db->head->next = entries[1];
+ entries[n]->prev = entries[n - 1];
+#if KEEP_NIS_AT_END
+ entries[n]->next = nis;
+#else
+ entries[n]->next = NULL;
+#endif
+
+ /* Now other elements have prev and next entries */
+ for (i = 1; i < n; i++) {
+ entries[i]->prev = entries[i - 1];
+ entries[i]->next = entries[i + 1];
+ }
+
+ free (entries);
+ db->changed = true;
+
+ return 0;
+}
+
+/*
+ * Sort entries in db according to order in another.
+ */
+int commonio_sort_wrt (struct commonio_db *shadow,
+ const struct commonio_db *passwd)
+{
+ struct commonio_entry *head = NULL, *pw_ptr, *spw_ptr;
+ const char *name;
+
+ if ((NULL == shadow) || (NULL == shadow->head)) {
+ return 0;
+ }
+
+ for (pw_ptr = passwd->head; NULL != pw_ptr; pw_ptr = pw_ptr->next) {
+ if (NULL == pw_ptr->eptr) {
+ continue;
+ }
+ name = passwd->ops->getname (pw_ptr->eptr);
+ for (spw_ptr = shadow->head;
+ NULL != spw_ptr;
+ spw_ptr = spw_ptr->next) {
+ if (NULL == spw_ptr->eptr) {
+ continue;
+ }
+ if (strcmp (name, shadow->ops->getname (spw_ptr->eptr))
+ == 0) {
+ break;
+ }
+ }
+ if (NULL == spw_ptr) {
+ continue;
+ }
+ commonio_del_entry (shadow, spw_ptr);
+ spw_ptr->next = head;
+ head = spw_ptr;
+ }
+
+ for (spw_ptr = head; NULL != spw_ptr; spw_ptr = head) {
+ head = head->next;
+
+ if (NULL != shadow->head) {
+ shadow->head->prev = spw_ptr;
+ }
+ spw_ptr->next = shadow->head;
+ shadow->head = spw_ptr;
+ }
+
+ shadow->head->prev = NULL;
+ shadow->changed = true;
+
+ return 0;
+}
+
+/*
+ * write_all - Write the database to its file.
+ *
+ * It returns 0 if all the entries could be written correctly.
+ */
+static int write_all (const struct commonio_db *db)
+ /*@requires notnull db->fp@*/
+{
+ const struct commonio_entry *p;
+ void *eptr;
+
+ for (p = db->head; NULL != p; p = p->next) {
+ if (p->changed) {
+ eptr = p->eptr;
+ assert (NULL != eptr);
+ if (db->ops->put (eptr, db->fp) != 0) {
+ return -1;
+ }
+ } else if (NULL != p->line) {
+ if (db->ops->fputs (p->line, db->fp) == EOF) {
+ return -1;
+ }
+ if (putc ('\n', db->fp) == EOF) {
+ return -1;
+ }
+ }
+ }
+ return 0;
+}
+
+
+int commonio_close (struct commonio_db *db)
+ /*@requires notnull db->fp@*/
+{
+ char buf[1024];
+ int errors = 0;
+ struct stat sb;
+
+ if (!db->isopen) {
+ errno = EINVAL;
+ return 0;
+ }
+ db->isopen = false;
+
+ if (!db->changed || db->readonly) {
+ (void) fclose (db->fp);
+ db->fp = NULL;
+ goto success;
+ }
+
+ if ((NULL != db->ops->close_hook) && (db->ops->close_hook () == 0)) {
+ goto fail;
+ }
+
+ memzero (&sb, sizeof sb);
+ if (NULL != db->fp) {
+ if (fstat (fileno (db->fp), &sb) != 0) {
+ (void) fclose (db->fp);
+ db->fp = NULL;
+ goto fail;
+ }
+
+ /*
+ * Create backup file.
+ */
+ snprintf (buf, sizeof buf, "%s-", db->filename);
+
+#ifdef WITH_SELINUX
+ if (set_selinux_file_context (buf) != 0) {
+ errors++;
+ }
+#endif
+ if (create_backup (buf, db->fp) != 0) {
+ errors++;
+ }
+
+ if (fclose (db->fp) != 0) {
+ errors++;
+ }
+
+#ifdef WITH_SELINUX
+ if (reset_selinux_file_context () != 0) {
+ errors++;
+ }
+#endif
+ if (errors != 0) {
+ db->fp = NULL;
+ goto fail;
+ }
+ } else {
+ /*
+ * Default permissions for new [g]shadow files.
+ */
+ sb.st_mode = db->st_mode;
+ sb.st_uid = db->st_uid;
+ sb.st_gid = db->st_gid;
+ }
+
+ snprintf (buf, sizeof buf, "%s+", db->filename);
+
+#ifdef WITH_SELINUX
+ if (set_selinux_file_context (buf) != 0) {
+ errors++;
+ }
+#endif
+
+ db->fp = fopen_set_perms (buf, "w", &sb);
+ if (NULL == db->fp) {
+ goto fail;
+ }
+
+ if (write_all (db) != 0) {
+ errors++;
+ }
+
+ if (fflush (db->fp) != 0) {
+ errors++;
+ }
+#ifdef HAVE_FSYNC
+ if (fsync (fileno (db->fp)) != 0) {
+ errors++;
+ }
+#else /* !HAVE_FSYNC */
+ sync ();
+#endif /* !HAVE_FSYNC */
+ if (fclose (db->fp) != 0) {
+ errors++;
+ }
+
+ db->fp = NULL;
+
+ if (errors != 0) {
+ unlink (buf);
+ goto fail;
+ }
+
+ if (lrename (buf, db->filename) != 0) {
+ goto fail;
+ }
+
+#ifdef WITH_SELINUX
+ if (reset_selinux_file_context () != 0) {
+ goto fail;
+ }
+#endif
+
+ nscd_need_reload = true;
+ goto success;
+ fail:
+ errors++;
+ success:
+
+ free_linked_list (db);
+ return errors == 0;
+}
+
+static /*@dependent@*/ /*@null@*/struct commonio_entry *next_entry_by_name (
+ struct commonio_db *db,
+ /*@null@*/struct commonio_entry *pos,
+ const char *name)
+{
+ struct commonio_entry *p;
+ void *ep;
+
+ if (NULL == pos) {
+ return NULL;
+ }
+
+ for (p = pos; NULL != p; p = p->next) {
+ ep = p->eptr;
+ if ( (NULL != ep)
+ && (strcmp (db->ops->getname (ep), name) == 0)) {
+ break;
+ }
+ }
+ return p;
+}
+
+static /*@dependent@*/ /*@null@*/struct commonio_entry *find_entry_by_name (
+ struct commonio_db *db,
+ const char *name)
+{
+ return next_entry_by_name (db, db->head, name);
+}
+
+
+int commonio_update (struct commonio_db *db, const void *eptr)
+{
+ struct commonio_entry *p;
+ void *nentry;
+
+ if (!db->isopen || db->readonly) {
+ errno = EINVAL;
+ return 0;
+ }
+ nentry = db->ops->dup (eptr);
+ if (NULL == nentry) {
+ errno = ENOMEM;
+ return 0;
+ }
+ p = find_entry_by_name (db, db->ops->getname (eptr));
+ if (NULL != p) {
+ if (next_entry_by_name (db, p->next, db->ops->getname (eptr)) != NULL) {
+ fprintf (stderr, _("Multiple entries named '%s' in %s. Please fix this with pwck or grpck.\n"), db->ops->getname (eptr), db->filename);
+ db->ops->free (nentry);
+ return 0;
+ }
+ db->ops->free (p->eptr);
+ p->eptr = nentry;
+ p->changed = true;
+ db->cursor = p;
+
+ db->changed = true;
+ return 1;
+ }
+ /* not found, new entry */
+ p = (struct commonio_entry *) malloc (sizeof *p);
+ if (NULL == p) {
+ db->ops->free (nentry);
+ errno = ENOMEM;
+ return 0;
+ }
+
+ p->eptr = nentry;
+ p->line = NULL;
+ p->changed = true;
+
+#if KEEP_NIS_AT_END
+ add_one_entry_nis (db, p);
+#else /* !KEEP_NIS_AT_END */
+ add_one_entry (db, p);
+#endif /* !KEEP_NIS_AT_END */
+
+ db->changed = true;
+ return 1;
+}
+
+#ifdef ENABLE_SUBIDS
+int commonio_append (struct commonio_db *db, const void *eptr)
+{
+ struct commonio_entry *p;
+ void *nentry;
+
+ if (!db->isopen || db->readonly) {
+ errno = EINVAL;
+ return 0;
+ }
+ nentry = db->ops->dup (eptr);
+ if (NULL == nentry) {
+ errno = ENOMEM;
+ return 0;
+ }
+ /* new entry */
+ p = (struct commonio_entry *) malloc (sizeof *p);
+ if (NULL == p) {
+ db->ops->free (nentry);
+ errno = ENOMEM;
+ return 0;
+ }
+
+ p->eptr = nentry;
+ p->line = NULL;
+ p->changed = true;
+ add_one_entry (db, p);
+
+ db->changed = true;
+ return 1;
+}
+#endif /* ENABLE_SUBIDS */
+
+void commonio_del_entry (struct commonio_db *db, const struct commonio_entry *p)
+{
+ if (p == db->cursor) {
+ db->cursor = p->next;
+ }
+
+ if (NULL != p->prev) {
+ p->prev->next = p->next;
+ } else {
+ db->head = p->next;
+ }
+
+ if (NULL != p->next) {
+ p->next->prev = p->prev;
+ } else {
+ db->tail = p->prev;
+ }
+
+ db->changed = true;
+}
+
+/*
+ * commonio_remove - Remove the entry of the given name from the database.
+ */
+int commonio_remove (struct commonio_db *db, const char *name)
+{
+ struct commonio_entry *p;
+
+ if (!db->isopen || db->readonly) {
+ errno = EINVAL;
+ return 0;
+ }
+ p = find_entry_by_name (db, name);
+ if (NULL == p) {
+ errno = ENOENT;
+ return 0;
+ }
+ if (next_entry_by_name (db, p->next, name) != NULL) {
+ fprintf (stderr, _("Multiple entries named '%s' in %s. Please fix this with pwck or grpck.\n"), name, db->filename);
+ return 0;
+ }
+
+ commonio_del_entry (db, p);
+
+ if (NULL != p->line) {
+ free (p->line);
+ }
+
+ if (NULL != p->eptr) {
+ db->ops->free (p->eptr);
+ }
+
+ return 1;
+}
+
+/*
+ * commonio_locate - Find the first entry with the specified name in
+ * the database.
+ *
+ * If found, it returns the entry and set the cursor of the database to
+ * that entry.
+ *
+ * Otherwise, it returns NULL.
+ */
+/*@observer@*/ /*@null@*/const void *commonio_locate (struct commonio_db *db, const char *name)
+{
+ struct commonio_entry *p;
+
+ if (!db->isopen) {
+ errno = EINVAL;
+ return NULL;
+ }
+ p = find_entry_by_name (db, name);
+ if (NULL == p) {
+ errno = ENOENT;
+ return NULL;
+ }
+ db->cursor = p;
+ return p->eptr;
+}
+
+/*
+ * commonio_rewind - Restore the database cursor to the first entry.
+ *
+ * It returns 0 on error, 1 on success.
+ */
+int commonio_rewind (struct commonio_db *db)
+{
+ if (!db->isopen) {
+ errno = EINVAL;
+ return 0;
+ }
+ db->cursor = NULL;
+ return 1;
+}
+
+/*
+ * commonio_next - Return the next entry of the specified database
+ *
+ * It returns the next entry, or NULL if no other entries could be found.
+ */
+/*@observer@*/ /*@null@*/const void *commonio_next (struct commonio_db *db)
+{
+ void *eptr;
+
+ if (!db->isopen) {
+ errno = EINVAL;
+ return 0;
+ }
+ if (NULL == db->cursor) {
+ db->cursor = db->head;
+ } else {
+ db->cursor = db->cursor->next;
+ }
+
+ while (NULL != db->cursor) {
+ eptr = db->cursor->eptr;
+ if (NULL != eptr) {
+ return eptr;
+ }
+
+ db->cursor = db->cursor->next;
+ }
+ return NULL;
+}
+
diff --git a/lib/commonio.h b/lib/commonio.h
new file mode 100644
index 0000000..40e5708
--- /dev/null
+++ b/lib/commonio.h
@@ -0,0 +1,170 @@
+/*
+ * Copyright (c) 1990 - 1994, Julianne Frances Haugh
+ * Copyright (c) 1996 - 2000, Marek Michałkiewicz
+ * Copyright (c) 2001 - 2005, Tomasz Kłoczko
+ * Copyright (c) 2007 - 2010, Nicolas François
+ * 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. The name of the copyright holders or contributors may not be used to
+ * endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 THE COPYRIGHT
+ * HOLDERS 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.
+ */
+
+/* $Id$ */
+#ifndef COMMONIO_H
+#define COMMONIO_H
+
+#ifdef WITH_SELINUX
+#include <selinux/selinux.h>
+#endif
+
+#include "defines.h" /* bool */
+
+/*
+ * Linked list entry.
+ */
+struct commonio_entry {
+ /*@null@*/char *line;
+ /*@null@*/void *eptr; /* struct passwd, struct spwd, ... */
+ /*@dependent@*/ /*@null@*/struct commonio_entry *prev;
+ /*@owned@*/ /*@null@*/struct commonio_entry *next;
+ bool changed:1;
+};
+
+/*
+ * Operations depending on database type: passwd, group, shadow etc.
+ */
+struct commonio_ops {
+ /*
+ * Make a copy of the object (for example, struct passwd)
+ * and all strings pointed by it, in malloced memory.
+ */
+ /*@null@*/ /*@only@*/void *(*dup) (const void *);
+
+ /*
+ * free() the object including any strings pointed by it.
+ */
+ void (*free) (/*@out@*/ /*@only@*/void *);
+
+ /*
+ * Return the name of the object (for example, pw_name
+ * for struct passwd).
+ */
+ const char *(*getname) (const void *);
+
+ /*
+ * Parse a string, return object (in static area -
+ * should be copied using the dup operation above).
+ */
+ void *(*parse) (const char *);
+
+ /*
+ * Write the object to the file (this calls putpwent()
+ * for struct passwd, for example).
+ */
+ int (*put) (const void *, FILE *);
+
+ /*
+ * fgets and fputs (can be replaced by versions that
+ * understand line continuation conventions).
+ */
+ /*@null@*/char *(*fgets) (/*@returned@*/ /*@out@*/char *s, int n, FILE *stream);
+ int (*fputs) (const char *, FILE *);
+
+ /*
+ * open_hook and close_hook.
+ * If non NULL, these functions will be called after the database
+ * is open or before it is closed.
+ * They return 0 on failure and 1 on success.
+ */
+ /*@null@*/int (*open_hook) (void);
+ /*@null@*/int (*close_hook) (void);
+};
+
+/*
+ * Database structure.
+ */
+struct commonio_db {
+ /*
+ * Name of the data file.
+ */
+ char filename[1024];
+
+ /*
+ * Operations from above.
+ */
+ /*@observer@*/const struct commonio_ops *ops;
+
+ /*
+ * Currently open file stream.
+ */
+ /*@dependent@*/ /*@null@*/FILE *fp;
+
+#ifdef WITH_SELINUX
+ /*@null@*/security_context_t scontext;
+#endif
+ /*
+ * Default permissions and owner for newly created data file.
+ */
+ mode_t st_mode;
+ uid_t st_uid;
+ gid_t st_gid;
+ /*
+ * Head, tail, current position in linked list.
+ */
+ /*@owned@*/ /*@null@*/struct commonio_entry *head;
+ /*@dependent@*/ /*@null@*/struct commonio_entry *tail;
+ /*@dependent@*/ /*@null@*/struct commonio_entry *cursor;
+
+ /*
+ * Various flags.
+ */
+ bool changed:1;
+ bool isopen:1;
+ bool locked:1;
+ bool readonly:1;
+};
+
+extern int commonio_setname (struct commonio_db *, const char *);
+extern bool commonio_present (const struct commonio_db *db);
+extern int commonio_lock (struct commonio_db *);
+extern int commonio_lock_nowait (struct commonio_db *, bool log);
+extern int commonio_open (struct commonio_db *, int);
+extern /*@observer@*/ /*@null@*/const void *commonio_locate (struct commonio_db *, const char *);
+extern int commonio_update (struct commonio_db *, const void *);
+#ifdef ENABLE_SUBIDS
+extern int commonio_append (struct commonio_db *, const void *);
+#endif /* ENABLE_SUBIDS */
+extern int commonio_remove (struct commonio_db *, const char *);
+extern int commonio_rewind (struct commonio_db *);
+extern /*@observer@*/ /*@null@*/const void *commonio_next (struct commonio_db *);
+extern int commonio_close (struct commonio_db *);
+extern int commonio_unlock (struct commonio_db *);
+extern void commonio_del_entry (struct commonio_db *,
+ const struct commonio_entry *);
+extern int commonio_sort_wrt (struct commonio_db *shadow,
+ const struct commonio_db *passwd);
+extern int commonio_sort (struct commonio_db *db,
+ int (*cmp) (const void *, const void *));
+
+#endif
diff --git a/lib/defines.h b/lib/defines.h
new file mode 100644
index 0000000..62bd73e
--- /dev/null
+++ b/lib/defines.h
@@ -0,0 +1,385 @@
+/* $Id$ */
+/* some useful defines */
+
+#ifndef _DEFINES_H_
+#define _DEFINES_H_
+
+#if HAVE_STDBOOL_H
+# include <stdbool.h>
+#else
+# if ! HAVE__BOOL
+# ifdef __cplusplus
+typedef bool _Bool;
+# else
+typedef unsigned char _Bool;
+# endif
+# endif
+# define bool _Bool
+# define false (0)
+# define true (1)
+# define __bool_true_false_are_defined 1
+#endif
+
+#define ISDIGIT_LOCALE(c) (IN_CTYPE_DOMAIN (c) && isdigit (c))
+
+/* Take care of NLS matters. */
+#ifdef S_SPLINT_S
+extern char *setlocale(int categorie, const char *locale);
+# define LC_ALL (6)
+extern char * bindtextdomain (const char * domainname, const char * dirname);
+extern char * textdomain (const char * domainname);
+# define _(Text) Text
+# define ngettext(Msgid1, Msgid2, N) \
+ ((N) == 1 ? (const char *) (Msgid1) : (const char *) (Msgid2))
+#else
+#ifdef HAVE_LOCALE_H
+# include <locale.h>
+#else
+# undef setlocale
+# define setlocale(category, locale) (NULL)
+# ifndef LC_ALL
+# define LC_ALL 6
+# endif
+#endif
+
+#define gettext_noop(String) (String)
+/* #define gettext_def(String) "#define String" */
+
+#ifdef ENABLE_NLS
+# include <libintl.h>
+# define _(Text) gettext (Text)
+#else
+# undef bindtextdomain
+# define bindtextdomain(Domain, Directory) (NULL)
+# undef textdomain
+# define textdomain(Domain) (NULL)
+# define _(Text) Text
+# define ngettext(Msgid1, Msgid2, N) \
+ ((N) == 1 ? (const char *) (Msgid1) : (const char *) (Msgid2))
+#endif
+#endif
+
+#if STDC_HEADERS
+# include <stdlib.h>
+# include <string.h>
+#else /* not STDC_HEADERS */
+# ifndef HAVE_STRCHR
+# define strchr index
+# define strrchr rindex
+# endif
+char *strchr (), *strrchr (), *strtok ();
+
+# ifndef HAVE_MEMCPY
+# define memcpy(d, s, n) bcopy((s), (d), (n))
+# endif
+#endif /* not STDC_HEADERS */
+
+#if HAVE_ERRNO_H
+# include <errno.h>
+#endif
+
+#include <sys/stat.h>
+#include <sys/types.h>
+#if HAVE_SYS_WAIT_H
+# include <sys/wait.h>
+#endif
+#ifndef WEXITSTATUS
+# define WEXITSTATUS(stat_val) ((unsigned)(stat_val) >> 8)
+#endif
+#ifndef WIFEXITED
+# define WIFEXITED(stat_val) (((stat_val) & 255) == 0)
+#endif
+
+#if HAVE_UNISTD_H
+# include <unistd.h>
+#endif
+
+#if TIME_WITH_SYS_TIME
+# include <sys/time.h>
+# include <time.h>
+#else /* not TIME_WITH_SYS_TIME */
+# if HAVE_SYS_TIME_H
+# include <sys/time.h>
+# else
+# include <time.h>
+# endif
+#endif /* not TIME_WITH_SYS_TIME */
+
+#ifdef HAVE_MEMSET
+# define memzero(ptr, size) memset((void *)(ptr), 0, (size))
+#else
+# define memzero(ptr, size) bzero((char *)(ptr), (size))
+#endif
+#define strzero(s) memzero(s, strlen(s)) /* warning: evaluates twice */
+
+#ifdef HAVE_DIRENT_H /* DIR_SYSV */
+# include <dirent.h>
+# define DIRECT dirent
+#else
+# ifdef HAVE_SYS_NDIR_H /* DIR_XENIX */
+# include <sys/ndir.h>
+# endif
+# ifdef HAVE_SYS_DIR_H /* DIR_??? */
+# include <sys/dir.h>
+# endif
+# ifdef HAVE_NDIR_H /* DIR_BSD */
+# include <ndir.h>
+# endif
+# define DIRECT direct
+#endif
+
+/*
+ * Possible cases:
+ * - /usr/include/shadow.h exists and includes the shadow group stuff.
+ * - /usr/include/shadow.h exists, but we use our own gshadow.h.
+ */
+#include <shadow.h>
+#if defined(SHADOWGRP) && !defined(GSHADOW)
+#include "gshadow_.h"
+#endif
+
+#include <limits.h>
+
+#ifndef NGROUPS_MAX
+#ifdef NGROUPS
+#define NGROUPS_MAX NGROUPS
+#else
+#define NGROUPS_MAX 64
+#endif
+#endif
+
+#ifdef USE_SYSLOG
+#include <syslog.h>
+
+#ifndef LOG_WARN
+#define LOG_WARN LOG_WARNING
+#endif
+
+/* LOG_NOWAIT is deprecated */
+#ifndef LOG_NOWAIT
+#define LOG_NOWAIT 0
+#endif
+
+/* LOG_AUTH is deprecated, use LOG_AUTHPRIV instead */
+#ifndef LOG_AUTHPRIV
+#define LOG_AUTHPRIV LOG_AUTH
+#endif
+
+/* cleaner than lots of #ifdefs everywhere - use this as follows:
+ SYSLOG((LOG_CRIT, "user %s cracked root", user)); */
+#ifdef ENABLE_NLS
+/* Temporarily set LC_TIME to "C" to avoid strange dates in syslog.
+ This is a workaround for a more general syslog(d) design problem -
+ syslogd should log the current system time for each event, and not
+ trust the formatted time received from the unix domain (or worse,
+ UDP) socket. -MM */
+/* Avoid translated PAM error messages: Set LC_ALL to "C".
+ * --Nekral */
+#define SYSLOG(x) \
+ do { \
+ char *old_locale = setlocale (LC_ALL, NULL); \
+ char *saved_locale = NULL; \
+ if (NULL != old_locale) { \
+ saved_locale = strdup (old_locale); \
+ } \
+ if (NULL != saved_locale) { \
+ (void) setlocale (LC_ALL, "C"); \
+ } \
+ syslog x ; \
+ if (NULL != saved_locale) { \
+ (void) setlocale (LC_ALL, saved_locale); \
+ free (saved_locale); \
+ } \
+ } while (false)
+#else /* !ENABLE_NLS */
+#define SYSLOG(x) syslog x
+#endif /* !ENABLE_NLS */
+
+#else /* !USE_SYSLOG */
+
+#define SYSLOG(x) /* empty */
+#define openlog(a,b,c) /* empty */
+#define closelog() /* empty */
+
+#endif /* !USE_SYSLOG */
+
+/* The default syslog settings can now be changed here,
+ in just one place. */
+
+#ifndef SYSLOG_OPTIONS
+/* #define SYSLOG_OPTIONS (LOG_PID | LOG_CONS | LOG_NOWAIT) */
+#define SYSLOG_OPTIONS (LOG_PID)
+#endif
+
+#ifndef SYSLOG_FACILITY
+#define SYSLOG_FACILITY LOG_AUTHPRIV
+#endif
+
+#define OPENLOG(progname) openlog(progname, SYSLOG_OPTIONS, SYSLOG_FACILITY)
+
+#ifndef F_OK
+# define F_OK 0
+# define X_OK 1
+# define W_OK 2
+# define R_OK 4
+#endif
+
+#ifndef SEEK_SET
+# define SEEK_SET 0
+# define SEEK_CUR 1
+# define SEEK_END 2
+#endif
+
+#ifdef STAT_MACROS_BROKEN
+# define S_ISDIR(x) ((x) & S_IFMT) == S_IFDIR)
+# define S_ISREG(x) ((x) & S_IFMT) == S_IFREG)
+# ifdef S_IFLNK
+# define S_ISLNK(x) ((x) & S_IFMT) == S_IFLNK)
+# endif
+#endif
+
+#ifndef S_ISLNK
+#define S_ISLNK(x) (0)
+#endif
+
+#if HAVE_LCHOWN
+#define LCHOWN lchown
+#else
+#define LCHOWN chown
+#endif
+
+#if HAVE_LSTAT
+#define LSTAT lstat
+#else
+#define LSTAT stat
+#endif
+
+#if HAVE_TERMIOS_H
+# include <termios.h>
+# define STTY(fd, termio) tcsetattr(fd, TCSANOW, termio)
+# define GTTY(fd, termio) tcgetattr(fd, termio)
+# define TERMIO struct termios
+# define USE_TERMIOS
+#else /* assumed HAVE_TERMIO_H */
+# include <sys/ioctl.h>
+# include <termio.h>
+# define STTY(fd, termio) ioctl(fd, TCSETA, termio)
+# define GTTY(fd, termio) ioctl(fd, TCGETA, termio)
+# define TEMRIO struct termio
+# define USE_TERMIO
+#endif
+
+/*
+ * Password aging constants
+ *
+ * DAY - seconds / day
+ * WEEK - seconds / week
+ * SCALE - seconds / aging unit
+ */
+
+/* Solaris defines this in shadow.h */
+#ifndef DAY
+#define DAY (24L*3600L)
+#endif
+
+#define WEEK (7*DAY)
+
+#ifdef ITI_AGING
+#define SCALE 1
+#else
+#define SCALE DAY
+#endif
+
+/* Copy string pointed by B to array A with size checking. It was originally
+ in lmain.c but is _very_ useful elsewhere. Some setuid root programs with
+ very sloppy coding used to assume that BUFSIZ will always be enough... */
+
+ /* danger - side effects */
+#define STRFCPY(A,B) \
+ (strncpy((A), (B), sizeof(A) - 1), (A)[sizeof(A) - 1] = '\0')
+
+#ifndef PASSWD_FILE
+#define PASSWD_FILE "/etc/passwd"
+#endif
+
+#ifndef GROUP_FILE
+#define GROUP_FILE "/etc/group"
+#endif
+
+#ifndef SHADOW_FILE
+#define SHADOW_FILE "/etc/shadow"
+#endif
+
+#ifdef SHADOWGRP
+#ifndef SGROUP_FILE
+#define SGROUP_FILE "/etc/gshadow"
+#endif
+#endif
+
+#ifndef NULL
+#define NULL ((void *) 0)
+#endif
+
+#ifdef sun /* hacks for compiling on SunOS */
+# ifndef SOLARIS
+extern int fputs ();
+extern char *strdup ();
+extern char *strerror ();
+# endif
+#endif
+
+/*
+ * string to use for the pw_passwd field in /etc/passwd when using
+ * shadow passwords - most systems use "x" but there are a few
+ * exceptions, so it can be changed here if necessary. --marekm
+ */
+#ifndef SHADOW_PASSWD_STRING
+#define SHADOW_PASSWD_STRING "x"
+#endif
+
+#define SHADOW_SP_FLAG_UNSET ((unsigned long int)-1)
+
+#ifdef WITH_AUDIT
+#ifdef __u8 /* in case we use pam < 0.80 */
+#undef __u8
+#endif
+#ifdef __u32
+#undef __u32
+#endif
+
+#include <libaudit.h>
+#endif
+
+/* To be used for verified unused parameters */
+#if defined(__GNUC__) && !defined(__STRICT_ANSI__)
+# define unused __attribute__((unused))
+#else
+# define unused
+#endif
+
+/* ! Arguments evaluated twice ! */
+#ifndef MIN
+#define MIN(a,b) (((a) < (b)) ? (a) : (b))
+#endif
+#ifndef MAX
+#define MAX(x,y) (((x) > (y)) ? (x) : (y))
+#endif
+
+/* Maximum length of usernames */
+#ifdef HAVE_UTMPX_H
+# include <utmpx.h>
+# define USER_NAME_MAX_LENGTH (sizeof (((struct utmpx *)NULL)->ut_user))
+#else
+# include <utmp.h>
+# ifdef HAVE_STRUCT_UTMP_UT_USER
+# define USER_NAME_MAX_LENGTH (sizeof (((struct utmp *)NULL)->ut_user))
+# else
+# ifdef HAVE_STRUCT_UTMP_UT_NAME
+# define USER_NAME_MAX_LENGTH (sizeof (((struct utmp *)NULL)->ut_name))
+# else
+# define USER_NAME_MAX_LENGTH 32
+# endif
+# endif
+#endif
+
+#endif /* _DEFINES_H_ */
diff --git a/lib/encrypt.c b/lib/encrypt.c
new file mode 100644
index 0000000..53c99c9
--- /dev/null
+++ b/lib/encrypt.c
@@ -0,0 +1,95 @@
+/*
+ * Copyright (c) 1990 - 1993, Julianne Frances Haugh
+ * Copyright (c) 1996 - 2000, Marek Michałkiewicz
+ * Copyright (c) 2005 , Tomasz Kłoczko
+ * Copyright (c) 2007 - 2010, Nicolas François
+ * 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. The name of the copyright holders or contributors may not be used to
+ * endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 THE COPYRIGHT
+ * HOLDERS 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.
+ */
+
+#include <config.h>
+
+#ident "$Id$"
+
+#include <unistd.h>
+#include <stdio.h>
+
+#include "prototypes.h"
+#include "defines.h"
+
+/*@exposed@*//*@null@*/char *pw_encrypt (const char *clear, const char *salt)
+{
+ static char cipher[128];
+ char *cp;
+
+ cp = crypt (clear, salt);
+ if (NULL == cp) {
+ /*
+ * Single Unix Spec: crypt() may return a null pointer,
+ * and set errno to indicate an error. In this case return
+ * the NULL so the caller can handle appropriately.
+ */
+ return NULL;
+ }
+
+ /* Some crypt() do not return NULL if the algorithm is not
+ * supported, and return a DES encrypted password. */
+ if ((NULL != salt) && (salt[0] == '$') && (strlen (cp) <= 13))
+ {
+ /*@observer@*/const char *method;
+ switch (salt[1])
+ {
+ case '1':
+ method = "MD5";
+ break;
+ case '5':
+ method = "SHA256";
+ break;
+ case '6':
+ method = "SHA512";
+ break;
+ default:
+ {
+ static char nummethod[4] = "$x$";
+ nummethod[1] = salt[1];
+ method = &nummethod[0];
+ }
+ }
+ (void) fprintf (stderr,
+ _("crypt method not supported by libcrypt? (%s)\n"),
+ method);
+ exit (EXIT_FAILURE);
+ }
+
+ if (strlen (cp) != 13) {
+ return cp; /* nonstandard crypt() in libc, better bail out */
+ }
+
+ strcpy (cipher, cp);
+
+ return cipher;
+}
+
diff --git a/lib/exitcodes.h b/lib/exitcodes.h
new file mode 100644
index 0000000..96b2340
--- /dev/null
+++ b/lib/exitcodes.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2005 - 2006, Tomasz Kłoczko
+ * 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. The name of the copyright holders or contributors may not be used to
+ * endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 THE COPYRIGHT
+ * HOLDERS 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.
+ */
+
+/* $Id$ */
+
+/*
+ * Exit codes used by shadow programs
+ */
+#define E_SUCCESS EXIT_SUCCESS /* success */
+/*
+ * FIXME: other values should differ from EXIT_FAILURE (and EXIT_SUCCESS).
+ *
+ * FIXME: reserve EXIT_FAILURE for internal failures.
+ */
+#define E_NOPERM 1 /* permission denied */
+#define E_USAGE 2 /* invalid command syntax */
+#define E_BAD_ARG 3 /* invalid argument to option */
+#define E_PASSWD_NOTFOUND 14 /* not found password file */
+#define E_SHADOW_NOTFOUND 15 /* not found shadow password file */
+#define E_GROUP_NOTFOUND 16 /* not found group file */
+#define E_GSHADOW_NOTFOUND 17 /* not found shadow group file */
+#define E_CMD_NOEXEC 126 /* can't run command/shell */
+#define E_CMD_NOTFOUND 127 /* can't find command/shell to run */
diff --git a/lib/faillog.h b/lib/faillog.h
new file mode 100644
index 0000000..a0a95b3
--- /dev/null
+++ b/lib/faillog.h
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 1989 - 1994, Julianne Frances Haugh
+ * Copyright (c) 1996 - 1997, Marek Michałkiewicz
+ * Copyright (c) 2005 , Tomasz Kłoczko
+ * 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. The name of the copyright holders or contributors may not be used to
+ * endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 THE COPYRIGHT
+ * HOLDERS 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.
+ */
+
+/*
+ * faillog.h - login failure logging file format
+ *
+ * $Id$
+ *
+ * The login failure file is maintained by login(1) and faillog(8)
+ * Each record in the file represents a separate UID and the file
+ * is indexed in that fashion.
+ */
+
+#ifndef _FAILLOG_H
+#define _FAILLOG_H
+
+struct faillog {
+ short fail_cnt; /* failures since last success */
+ short fail_max; /* failures before turning account off */
+ char fail_line[12]; /* last failure occured here */
+ time_t fail_time; /* last failure occured then */
+ /*
+ * If nonzero, the account will be re-enabled if there are no
+ * failures for fail_locktime seconds since last failure.
+ */
+ long fail_locktime;
+};
+
+#endif
diff --git a/lib/fields.c b/lib/fields.c
new file mode 100644
index 0000000..649fae1
--- /dev/null
+++ b/lib/fields.c
@@ -0,0 +1,130 @@
+/*
+ * Copyright (c) 1990 , Julianne Frances Haugh
+ * Copyright (c) 1996 - 1997, Marek Michałkiewicz
+ * Copyright (c) 2003 - 2005, Tomasz Kłoczko
+ * Copyright (c) 2007 , Nicolas François
+ * 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. The name of the copyright holders or contributors may not be used to
+ * endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 THE COPYRIGHT
+ * HOLDERS 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.
+ */
+
+#include <config.h>
+
+#ident "$Id$"
+
+#include <ctype.h>
+#include <string.h>
+#include <stdio.h>
+#include "prototypes.h"
+
+/*
+ * valid_field - insure that a field contains all legal characters
+ *
+ * The supplied field is scanned for non-printable and other illegal
+ * characters.
+ * + -1 is returned if an illegal character is present.
+ * + 1 is returned if no illegal characters are present, but the field
+ * contains a non-printable character.
+ * + 0 is returned otherwise.
+ */
+int valid_field (const char *field, const char *illegal)
+{
+ const char *cp;
+ int err = 0;
+
+ if (NULL == field) {
+ return -1;
+ }
+
+ /* For each character of field, search if it appears in the list
+ * of illegal characters. */
+ for (cp = field; '\0' != *cp; cp++) {
+ if (strchr (illegal, *cp) != NULL) {
+ err = -1;
+ break;
+ }
+ }
+
+ if (0 == err) {
+ /* Search if there are some non-printable characters */
+ for (cp = field; '\0' != *cp; cp++) {
+ if (!isprint (*cp)) {
+ err = 1;
+ break;
+ }
+ }
+ }
+
+ return err;
+}
+
+/*
+ * change_field - change a single field if a new value is given.
+ *
+ * prompt the user with the name of the field being changed and the
+ * current value.
+ */
+void change_field (char *buf, size_t maxsize, const char *prompt)
+{
+ char newf[200];
+ char *cp;
+
+ if (maxsize > sizeof (newf)) {
+ maxsize = sizeof (newf);
+ }
+
+ printf ("\t%s [%s]: ", prompt, buf);
+ (void) fflush (stdout);
+ if (fgets (newf, (int) maxsize, stdin) != newf) {
+ return;
+ }
+
+ cp = strchr (newf, '\n');
+ if (NULL == cp) {
+ return;
+ }
+ *cp = '\0';
+
+ if ('\0' != newf[0]) {
+ /*
+ * Remove leading and trailing whitespace. This also
+ * makes it possible to change the field to empty, by
+ * entering a space. --marekm
+ */
+
+ while (--cp >= newf && isspace (*cp));
+ cp++;
+ *cp = '\0';
+
+ cp = newf;
+ while (('\0' != *cp) && isspace (*cp)) {
+ cp++;
+ }
+
+ strncpy (buf, cp, maxsize - 1);
+ buf[maxsize - 1] = '\0';
+ }
+}
+
diff --git a/lib/fputsx.c b/lib/fputsx.c
new file mode 100644
index 0000000..c42b40b
--- /dev/null
+++ b/lib/fputsx.c
@@ -0,0 +1,90 @@
+/*
+ * Copyright (c) 1990 - 1994, Julianne Frances Haugh
+ * Copyright (c) 1996 - 1999, Marek Michałkiewicz
+ * Copyright (c) 2005 , Tomasz Kłoczko
+ * Copyright (c) 2008 , Nicolas François
+ * 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. The name of the copyright holders or contributors may not be used to
+ * endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 THE COPYRIGHT
+ * HOLDERS 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.
+ */
+
+#include <config.h>
+
+#include <stdio.h>
+#include "defines.h"
+#include "prototypes.h"
+
+#ident "$Id$"
+
+
+/*@null@*/char *fgetsx (/*@returned@*/ /*@out@*/char *buf, int cnt, FILE * f)
+{
+ char *cp = buf;
+ char *ep;
+
+ while (cnt > 0) {
+ if (fgets (cp, cnt, f) != cp) {
+ if (cp == buf) {
+ return 0;
+ } else {
+ break;
+ }
+ }
+ ep = strrchr (cp, '\\');
+ if ((NULL != ep) && (*(ep + 1) == '\n')) {
+ cnt -= ep - cp;
+ if (cnt > 0) {
+ cp = ep;
+ *cp = '\0';
+ }
+ } else {
+ break;
+ }
+ }
+ return buf;
+}
+
+int fputsx (const char *s, FILE * stream)
+{
+ int i;
+
+ for (i = 0; '\0' != *s; i++, s++) {
+ if (putc (*s, stream) == EOF) {
+ return EOF;
+ }
+
+#if 0 /* The standard getgr*() can't handle that. --marekm */
+ if (i > (BUFSIZ / 2)) {
+ if (putc ('\\', stream) == EOF ||
+ putc ('\n', stream) == EOF)
+ return EOF;
+
+ i = 0;
+ }
+#endif
+ }
+ return 0;
+}
+
diff --git a/lib/get_gid.c b/lib/get_gid.c
new file mode 100644
index 0000000..6fb31ff
--- /dev/null
+++ b/lib/get_gid.c
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2009 , Nicolas François
+ * 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. The name of the copyright holders or contributors may not be used to
+ * endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 THE COPYRIGHT
+ * HOLDERS 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.
+ */
+
+#include <config.h>
+
+#ident "$Id$"
+
+#include "prototypes.h"
+#include "defines.h"
+
+int get_gid (const char *gidstr, gid_t *gid)
+{
+ long long int val;
+ char *endptr;
+
+ errno = 0;
+ val = strtoll (gidstr, &endptr, 10);
+ if ( ('\0' == *gidstr)
+ || ('\0' != *endptr)
+ || (ERANGE == errno)
+ || (/*@+longintegral@*/val != (gid_t)val)/*@=longintegral@*/) {
+ return 0;
+ }
+
+ *gid = (gid_t)val;
+ return 1;
+}
+
diff --git a/lib/get_pid.c b/lib/get_pid.c
new file mode 100644
index 0000000..e2b47ce
--- /dev/null
+++ b/lib/get_pid.c
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2009 , Nicolas François
+ * 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. The name of the copyright holders or contributors may not be used to
+ * endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 THE COPYRIGHT
+ * HOLDERS 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.
+ */
+
+#include <config.h>
+
+#ident "$Id$"
+
+#include "prototypes.h"
+#include "defines.h"
+
+int get_pid (const char *pidstr, pid_t *pid)
+{
+ long long int val;
+ char *endptr;
+
+ errno = 0;
+ val = strtoll (pidstr, &endptr, 10);
+ if ( ('\0' == *pidstr)
+ || ('\0' != *endptr)
+ || (ERANGE == errno)
+ || (/*@+longintegral@*/val != (pid_t)val)/*@=longintegral@*/) {
+ return 0;
+ }
+
+ *pid = (pid_t)val;
+ return 1;
+}
+
diff --git a/lib/get_uid.c b/lib/get_uid.c
new file mode 100644
index 0000000..ac0f2df
--- /dev/null
+++ b/lib/get_uid.c
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2009 , Nicolas François
+ * 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. The name of the copyright holders or contributors may not be used to
+ * endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 THE COPYRIGHT
+ * HOLDERS 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.
+ */
+
+#include <config.h>
+
+#ident "$Id$"
+
+#include "prototypes.h"
+#include "defines.h"
+
+int get_uid (const char *uidstr, uid_t *uid)
+{
+ long long int val;
+ char *endptr;
+
+ errno = 0;
+ val = strtoll (uidstr, &endptr, 10);
+ if ( ('\0' == *uidstr)
+ || ('\0' != *endptr)
+ || (ERANGE == errno)
+ || (/*@+longintegral@*/val != (uid_t)val)/*@=longintegral@*/) {
+ return 0;
+ }
+
+ *uid = (uid_t)val;
+ return 1;
+}
+
diff --git a/lib/getdef.c b/lib/getdef.c
new file mode 100644
index 0000000..a181cc2
--- /dev/null
+++ b/lib/getdef.c
@@ -0,0 +1,539 @@
+/*
+ * Copyright (c) 1991 - 1994, Julianne Frances Haugh
+ * Copyright (c) 1996 - 2000, Marek Michałkiewicz
+ * Copyright (c) 2002 - 2006, Tomasz Kłoczko
+ * Copyright (c) 2007 - 2008, Nicolas François
+ * 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. The name of the copyright holders or contributors may not be used to
+ * endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 THE COPYRIGHT
+ * HOLDERS 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.
+ */
+
+#include <config.h>
+
+#ident "$Id$"
+
+#include "prototypes.h"
+#include "defines.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <errno.h>
+#include "getdef.h"
+/*
+ * A configuration item definition.
+ */
+struct itemdef {
+ /*@null@*/const char *name; /* name of the item */
+ /*@null@*/char *value; /* value given, or NULL if no value */
+};
+
+#define PAMDEFS \
+ {"CHFN_AUTH", NULL}, \
+ {"CHSH_AUTH", NULL}, \
+ {"CRACKLIB_DICTPATH", NULL}, \
+ {"ENV_HZ", NULL}, \
+ {"ENVIRON_FILE", NULL}, \
+ {"ENV_TZ", NULL}, \
+ {"FAILLOG_ENAB", NULL}, \
+ {"FTMP_FILE", NULL}, \
+ {"ISSUE_FILE", NULL}, \
+ {"LASTLOG_ENAB", NULL}, \
+ {"LOGIN_STRING", NULL}, \
+ {"MAIL_CHECK_ENAB", NULL}, \
+ {"MOTD_FILE", NULL}, \
+ {"NOLOGINS_FILE", NULL}, \
+ {"OBSCURE_CHECKS_ENAB", NULL}, \
+ {"PASS_ALWAYS_WARN", NULL}, \
+ {"PASS_CHANGE_TRIES", NULL}, \
+ {"PASS_MAX_LEN", NULL}, \
+ {"PASS_MIN_LEN", NULL}, \
+ {"PORTTIME_CHECKS_ENAB", NULL}, \
+ {"QUOTAS_ENAB", NULL}, \
+ {"SU_WHEEL_ONLY", NULL}, \
+ {"ULIMIT", NULL},
+
+
+#define NUMDEFS (sizeof(def_table)/sizeof(def_table[0]))
+static struct itemdef def_table[] = {
+ {"CHFN_RESTRICT", NULL},
+ {"CONSOLE_GROUPS", NULL},
+ {"CONSOLE", NULL},
+ {"CREATE_HOME", NULL},
+ {"DEFAULT_HOME", NULL},
+ {"ENCRYPT_METHOD", NULL},
+ {"ENV_PATH", NULL},
+ {"ENV_SUPATH", NULL},
+ {"ERASECHAR", NULL},
+ {"FAIL_DELAY", NULL},
+ {"FAKE_SHELL", NULL},
+ {"GID_MAX", NULL},
+ {"GID_MIN", NULL},
+ {"HUSHLOGIN_FILE", NULL},
+ {"KILLCHAR", NULL},
+ {"LOGIN_RETRIES", NULL},
+ {"LOGIN_TIMEOUT", NULL},
+ {"LOG_OK_LOGINS", NULL},
+ {"LOG_UNKFAIL_ENAB", NULL},
+ {"MAIL_DIR", NULL},
+ {"MAIL_FILE", NULL},
+ {"MAX_MEMBERS_PER_GROUP", NULL},
+ {"MD5_CRYPT_ENAB", NULL},
+ {"PASS_MAX_DAYS", NULL},
+ {"PASS_MIN_DAYS", NULL},
+ {"PASS_WARN_AGE", NULL},
+#ifdef USE_SHA_CRYPT
+ {"SHA_CRYPT_MAX_ROUNDS", NULL},
+ {"SHA_CRYPT_MIN_ROUNDS", NULL},
+#endif
+ {"SUB_GID_COUNT", NULL},
+ {"SUB_GID_MAX", NULL},
+ {"SUB_GID_MIN", NULL},
+ {"SUB_UID_COUNT", NULL},
+ {"SUB_UID_MAX", NULL},
+ {"SUB_UID_MIN", NULL},
+ {"SULOG_FILE", NULL},
+ {"SU_NAME", NULL},
+ {"SYS_GID_MAX", NULL},
+ {"SYS_GID_MIN", NULL},
+ {"SYS_UID_MAX", NULL},
+ {"SYS_UID_MIN", NULL},
+ {"TTYGROUP", NULL},
+ {"TTYPERM", NULL},
+ {"TTYTYPE_FILE", NULL},
+ {"UID_MAX", NULL},
+ {"UID_MIN", NULL},
+ {"UMASK", NULL},
+ {"USERDEL_CMD", NULL},
+ {"USERGROUPS_ENAB", NULL},
+#ifndef USE_PAM
+ PAMDEFS
+#endif
+#ifdef USE_SYSLOG
+ {"SYSLOG_SG_ENAB", NULL},
+ {"SYSLOG_SU_ENAB", NULL},
+#endif
+#ifdef WITH_TCB
+ {"TCB_AUTH_GROUP", NULL},
+ {"TCB_SYMLINKS", NULL},
+ {"USE_TCB", NULL},
+#endif
+ {"FORCE_SHADOW", NULL},
+ {NULL, NULL}
+};
+
+#define NUMKNOWNDEFS (sizeof(knowndef_table)/sizeof(knowndef_table[0]))
+static struct itemdef knowndef_table[] = {
+#ifdef USE_PAM
+ PAMDEFS
+#endif
+ {NULL, NULL}
+};
+
+#ifndef LOGINDEFS
+#define LOGINDEFS "/etc/login.defs"
+#endif
+
+static char def_fname[] = LOGINDEFS; /* login config defs file */
+static bool def_loaded = false; /* are defs already loaded? */
+
+/* local function prototypes */
+static /*@observer@*/ /*@null@*/struct itemdef *def_find (const char *);
+static void def_load (void);
+
+
+/*
+ * getdef_str - get string value from table of definitions.
+ *
+ * Return point to static data for specified item, or NULL if item is not
+ * defined. First time invoked, will load definitions from the file.
+ */
+
+/*@observer@*/ /*@null@*/const char *getdef_str (const char *item)
+{
+ struct itemdef *d;
+
+ if (!def_loaded) {
+ def_load ();
+ }
+
+ d = def_find (item);
+ return ((NULL == d)? (const char *) NULL : d->value);
+}
+
+
+/*
+ * getdef_bool - get boolean value from table of definitions.
+ *
+ * Return TRUE if specified item is defined as "yes", else FALSE.
+ */
+
+bool getdef_bool (const char *item)
+{
+ struct itemdef *d;
+
+ if (!def_loaded) {
+ def_load ();
+ }
+
+ d = def_find (item);
+ if ((NULL == d) || (NULL == d->value)) {
+ return false;
+ }
+
+ return (strcasecmp (d->value, "yes") == 0);
+}
+
+
+/*
+ * getdef_num - get numerical value from table of definitions
+ *
+ * Returns numeric value of specified item, else the "dflt" value if
+ * the item is not defined. Octal (leading "0") and hex (leading "0x")
+ * values are handled.
+ */
+
+int getdef_num (const char *item, int dflt)
+{
+ struct itemdef *d;
+ long val;
+
+ if (!def_loaded) {
+ def_load ();
+ }
+
+ d = def_find (item);
+ if ((NULL == d) || (NULL == d->value)) {
+ return dflt;
+ }
+
+ if ( (getlong (d->value, &val) == 0)
+ || (val > INT_MAX)
+ || (val < INT_MIN)) {
+ fprintf (stderr,
+ _("configuration error - cannot parse %s value: '%s'"),
+ item, d->value);
+ return dflt;
+ }
+
+ return (int) val;
+}
+
+
+/*
+ * getdef_unum - get unsigned numerical value from table of definitions
+ *
+ * Returns numeric value of specified item, else the "dflt" value if
+ * the item is not defined. Octal (leading "0") and hex (leading "0x")
+ * values are handled.
+ */
+
+unsigned int getdef_unum (const char *item, unsigned int dflt)
+{
+ struct itemdef *d;
+ long val;
+
+ if (!def_loaded) {
+ def_load ();
+ }
+
+ d = def_find (item);
+ if ((NULL == d) || (NULL == d->value)) {
+ return dflt;
+ }
+
+ if ( (getlong (d->value, &val) == 0)
+ || (val < 0)
+ || (val > INT_MAX)) {
+ fprintf (stderr,
+ _("configuration error - cannot parse %s value: '%s'"),
+ item, d->value);
+ return dflt;
+ }
+
+ return (unsigned int) val;
+}
+
+
+/*
+ * getdef_long - get long integer value from table of definitions
+ *
+ * Returns numeric value of specified item, else the "dflt" value if
+ * the item is not defined. Octal (leading "0") and hex (leading "0x")
+ * values are handled.
+ */
+
+long getdef_long (const char *item, long dflt)
+{
+ struct itemdef *d;
+ long val;
+
+ if (!def_loaded) {
+ def_load ();
+ }
+
+ d = def_find (item);
+ if ((NULL == d) || (NULL == d->value)) {
+ return dflt;
+ }
+
+ if (getlong (d->value, &val) == 0) {
+ fprintf (stderr,
+ _("configuration error - cannot parse %s value: '%s'"),
+ item, d->value);
+ return dflt;
+ }
+
+ return val;
+}
+
+/*
+ * getdef_ulong - get unsigned long numerical value from table of definitions
+ *
+ * Returns numeric value of specified item, else the "dflt" value if
+ * the item is not defined. Octal (leading "0") and hex (leading "0x")
+ * values are handled.
+ */
+
+unsigned long getdef_ulong (const char *item, unsigned long dflt)
+{
+ struct itemdef *d;
+ unsigned long val;
+
+ if (!def_loaded) {
+ def_load ();
+ }
+
+ d = def_find (item);
+ if ((NULL == d) || (NULL == d->value)) {
+ return dflt;
+ }
+
+ if (getulong (d->value, &val) == 0) {
+ /* FIXME: we should have a getulong */
+ fprintf (stderr,
+ _("configuration error - cannot parse %s value: '%s'"),
+ item, d->value);
+ return dflt;
+ }
+
+ return val;
+}
+
+/*
+ * putdef_str - override the value read from /etc/login.defs
+ * (also used when loading the initial defaults)
+ */
+
+int putdef_str (const char *name, const char *value)
+{
+ struct itemdef *d;
+ char *cp;
+
+ if (!def_loaded) {
+ def_load ();
+ }
+
+ /*
+ * Locate the slot to save the value. If this parameter
+ * is unknown then "def_find" will print an err message.
+ */
+ d = def_find (name);
+ if (NULL == d) {
+ return -1;
+ }
+
+ /*
+ * Save off the value.
+ */
+ cp = strdup (value);
+ if (NULL == cp) {
+ (void) fputs (_("Could not allocate space for config info.\n"),
+ stderr);
+ SYSLOG ((LOG_ERR, "could not allocate space for config info"));
+ return -1;
+ }
+
+ if (NULL != d->value) {
+ free (d->value);
+ }
+
+ d->value = cp;
+ return 0;
+}
+
+
+/*
+ * def_find - locate named item in table
+ *
+ * Search through a table of configurable items to locate the
+ * specified configuration option.
+ */
+
+static /*@observer@*/ /*@null@*/struct itemdef *def_find (const char *name)
+{
+ struct itemdef *ptr;
+
+
+ /*
+ * Search into the table.
+ */
+
+ for (ptr = def_table; NULL != ptr->name; ptr++) {
+ if (strcmp (ptr->name, name) == 0) {
+ return ptr;
+ }
+ }
+
+ /*
+ * Item was never found.
+ */
+
+ for (ptr = knowndef_table; NULL != ptr->name; ptr++) {
+ if (strcmp (ptr->name, name) == 0) {
+ goto out;
+ }
+ }
+ fprintf (stderr,
+ _("configuration error - unknown item '%s' (notify administrator)\n"),
+ name);
+ SYSLOG ((LOG_CRIT, "unknown configuration item `%s'", name));
+
+out:
+ return (struct itemdef *) NULL;
+}
+
+/*
+ * def_load - load configuration table
+ *
+ * Loads the user-configured options from the default configuration file
+ */
+
+static void def_load (void)
+{
+ int i;
+ FILE *fp;
+ char buf[1024], *name, *value, *s;
+
+ /*
+ * Set the initialized flag.
+ * (do it early to prevent recursion in putdef_str())
+ */
+ def_loaded = true;
+
+ /*
+ * Open the configuration definitions file.
+ */
+ fp = fopen (def_fname, "r");
+ if (NULL == fp) {
+ if (errno == ENOENT)
+ return;
+
+ int err = errno;
+ SYSLOG ((LOG_CRIT, "cannot open login definitions %s [%s]",
+ def_fname, strerror (err)));
+ exit (EXIT_FAILURE);
+ }
+
+ /*
+ * Go through all of the lines in the file.
+ */
+ while (fgets (buf, (int) sizeof (buf), fp) != NULL) {
+
+ /*
+ * Trim trailing whitespace.
+ */
+ for (i = (int) strlen (buf) - 1; i >= 0; --i) {
+ if (!isspace (buf[i])) {
+ break;
+ }
+ }
+ i++;
+ buf[i] = '\0';
+
+ /*
+ * Break the line into two fields.
+ */
+ name = buf + strspn (buf, " \t"); /* first nonwhite */
+ if (*name == '\0' || *name == '#')
+ continue; /* comment or empty */
+
+ s = name + strcspn (name, " \t"); /* end of field */
+ if (*s == '\0')
+ continue; /* only 1 field?? */
+
+ *s++ = '\0';
+ value = s + strspn (s, " \"\t"); /* next nonwhite */
+ *(value + strcspn (value, "\"")) = '\0';
+
+ /*
+ * Store the value in def_table.
+ *
+ * Ignore failures to load the login.defs file.
+ * The error was already reported to the user and to
+ * syslog. The tools will just use their default values.
+ */
+ (void)putdef_str (name, value);
+ }
+
+ if (ferror (fp) != 0) {
+ int err = errno;
+ SYSLOG ((LOG_CRIT, "cannot read login definitions %s [%s]",
+ def_fname, strerror (err)));
+ exit (EXIT_FAILURE);
+ }
+
+ (void) fclose (fp);
+}
+
+
+#ifdef CKDEFS
+int main (int argc, char **argv)
+{
+ int i;
+ char *cp;
+ struct itemdef *d;
+
+ def_load ();
+
+ for (i = 0; i < NUMDEFS; ++i) {
+ d = def_find (def_table[i].name);
+ if (NULL == d) {
+ printf ("error - lookup '%s' failed\n",
+ def_table[i].name);
+ } else {
+ printf ("%4d %-24s %s\n", i + 1, d->name, d->value);
+ }
+ }
+ for (i = 1; i < argc; i++) {
+ cp = getdef_str (argv[1]);
+ if (NULL != cp) {
+ printf ("%s `%s'\n", argv[1], cp);
+ } else {
+ printf ("%s not found\n", argv[1]);
+ }
+ }
+ exit (EXIT_SUCCESS);
+}
+#endif
diff --git a/lib/getdef.h b/lib/getdef.h
new file mode 100644
index 0000000..15e35ff
--- /dev/null
+++ b/lib/getdef.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 1991 - 1994, Julianne Frances Haugh
+ * Copyright (c) 1996 - 2000, Marek Michałkiewicz
+ * Copyright (c) 2002 - 2006, Tomasz Kłoczko
+ * Copyright (c) 2008 , Nicolas François
+ * 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. The name of the copyright holders or contributors may not be used to
+ * endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 THE COPYRIGHT
+ * HOLDERS 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.
+ */
+#ifndef _GETDEF_H
+#define _GETDEF_H
+
+/* getdef.c */
+extern bool getdef_bool (const char *);
+extern long getdef_long (const char *, long);
+extern int getdef_num (const char *, int);
+extern unsigned long getdef_ulong (const char *, unsigned long);
+extern unsigned int getdef_unum (const char *, unsigned int);
+extern /*@observer@*/ /*@null@*/const char *getdef_str (const char *);
+extern int putdef_str (const char *, const char *);
+
+/* default UMASK value if not specified in /etc/login.defs */
+#define GETDEF_DEFAULT_UMASK 022
+
+#endif /* _GETDEF_H */
diff --git a/lib/getlong.c b/lib/getlong.c
new file mode 100644
index 0000000..47c3a60
--- /dev/null
+++ b/lib/getlong.c
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2007 - 2009, Nicolas François
+ * 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. The name of the copyright holders or contributors may not be used to
+ * endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 THE COPYRIGHT
+ * HOLDERS 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.
+ */
+
+#include <config.h>
+
+#ident "$Id$"
+
+#include <stdlib.h>
+#include <errno.h>
+#include "prototypes.h"
+
+/*
+ * getlong - extract a long integer provided by the numstr string in *result
+ *
+ * It supports decimal, hexadecimal or octal representations.
+ *
+ * Returns 0 on failure, 1 on success.
+ */
+int getlong (const char *numstr, /*@out@*/long int *result)
+{
+ long val;
+ char *endptr;
+
+ errno = 0;
+ val = strtol (numstr, &endptr, 0);
+ if (('\0' == *numstr) || ('\0' != *endptr) || (ERANGE == errno)) {
+ return 0;
+ }
+
+ *result = val;
+ return 1;
+}
+
diff --git a/lib/getulong.c b/lib/getulong.c
new file mode 100644
index 0000000..08d2c1a
--- /dev/null
+++ b/lib/getulong.c
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2007 - 2009, Nicolas François
+ * 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. The name of the copyright holders or contributors may not be used to
+ * endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 THE COPYRIGHT
+ * HOLDERS 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.
+ */
+
+#include <config.h>
+
+#ident "$Id: getlong.c 2763 2009-04-23 09:57:03Z nekral-guest $"
+
+#include <stdlib.h>
+#include <errno.h>
+#include "prototypes.h"
+
+/*
+ * getulong - extract an unsigned long integer provided by the numstr string in *result
+ *
+ * It supports decimal, hexadecimal or octal representations.
+ *
+ * Returns 0 on failure, 1 on success.
+ */
+int getulong (const char *numstr, /*@out@*/unsigned long int *result)
+{
+ unsigned long int val;
+ char *endptr;
+
+ errno = 0;
+ val = strtoul (numstr, &endptr, 0);
+ if ( ('\0' == *numstr)
+ || ('\0' != *endptr)
+ || (ERANGE == errno)
+ ) {
+ return 0;
+ }
+
+ *result = val;
+ return 1;
+}
+
diff --git a/lib/groupio.c b/lib/groupio.c
new file mode 100644
index 0000000..ae2302b
--- /dev/null
+++ b/lib/groupio.c
@@ -0,0 +1,462 @@
+/*
+ * Copyright (c) 1990 - 1994, Julianne Frances Haugh
+ * Copyright (c) 1996 - 2000, Marek Michałkiewicz
+ * Copyright (c) 2001 , Michał Moskal
+ * Copyright (c) 2005 , Tomasz Kłoczko
+ * Copyright (c) 2007 - 2010, Nicolas François
+ * 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. The name of the copyright holders or contributors may not be used to
+ * endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 THE COPYRIGHT
+ * HOLDERS 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.
+ */
+
+#include <config.h>
+
+#ident "$Id$"
+
+#include <assert.h>
+#include <stdio.h>
+
+#include "prototypes.h"
+#include "defines.h"
+#include "commonio.h"
+#include "getdef.h"
+#include "groupio.h"
+
+static /*@null@*/struct commonio_entry *merge_group_entries (
+ /*@null@*/ /*@returned@*/struct commonio_entry *gr1,
+ /*@null@*/struct commonio_entry *gr2);
+static int split_groups (unsigned int max_members);
+static int group_open_hook (void);
+
+static /*@null@*/ /*@only@*/void *group_dup (const void *ent)
+{
+ const struct group *gr = ent;
+
+ return __gr_dup (gr);
+}
+
+static void group_free (/*@out@*/ /*@only@*/void *ent)
+{
+ struct group *gr = ent;
+
+ gr_free (gr);
+}
+
+static const char *group_getname (const void *ent)
+{
+ const struct group *gr = ent;
+
+ return gr->gr_name;
+}
+
+static void *group_parse (const char *line)
+{
+ return (void *) sgetgrent (line);
+}
+
+static int group_put (const void *ent, FILE * file)
+{
+ const struct group *gr = ent;
+
+ if ( (NULL == gr)
+ || (valid_field (gr->gr_name, ":\n") == -1)
+ || (valid_field (gr->gr_passwd, ":\n") == -1)
+ || (gr->gr_gid == (gid_t)-1)) {
+ return -1;
+ }
+
+ /* FIXME: fail also if gr->gr_mem == NULL ?*/
+ if (NULL != gr->gr_mem) {
+ size_t i;
+ for (i = 0; NULL != gr->gr_mem[i]; i++) {
+ if (valid_field (gr->gr_mem[i], ",:\n") == -1) {
+ return -1;
+ }
+ }
+ }
+
+ return (putgrent (gr, file) == -1) ? -1 : 0;
+}
+
+static int group_close_hook (void)
+{
+ unsigned int max_members = getdef_unum("MAX_MEMBERS_PER_GROUP", 0);
+
+ if (0 == max_members) {
+ return 1;
+ }
+
+ return split_groups (max_members);
+}
+
+static struct commonio_ops group_ops = {
+ group_dup,
+ group_free,
+ group_getname,
+ group_parse,
+ group_put,
+ fgetsx,
+ fputsx,
+ group_open_hook,
+ group_close_hook
+};
+
+static /*@owned@*/struct commonio_db group_db = {
+ GROUP_FILE, /* filename */
+ &group_ops, /* ops */
+ NULL, /* fp */
+#ifdef WITH_SELINUX
+ NULL, /* scontext */
+#endif
+ 0644, /* st_mode */
+ 0, /* st_uid */
+ 0, /* st_gid */
+ NULL, /* head */
+ NULL, /* tail */
+ NULL, /* cursor */
+ false, /* changed */
+ false, /* isopen */
+ false, /* locked */
+ false /* readonly */
+};
+
+int gr_setdbname (const char *filename)
+{
+ return commonio_setname (&group_db, filename);
+}
+
+/*@observer@*/const char *gr_dbname (void)
+{
+ return group_db.filename;
+}
+
+int gr_lock (void)
+{
+ return commonio_lock (&group_db);
+}
+
+int gr_open (int mode)
+{
+ return commonio_open (&group_db, mode);
+}
+
+/*@observer@*/ /*@null@*/const struct group *gr_locate (const char *name)
+{
+ return commonio_locate (&group_db, name);
+}
+
+/*@observer@*/ /*@null@*/const struct group *gr_locate_gid (gid_t gid)
+{
+ const struct group *grp;
+
+ gr_rewind ();
+ while ( ((grp = gr_next ()) != NULL)
+ && (grp->gr_gid != gid)) {
+ }
+
+ return grp;
+}
+
+int gr_update (const struct group *gr)
+{
+ return commonio_update (&group_db, (const void *) gr);
+}
+
+int gr_remove (const char *name)
+{
+ return commonio_remove (&group_db, name);
+}
+
+int gr_rewind (void)
+{
+ return commonio_rewind (&group_db);
+}
+
+/*@observer@*/ /*@null@*/const struct group *gr_next (void)
+{
+ return commonio_next (&group_db);
+}
+
+int gr_close (void)
+{
+ return commonio_close (&group_db);
+}
+
+int gr_unlock (void)
+{
+ return commonio_unlock (&group_db);
+}
+
+void __gr_set_changed (void)
+{
+ group_db.changed = true;
+}
+
+/*@dependent@*/ /*@null@*/struct commonio_entry *__gr_get_head (void)
+{
+ return group_db.head;
+}
+
+/*@observer@*/const struct commonio_db *__gr_get_db (void)
+{
+ return &group_db;
+}
+
+void __gr_del_entry (const struct commonio_entry *ent)
+{
+ commonio_del_entry (&group_db, ent);
+}
+
+static int gr_cmp (const void *p1, const void *p2)
+{
+ gid_t u1, u2;
+
+ if ((*(struct commonio_entry **) p1)->eptr == NULL) {
+ return 1;
+ }
+ if ((*(struct commonio_entry **) p2)->eptr == NULL) {
+ return -1;
+ }
+
+ u1 = ((struct group *) (*(struct commonio_entry **) p1)->eptr)->gr_gid;
+ u2 = ((struct group *) (*(struct commonio_entry **) p2)->eptr)->gr_gid;
+
+ if (u1 < u2) {
+ return -1;
+ } else if (u1 > u2) {
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+/* Sort entries by GID */
+int gr_sort ()
+{
+ return commonio_sort (&group_db, gr_cmp);
+}
+
+static int group_open_hook (void)
+{
+ unsigned int max_members = getdef_unum("MAX_MEMBERS_PER_GROUP", 0);
+ struct commonio_entry *gr1, *gr2;
+
+ if (0 == max_members) {
+ return 1;
+ }
+
+ for (gr1 = group_db.head; NULL != gr1; gr1 = gr1->next) {
+ for (gr2 = gr1->next; NULL != gr2; gr2 = gr2->next) {
+ struct group *g1 = (struct group *)gr1->eptr;
+ struct group *g2 = (struct group *)gr2->eptr;
+ if (NULL != g1 &&
+ NULL != g2 &&
+ 0 == strcmp (g1->gr_name, g2->gr_name) &&
+ 0 == strcmp (g1->gr_passwd, g2->gr_passwd) &&
+ g1->gr_gid == g2->gr_gid) {
+ /* Both group entries refer to the same
+ * group. It is a split group. Merge the
+ * members. */
+ gr1 = merge_group_entries (gr1, gr2);
+ if (NULL == gr1)
+ return 0;
+ /* Unlink gr2 */
+ if (NULL != gr2->next) {
+ gr2->next->prev = gr2->prev;
+ }
+ /* gr2 does not start with head */
+ assert (NULL != gr2->prev);
+ gr2->prev->next = gr2->next;
+ }
+ }
+ assert (NULL != gr1);
+ }
+
+ return 1;
+}
+
+/*
+ * Merge the list of members of the two group entries.
+ *
+ * The commonio_entry arguments shall be group entries.
+ *
+ * You should not merge the members of two groups if they don't have the
+ * same name, password and gid.
+ *
+ * It merge the members of the second entry in the first one, and return
+ * the modified first entry on success, or NULL on failure (with errno
+ * set).
+ */
+static /*@null@*/struct commonio_entry *merge_group_entries (
+ /*@null@*/ /*@returned@*/struct commonio_entry *gr1,
+ /*@null@*/struct commonio_entry *gr2)
+{
+ struct group *gptr1;
+ struct group *gptr2;
+ char **new_members;
+ size_t members = 0;
+ char *new_line;
+ size_t new_line_len, i;
+ if (NULL == gr2 || NULL == gr1) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ gptr1 = (struct group *)gr1->eptr;
+ gptr2 = (struct group *)gr2->eptr;
+ if (NULL == gptr2 || NULL == gptr1) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ /* Concatenate the 2 lines */
+ new_line_len = strlen (gr1->line) + strlen (gr2->line) +1;
+ new_line = (char *)malloc (new_line_len + 1);
+ if (NULL == new_line) {
+ errno = ENOMEM;
+ return NULL;
+ }
+ snprintf(new_line, new_line_len + 1, "%s\n%s", gr1->line, gr2->line);
+
+ /* Concatenate the 2 list of members */
+ for (i=0; NULL != gptr1->gr_mem[i]; i++);
+ members += i;
+ for (i=0; NULL != gptr2->gr_mem[i]; i++) {
+ char **pmember = gptr1->gr_mem;
+ while (NULL != *pmember) {
+ if (0 == strcmp(*pmember, gptr2->gr_mem[i])) {
+ break;
+ }
+ pmember++;
+ }
+ if (NULL == *pmember) {
+ members++;
+ }
+ }
+ new_members = (char **)calloc ( (members+1), sizeof(char*) );
+ if (NULL == new_members) {
+ free (new_line);
+ errno = ENOMEM;
+ return NULL;
+ }
+ for (i=0; NULL != gptr1->gr_mem[i]; i++) {
+ new_members[i] = gptr1->gr_mem[i];
+ }
+ /* NULL termination enforced by above calloc */
+
+ members = i;
+ for (i=0; NULL != gptr2->gr_mem[i]; i++) {
+ char **pmember = new_members;
+ while (NULL != *pmember) {
+ if (0 == strcmp(*pmember, gptr2->gr_mem[i])) {
+ break;
+ }
+ pmember++;
+ }
+ if (NULL == *pmember) {
+ new_members[members] = gptr2->gr_mem[i];
+ members++;
+ new_members[members] = NULL;
+ }
+ }
+
+ gr1->line = new_line;
+ gptr1->gr_mem = new_members;
+
+ return gr1;
+}
+
+/*
+ * Scan the group database and split the groups which have more members
+ * than specified, if this is the result from a current change.
+ *
+ * Return 0 on failure (errno set) and 1 on success.
+ */
+static int split_groups (unsigned int max_members)
+{
+ struct commonio_entry *gr;
+
+ for (gr = group_db.head; NULL != gr; gr = gr->next) {
+ struct group *gptr = (struct group *)gr->eptr;
+ struct commonio_entry *new;
+ struct group *new_gptr;
+ unsigned int members = 0;
+ unsigned int i;
+
+ /* Check if this group must be split */
+ if (!gr->changed) {
+ continue;
+ }
+ if (NULL == gptr) {
+ continue;
+ }
+ for (members = 0; NULL != gptr->gr_mem[members]; members++);
+ if (members <= max_members) {
+ continue;
+ }
+
+ new = (struct commonio_entry *) malloc (sizeof *new);
+ if (NULL == new) {
+ errno = ENOMEM;
+ return 0;
+ }
+ new->eptr = group_dup(gr->eptr);
+ if (NULL == new->eptr) {
+ free (new);
+ errno = ENOMEM;
+ return 0;
+ }
+ new_gptr = (struct group *)new->eptr;
+ new->line = NULL;
+ new->changed = true;
+
+ /* Enforce the maximum number of members on gptr */
+ for (i = max_members; NULL != gptr->gr_mem[i]; i++) {
+ free (gptr->gr_mem[i]);
+ gptr->gr_mem[i] = NULL;
+ }
+ /* Shift all the members */
+ /* The number of members in new_gptr will be check later */
+ for (i = 0; NULL != new_gptr->gr_mem[i + max_members]; i++) {
+ if (NULL != new_gptr->gr_mem[i]) {
+ free (new_gptr->gr_mem[i]);
+ }
+ new_gptr->gr_mem[i] = new_gptr->gr_mem[i + max_members];
+ new_gptr->gr_mem[i + max_members] = NULL;
+ }
+ for (; NULL != new_gptr->gr_mem[i]; i++) {
+ free (new_gptr->gr_mem[i]);
+ new_gptr->gr_mem[i] = NULL;
+ }
+
+ /* insert the new entry in the list */
+ new->prev = gr;
+ new->next = gr->next;
+ gr->next = new;
+ }
+
+ return 1;
+}
+
diff --git a/lib/groupio.h b/lib/groupio.h
new file mode 100644
index 0000000..e1f1b02
--- /dev/null
+++ b/lib/groupio.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 1990 - 1994, Julianne Frances Haugh
+ * Copyright (c) 1996 - 2000, Marek Michałkiewicz
+ * Copyright (c) 2001 , Michał Moskal
+ * Copyright (c) 2005 , Tomasz Kłoczko
+ * Copyright (c) 2008 , Nicolas François
+ * 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. The name of the copyright holders or contributors may not be used to
+ * endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 THE COPYRIGHT
+ * HOLDERS 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.
+ */
+
+/* $Id$ */
+#ifndef _GROUPIO_H
+#define _GROUPIO_H
+
+#include <sys/types.h>
+#include <grp.h>
+
+extern int gr_close (void);
+extern /*@observer@*/ /*@null@*/const struct group *gr_locate (const char *name);
+extern /*@observer@*/ /*@null@*/const struct group *gr_locate_gid (gid_t gid);
+extern int gr_lock (void);
+extern int gr_setdbname (const char *filename);
+extern /*@observer@*/const char *gr_dbname (void);
+extern /*@observer@*/ /*@null@*/const struct group *gr_next (void);
+extern int gr_open (int mode);
+extern int gr_remove (const char *name);
+extern int gr_rewind (void);
+extern int gr_unlock (void);
+extern int gr_update (const struct group *gr);
+extern int gr_sort (void);
+
+#endif
diff --git a/lib/groupmem.c b/lib/groupmem.c
new file mode 100644
index 0000000..1fd1c13
--- /dev/null
+++ b/lib/groupmem.c
@@ -0,0 +1,106 @@
+/*
+ * Copyright (c) 1990 - 1994, Julianne Frances Haugh
+ * Copyright (c) 1996 - 2000, Marek Michałkiewicz
+ * Copyright (c) 2001 , Michał Moskal
+ * Copyright (c) 2005 , Tomasz Kłoczko
+ * Copyright (c) 2007 - 2013, Nicolas François
+ * 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. The name of the copyright holders or contributors may not be used to
+ * endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 THE COPYRIGHT
+ * HOLDERS 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.
+ */
+
+#include <config.h>
+
+#ident "$Id$"
+
+#include "prototypes.h"
+#include "defines.h"
+#include "groupio.h"
+
+/*@null@*/ /*@only@*/struct group *__gr_dup (const struct group *grent)
+{
+ struct group *gr;
+ int i;
+
+ gr = (struct group *) malloc (sizeof *gr);
+ if (NULL == gr) {
+ return NULL;
+ }
+ /* The libc might define other fields. They won't be copied. */
+ memset (gr, 0, sizeof *gr);
+ gr->gr_gid = grent->gr_gid;
+ /*@-mustfreeonly@*/
+ gr->gr_name = strdup (grent->gr_name);
+ /*@=mustfreeonly@*/
+ if (NULL == gr->gr_name) {
+ gr_free(gr);
+ return NULL;
+ }
+ /*@-mustfreeonly@*/
+ gr->gr_passwd = strdup (grent->gr_passwd);
+ /*@=mustfreeonly@*/
+ if (NULL == gr->gr_passwd) {
+ gr_free(gr);
+ return NULL;
+ }
+
+ for (i = 0; grent->gr_mem[i]; i++);
+
+ /*@-mustfreeonly@*/
+ gr->gr_mem = (char **) malloc ((i + 1) * sizeof (char *));
+ /*@=mustfreeonly@*/
+ if (NULL == gr->gr_mem) {
+ gr_free(gr);
+ return NULL;
+ }
+ for (i = 0; grent->gr_mem[i]; i++) {
+ gr->gr_mem[i] = strdup (grent->gr_mem[i]);
+ if (NULL == gr->gr_mem[i]) {
+ gr_free(gr);
+ return NULL;
+ }
+ }
+ gr->gr_mem[i] = NULL;
+
+ return gr;
+}
+
+void gr_free (/*@out@*/ /*@only@*/struct group *grent)
+{
+ free (grent->gr_name);
+ if (NULL != grent->gr_passwd) {
+ memzero (grent->gr_passwd, strlen (grent->gr_passwd));
+ free (grent->gr_passwd);
+ }
+ if (NULL != grent->gr_mem) {
+ size_t i;
+ for (i = 0; NULL != grent->gr_mem[i]; i++) {
+ free (grent->gr_mem[i]);
+ }
+ free (grent->gr_mem);
+ }
+ free (grent);
+}
+
diff --git a/lib/gshadow.c b/lib/gshadow.c
new file mode 100644
index 0000000..e5a0f61
--- /dev/null
+++ b/lib/gshadow.c
@@ -0,0 +1,529 @@
+/*
+ * Copyright (c) 1990 - 1994, Julianne Frances Haugh
+ * Copyright (c) 1996 - 1998, Marek Michałkiewicz
+ * Copyright (c) 2005 , Tomasz Kłoczko
+ * Copyright (c) 2008 - 2009, Nicolas François
+ * 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. The name of the copyright holders or contributors may not be used to
+ * endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 THE COPYRIGHT
+ * HOLDERS 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.
+ */
+
+#include <config.h>
+
+/* Newer versions of Linux libc already have shadow support. */
+#if defined(SHADOWGRP) && !defined(HAVE_SHADOWGRP) /*{ */
+
+#ident "$Id$"
+
+#include <stdio.h>
+#include "prototypes.h"
+#include "defines.h"
+static /*@null@*/FILE *shadow;
+static /*@null@*//*@only@*/char **members = NULL;
+static size_t nmembers = 0;
+static /*@null@*//*@only@*/char **admins = NULL;
+static size_t nadmins = 0;
+static struct sgrp sgroup;
+
+#define FIELDS 4
+
+#ifdef USE_NIS
+static bool nis_used;
+static bool nis_ignore;
+static enum { native, start, middle, native2 } nis_state;
+static bool nis_bound;
+static char *nis_domain;
+static char *nis_key;
+static int nis_keylen;
+static char *nis_val;
+static int nis_vallen;
+
+#define IS_NISCHAR(c) ((c)=='+')
+#endif
+
+#ifdef USE_NIS
+/*
+ * bind_nis - bind to NIS server
+ */
+
+static int bind_nis (void)
+{
+ if (yp_get_default_domain (&nis_domain))
+ return -1;
+
+ nis_bound = true;
+ return 0;
+}
+#endif
+
+static /*@null@*/char **build_list (char *s, char **list[], size_t * nlist)
+{
+ char **ptr = *list;
+ size_t nelem = *nlist, size;
+
+ while (s != NULL && *s != '\0') {
+ size = (nelem + 1) * sizeof (ptr);
+ ptr = realloc (*list, size);
+ if (NULL != ptr) {
+ ptr[nelem] = s;
+ nelem++;
+ *list = ptr;
+ *nlist = nelem;
+ s = strchr (s, ',');
+ if (NULL != s) {
+ *s = '\0';
+ s++;
+ }
+ }
+ }
+ size = (nelem + 1) * sizeof (ptr);
+ ptr = realloc (*list, size);
+ if (NULL != ptr) {
+ ptr[nelem] = NULL;
+ *list = ptr;
+ }
+ return ptr;
+}
+
+void setsgent (void)
+{
+#ifdef USE_NIS
+ nis_state = native;
+#endif
+ if (NULL != shadow) {
+ rewind (shadow);
+ } else {
+ shadow = fopen (SGROUP_FILE, "r");
+ }
+}
+
+void endsgent (void)
+{
+ if (NULL != shadow) {
+ (void) fclose (shadow);
+ }
+
+ shadow = (FILE *) 0;
+}
+
+/*@observer@*//*@null@*/struct sgrp *sgetsgent (const char *string)
+{
+ static char *sgrbuf = NULL;
+ static size_t sgrbuflen = 0;
+
+ char *fields[FIELDS];
+ char *cp;
+ int i;
+ size_t len = strlen (string) + 1;
+
+ if (len > sgrbuflen) {
+ char *buf = (char *) realloc (sgrbuf, sizeof (char) * len);
+ if (NULL == buf) {
+ return NULL;
+ }
+ sgrbuf = buf;
+ sgrbuflen = len;
+ }
+
+ strncpy (sgrbuf, string, len);
+ sgrbuf[len-1] = '\0';
+
+ cp = strrchr (sgrbuf, '\n');
+ if (NULL != cp) {
+ *cp = '\0';
+ }
+
+ /*
+ * There should be exactly 4 colon separated fields. Find
+ * all 4 of them and save the starting addresses in fields[].
+ */
+
+ for (cp = sgrbuf, i = 0; (i < FIELDS) && (NULL != cp); i++) {
+ fields[i] = cp;
+ cp = strchr (cp, ':');
+ if (NULL != cp) {
+ *cp++ = '\0';
+ }
+ }
+
+ /*
+ * If there was an extra field somehow, or perhaps not enough,
+ * the line is invalid.
+ */
+
+ if ((NULL != cp) || (i != FIELDS)) {
+#ifdef USE_NIS
+ if (!IS_NISCHAR (fields[0][0])) {
+ return 0;
+ } else {
+ nis_used = true;
+ }
+#else
+ return 0;
+#endif
+ }
+
+ sgroup.sg_name = fields[0];
+ sgroup.sg_passwd = fields[1];
+ if (0 != nadmins) {
+ nadmins = 0;
+ free (admins);
+ admins = NULL;
+ }
+ if (0 != nmembers) {
+ nmembers = 0;
+ free (members);
+ members = NULL;
+ }
+ sgroup.sg_adm = build_list (fields[2], &admins, &nadmins);
+ sgroup.sg_mem = build_list (fields[3], &members, &nmembers);
+
+ return &sgroup;
+}
+
+/*
+ * fgetsgent - convert next line in stream to (struct sgrp)
+ *
+ * fgetsgent() reads the next line from the provided stream and
+ * converts it to a (struct sgrp). NULL is returned on EOF.
+ */
+
+/*@observer@*//*@null@*/struct sgrp *fgetsgent (/*@null@*/FILE * fp)
+{
+ static size_t buflen = 0;
+ static char *buf = NULL;
+
+ char *cp;
+
+ if (0 == buflen) {
+ buf = (char *) malloc (BUFSIZ);
+ if (NULL == buf) {
+ return NULL;
+ }
+ buflen = BUFSIZ;
+ }
+
+ if (NULL == fp) {
+ return NULL;
+ }
+
+#ifdef USE_NIS
+ while (fgetsx (buf, (int) buflen, fp) == buf)
+#else
+ if (fgetsx (buf, (int) buflen, fp) == buf)
+#endif
+ {
+ while ( ((cp = strrchr (buf, '\n')) == NULL)
+ && (feof (fp) == 0)) {
+ size_t len;
+
+ cp = (char *) realloc (buf, buflen*2);
+ if (NULL == cp) {
+ return NULL;
+ }
+ buf = cp;
+ buflen *= 2;
+
+ len = strlen (buf);
+ if (fgetsx (&buf[len],
+ (int) (buflen - len),
+ fp) != &buf[len]) {
+ return NULL;
+ }
+ }
+ cp = strrchr (buf, '\n');
+ if (NULL != cp) {
+ *cp = '\0';
+ }
+#ifdef USE_NIS
+ if (nis_ignore && IS_NISCHAR (buf[0])) {
+ continue;
+ }
+#endif
+ return (sgetsgent (buf));
+ }
+ return NULL;
+}
+
+/*
+ * getsgent - get a single shadow group entry
+ */
+
+/*@observer@*//*@null@*/struct sgrp *getsgent (void)
+{
+#ifdef USE_NIS
+ bool nis_1_group = false;
+ struct sgrp *val;
+#endif
+ if (NULL == shadow) {
+ setsgent ();
+ }
+
+#ifdef USE_NIS
+ again:
+ /*
+ * See if we are reading from the local file.
+ */
+
+ if (nis_state == native || nis_state == native2) {
+
+ /*
+ * Get the next entry from the shadow group file. Return
+ * NULL right away if there is none.
+ */
+
+ val = fgetsgent (shadow);
+ if (NULL == val) {
+ return 0;
+ }
+
+ /*
+ * If this entry began with a NIS escape character, we have
+ * to see if this is just a single group, or if the entire
+ * map is being asked for.
+ */
+
+ if (IS_NISCHAR (val->sg_name[0])) {
+ if ('\0' != val->sg_name[1]) {
+ nis_1_group = true;
+ } else {
+ nis_state = start;
+ }
+ }
+
+ /*
+ * If this isn't a NIS group and this isn't an escape to go
+ * use a NIS map, it must be a regular local group.
+ */
+
+ if (!nis_1_group && (nis_state != start)) {
+ return val;
+ }
+
+ /*
+ * If this is an escape to use an NIS map, switch over to
+ * that bunch of code.
+ */
+
+ if (nis_state == start) {
+ goto again;
+ }
+
+ /*
+ * NEEDSWORK. Here we substitute pieces-parts of this entry.
+ */
+
+ return 0;
+ } else {
+ if (!nis_bound) {
+ if (bind_nis ()) {
+ nis_state = native2;
+ goto again;
+ }
+ }
+ if (nis_state == start) {
+ if (yp_first (nis_domain, "gshadow.byname", &nis_key,
+ &nis_keylen, &nis_val, &nis_vallen)) {
+ nis_state = native2;
+ goto again;
+ }
+ nis_state = middle;
+ } else if (nis_state == middle) {
+ if (yp_next (nis_domain, "gshadow.byname", nis_key,
+ nis_keylen, &nis_key, &nis_keylen,
+ &nis_val, &nis_vallen)) {
+ nis_state = native2;
+ goto again;
+ }
+ }
+ return sgetsgent (nis_val);
+ }
+#else
+ return (fgetsgent (shadow));
+#endif
+}
+
+/*
+ * getsgnam - get a shadow group entry by name
+ */
+
+/*@observer@*//*@null@*/struct sgrp *getsgnam (const char *name)
+{
+ struct sgrp *sgrp;
+
+#ifdef USE_NIS
+ static char save_name[16];
+ int nis_disabled = 0;
+#endif
+
+ setsgent ();
+
+#ifdef USE_NIS
+ if (nis_used) {
+ again:
+
+ /*
+ * Search the gshadow.byname map for this group.
+ */
+
+ if (!nis_bound) {
+ bind_nis ();
+ }
+
+ if (nis_bound) {
+ char *cp;
+
+ if (yp_match (nis_domain, "gshadow.byname", name,
+ strlen (name), &nis_val,
+ &nis_vallen) == 0) {
+ cp = strchr (nis_val, '\n');
+ if (NULL != cp) {
+ *cp = '\0';
+ }
+
+ nis_state = middle;
+ sgrp = sgetsgent (nis_val);
+ if (NULL != sgrp) {
+ strcpy (save_name, sgrp->sg_name);
+ nis_key = save_name;
+ nis_keylen = strlen (save_name);
+ }
+ return sgrp;
+ }
+ }
+ nis_state = native2;
+ }
+#endif
+#ifdef USE_NIS
+ if (nis_used) {
+ nis_ignore = true;
+ nis_disabled = true;
+ }
+#endif
+ while ((sgrp = getsgent ()) != (struct sgrp *) 0) {
+ if (strcmp (name, sgrp->sg_name) == 0) {
+ break;
+ }
+ }
+#ifdef USE_NIS
+ nis_ignore = false;
+#endif
+ return sgrp;
+}
+
+/*
+ * putsgent - output shadow group entry in text form
+ *
+ * putsgent() converts the contents of a (struct sgrp) to text and
+ * writes the result to the given stream. This is the logical
+ * opposite of fgetsgent.
+ */
+
+int putsgent (const struct sgrp *sgrp, FILE * fp)
+{
+ char *buf, *cp;
+ int i;
+ size_t size;
+
+ if ((NULL == fp) || (NULL == sgrp)) {
+ return -1;
+ }
+
+ /* calculate the required buffer size */
+ size = strlen (sgrp->sg_name) + strlen (sgrp->sg_passwd) + 10;
+ for (i = 0; (NULL != sgrp->sg_adm) && (NULL != sgrp->sg_adm[i]); i++) {
+ size += strlen (sgrp->sg_adm[i]) + 1;
+ }
+ for (i = 0; (NULL != sgrp->sg_mem) && (NULL != sgrp->sg_mem[i]); i++) {
+ size += strlen (sgrp->sg_mem[i]) + 1;
+ }
+
+ buf = malloc (size);
+ if (NULL == buf) {
+ return -1;
+ }
+ cp = buf;
+
+ /*
+ * Copy the group name and passwd.
+ */
+
+ strcpy (cp, sgrp->sg_name);
+ cp += strlen (cp);
+ *cp++ = ':';
+
+ strcpy (cp, sgrp->sg_passwd);
+ cp += strlen (cp);
+ *cp++ = ':';
+
+ /*
+ * Copy the administrators, separating each from the other
+ * with a ",".
+ */
+
+ for (i = 0; NULL != sgrp->sg_adm[i]; i++) {
+ if (i > 0) {
+ *cp++ = ',';
+ }
+
+ strcpy (cp, sgrp->sg_adm[i]);
+ cp += strlen (cp);
+ }
+ *cp = ':';
+ cp++;
+
+ /*
+ * Now do likewise with the group members.
+ */
+
+ for (i = 0; NULL != sgrp->sg_mem[i]; i++) {
+ if (i > 0) {
+ *cp = ',';
+ cp++;
+ }
+
+ strcpy (cp, sgrp->sg_mem[i]);
+ cp += strlen (cp);
+ }
+ *cp = '\n';
+ cp++;
+ *cp = '\0';
+
+ /*
+ * Output using the function which understands the line
+ * continuation conventions.
+ */
+
+ if (fputsx (buf, fp) == EOF) {
+ free (buf);
+ return -1;
+ }
+
+ free (buf);
+ return 0;
+}
+#else
+extern int errno; /* warning: ANSI C forbids an empty source file */
+#endif /*} SHADOWGRP */
diff --git a/lib/gshadow_.h b/lib/gshadow_.h
new file mode 100644
index 0000000..7959c5a
--- /dev/null
+++ b/lib/gshadow_.h
@@ -0,0 +1,75 @@
+/*
+ * Copyright (c) 1988 - 1994, Julianne Frances Haugh
+ * Copyright (c) 1996 - 1997, Marek Michałkiewicz
+ * Copyright (c) 2003 - 2005, Tomasz Kłoczko
+ * 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. The name of the copyright holders or contributors may not be used to
+ * endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 THE COPYRIGHT
+ * HOLDERS 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.
+ */
+
+/*
+ * $Id$
+ */
+
+#ifndef _H_GSHADOW
+#define _H_GSHADOW
+
+/*
+ * Shadow group security file structure
+ */
+
+struct sgrp {
+ char *sg_name; /* group name */
+ char *sg_passwd; /* group password */
+ char **sg_adm; /* group administrator list */
+ char **sg_mem; /* group membership list */
+};
+
+/*
+ * Shadow group security file functions.
+ */
+
+#include <stdio.h> /* for FILE */
+
+#if __STDC__
+/*@observer@*//*@null@*/struct sgrp *getsgent (void);
+/*@observer@*//*@null@*/struct sgrp *getsgnam (const char *);
+/*@observer@*//*@null@*/struct sgrp *sgetsgent (const char *);
+/*@observer@*//*@null@*/struct sgrp *fgetsgent (/*@null@*/FILE *);
+void setsgent (void);
+void endsgent (void);
+int putsgent (const struct sgrp *, FILE *);
+#else
+/*@observer@*//*@null@*/struct sgrp *getsgent ();
+/*@observer@*//*@null@*/struct sgrp *getsgnam ();
+/*@observer@*//*@null@*/struct sgrp *sgetsgent ();
+/*@observer@*//*@null@*/struct sgrp *fgetsgent ();
+void setsgent ();
+void endsgent ();
+int putsgent ();
+#endif
+
+#define GSHADOW "/etc/gshadow"
+#endif /* ifndef _H_GSHADOW */
diff --git a/lib/lockpw.c b/lib/lockpw.c
new file mode 100644
index 0000000..0bcf195
--- /dev/null
+++ b/lib/lockpw.c
@@ -0,0 +1,108 @@
+/*
+ * Copyright (c) 1992 , Julianne Frances Haugh
+ * Copyright (c) 1996 - 1998, Marek Michałkiewicz
+ * Copyright (c) 2005 , Tomasz Kłoczko
+ * 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. The name of the copyright holders or contributors may not be used to
+ * endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 THE COPYRIGHT
+ * HOLDERS 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.
+ */
+
+#include <config.h>
+
+#ifndef HAVE_LCKPWDF
+
+#ident "$Id$"
+
+#include "prototypes.h"
+#include "defines.h"
+#include "pwio.h"
+#include "shadowio.h"
+/*
+ * lckpwdf - lock the password files
+ */
+int lckpwdf (void)
+{
+ int i;
+
+ /*
+ * We have 15 seconds to lock the whole mess
+ */
+
+ for (i = 0; i < 15; i++)
+ if (pw_lock ())
+ break;
+ else
+ sleep (1);
+
+ /*
+ * Did we run out of time?
+ */
+
+ if (i == 15)
+ return -1;
+
+ /*
+ * Nope, use any remaining time to lock the shadow password
+ * file.
+ */
+
+ for (; i < 15; i++)
+ if (spw_lock ())
+ break;
+ else
+ sleep (1);
+
+ /*
+ * Out of time yet?
+ */
+
+ if (i == 15) {
+ pw_unlock ();
+ return -1;
+ }
+
+ /*
+ * Nope - and both files are now locked.
+ */
+
+ return 0;
+}
+
+/*
+ * ulckpwdf - unlock the password files
+ */
+
+int ulckpwdf (void)
+{
+
+ /*
+ * Unlock both files.
+ */
+
+ return (pw_unlock () && spw_unlock ())? 0 : -1;
+}
+#else
+extern int errno; /* warning: ANSI C forbids an empty source file */
+#endif
diff --git a/lib/nscd.c b/lib/nscd.c
new file mode 100644
index 0000000..7adb58f
--- /dev/null
+++ b/lib/nscd.c
@@ -0,0 +1,57 @@
+/* Author: Peter Vrabec <pvrabec@redhat.com> */
+
+#include <config.h>
+#ifdef USE_NSCD
+
+#include <stdio.h>
+#include <sys/wait.h>
+#include <sys/types.h>
+#include "exitcodes.h"
+#include "defines.h"
+#include "prototypes.h"
+#include "nscd.h"
+
+#define MSG_NSCD_FLUSH_CACHE_FAILED "%s: Failed to flush the nscd cache.\n"
+
+/*
+ * nscd_flush_cache - flush specified service buffer in nscd cache
+ */
+int nscd_flush_cache (const char *service)
+{
+ int status, code;
+ const char *cmd = "/usr/sbin/nscd";
+ const char *spawnedArgs[] = {"nscd", "-i", service, NULL};
+ const char *spawnedEnv[] = {NULL};
+
+ if (run_command (cmd, spawnedArgs, spawnedEnv, &status) != 0) {
+ /* run_command writes its own more detailed message. */
+ (void) fprintf (stderr, _(MSG_NSCD_FLUSH_CACHE_FAILED), Prog);
+ return -1;
+ }
+
+ code = WEXITSTATUS (status);
+ if (!WIFEXITED (status)) {
+ (void) fprintf (stderr,
+ _("%s: nscd did not terminate normally (signal %d)\n"),
+ Prog, WTERMSIG (status));
+ return -1;
+ } else if (code == E_CMD_NOTFOUND) {
+ /* nscd is not installed, or it is installed but uses an
+ interpreter that is missing. Probably the former. */
+ return 0;
+ } else if (code == 1) {
+ /* nscd is installed, but it isn't active. */
+ return 0;
+ } else if (code != 0) {
+ (void) fprintf (stderr, _("%s: nscd exited with status %d\n"),
+ Prog, code);
+ (void) fprintf (stderr, _(MSG_NSCD_FLUSH_CACHE_FAILED), Prog);
+ return -1;
+ }
+
+ return 0;
+}
+#else /* USE_NSCD */
+extern int errno; /* warning: ANSI C forbids an empty source file */
+#endif /* USE_NSCD */
+
diff --git a/lib/nscd.h b/lib/nscd.h
new file mode 100644
index 0000000..a430b00
--- /dev/null
+++ b/lib/nscd.h
@@ -0,0 +1,13 @@
+#ifndef _NSCD_H_
+#define _NSCD_H_
+
+/*
+ * nscd_flush_cache - flush specified service buffer in nscd cache
+ */
+#ifdef USE_NSCD
+extern int nscd_flush_cache (const char *service);
+#else
+#define nscd_flush_cache(service) (0)
+#endif
+
+#endif
diff --git a/lib/pam_defs.h b/lib/pam_defs.h
new file mode 100644
index 0000000..343fbb4
--- /dev/null
+++ b/lib/pam_defs.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 1999 , Marek Michałkiewicz
+ * Copyright (c) 2001 - 2005, Tomasz Kłoczko
+ * 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. The name of the copyright holders or contributors may not be used to
+ * endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 THE COPYRIGHT
+ * HOLDERS 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.
+ */
+
+#include <config.h>
+#include <security/pam_appl.h>
+#ifdef HAVE_SECURITY_PAM_MISC_H
+# include <security/pam_misc.h>
+#endif
+#ifdef HAVE_SECURITY_OPENPAM_H
+# include <security/openpam.h>
+#endif
+
+
+static struct pam_conv conv = {
+ SHADOW_PAM_CONVERSATION,
+ NULL
+};
+
+/* compatibility with different versions of Linux-PAM */
+#if !HAVE_DECL_PAM_ESTABLISH_CRED
+#define PAM_ESTABLISH_CRED PAM_CRED_ESTABLISH
+#endif
+#if !HAVE_DECL_PAM_DELETE_CRED
+#define PAM_DELETE_CRED PAM_CRED_DELETE
+#endif
+#if !HAVE_DECL_PAM_NEW_AUTHTOK_REQD
+#define PAM_NEW_AUTHTOK_REQD PAM_AUTHTOKEN_REQD
+#endif
+#if !HAVE_DECL_PAM_DATA_SILENT
+#define PAM_DATA_SILENT 0
+#endif
diff --git a/lib/port.c b/lib/port.c
new file mode 100644
index 0000000..438879c
--- /dev/null
+++ b/lib/port.c
@@ -0,0 +1,477 @@
+/*
+ * Copyright (c) 1989 - 1994, Julianne Frances Haugh
+ * Copyright (c) 1996 - 1997, Marek Michałkiewicz
+ * Copyright (c) 2005 , Tomasz Kłoczko
+ * Copyright (c) 2008 , Nicolas François
+ * 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. The name of the copyright holders or contributors may not be used to
+ * endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 THE COPYRIGHT
+ * HOLDERS 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.
+ */
+
+#include <config.h>
+
+#ident "$Id$"
+
+#include <stdio.h>
+#include <ctype.h>
+#include <errno.h>
+#include "defines.h"
+#include "prototypes.h"
+#include "port.h"
+
+static FILE *ports;
+
+/*
+ * portcmp - compare the name of a port to a /etc/porttime entry
+ *
+ * portcmp works like strcmp, except that if the last character
+ * in a failing match is a '*', the match is considered to have
+ * passed. The "*" match is suppressed whenever the port is "SU",
+ * which is the token the "su" command uses to validate access.
+ * A match returns 0, failure returns non-zero.
+ */
+
+static int portcmp (const char *pattern, const char *port)
+{
+ const char *orig = port;
+
+ while (('\0' != *pattern) && (*pattern == *port)) {
+ pattern++;
+ port++;
+ }
+
+ if (('\0' == *pattern) && ('\0' == *port)) {
+ return 0;
+ }
+ if (('S' == orig[0]) && ('U' == orig[1]) && ('\0' == orig[2])) {
+ return 1;
+ }
+
+ return (*pattern == '*') ? 0 : 1;
+}
+
+/*
+ * setportent - open /etc/porttime file or rewind
+ *
+ * the /etc/porttime file is rewound if already open, or
+ * opened for reading.
+ */
+
+static void setportent (void)
+{
+ if (NULL != ports) {
+ rewind (ports);
+ } else {
+ ports = fopen (PORTS, "r");
+ }
+}
+
+/*
+ * endportent - close the /etc/porttime file
+ *
+ * the /etc/porttime file is closed and the ports variable set
+ * to NULL to indicate that the /etc/porttime file is no longer
+ * open.
+ */
+
+static void endportent (void)
+{
+ if (NULL != ports) {
+ (void) fclose (ports);
+ }
+
+ ports = (FILE *) 0;
+}
+
+/*
+ * getportent - read a single entry from /etc/porttime
+ *
+ * the next line in /etc/porttime is converted to a (struct port)
+ * and a pointer to a static (struct port) is returned to the
+ * invoker. NULL is returned on either EOF or error. errno is
+ * set to EINVAL on error to distinguish the two conditions.
+ */
+
+static struct port *getportent (void)
+{
+ static struct port port; /* static struct to point to */
+ static char buf[BUFSIZ]; /* some space for stuff */
+ static char *ttys[PORT_TTY + 1]; /* some pointers to tty names */
+ static char *users[PORT_IDS + 1]; /* some pointers to user ids */
+ static struct pt_time ptimes[PORT_TIMES + 1]; /* time ranges */
+ char *cp; /* pointer into line */
+ int dtime; /* scratch time of day */
+ int i, j;
+ int saveerr = errno; /* errno value on entry */
+
+ /*
+ * If the ports file is not open, open the file. Do not rewind
+ * since we want to search from the beginning each time.
+ */
+
+ if (NULL == ports) {
+ setportent ();
+ }
+
+ if (NULL == ports) {
+ errno = saveerr;
+ return 0;
+ }
+
+ /*
+ * Common point for beginning a new line -
+ *
+ * - read a line, and NUL terminate
+ * - skip lines which begin with '#'
+ * - parse off the tty names
+ * - parse off a list of user names
+ * - parse off a list of days and times
+ */
+
+ again:
+
+ /*
+ * Get the next line and remove the last character, which
+ * is a '\n'. Lines which begin with '#' are all ignored.
+ */
+
+ if (fgets (buf, (int) sizeof buf, ports) == 0) {
+ errno = saveerr;
+ return 0;
+ }
+ if ('#' == buf[0]) {
+ goto again;
+ }
+
+ /*
+ * Get the name of the TTY device. It is the first colon
+ * separated field, and is the name of the TTY with no
+ * leading "/dev". The entry '*' is used to specify all
+ * TTY devices.
+ */
+
+ buf[strlen (buf) - 1] = 0;
+
+ port.pt_names = ttys;
+ for (cp = buf, j = 0; j < PORT_TTY; j++) {
+ port.pt_names[j] = cp;
+ while (('\0' != *cp) && (':' != *cp) && (',' != *cp)) {
+ cp++;
+ }
+
+ if ('\0' == *cp) {
+ goto again; /* line format error */
+ }
+
+ if (':' == *cp) { /* end of tty name list */
+ break;
+ }
+
+ if (',' == *cp) { /* end of current tty name */
+ *cp++ = '\0';
+ }
+ }
+ *cp = '\0';
+ cp++;
+ port.pt_names[j + 1] = (char *) 0;
+
+ /*
+ * Get the list of user names. It is the second colon
+ * separated field, and is a comma separated list of user
+ * names. The entry '*' is used to specify all usernames.
+ * The last entry in the list is a (char *) 0 pointer.
+ */
+
+ if (':' != *cp) {
+ port.pt_users = users;
+ port.pt_users[0] = cp;
+
+ for (j = 1; ':' != *cp; cp++) {
+ if ((',' == *cp) && (j < PORT_IDS)) {
+ *cp = '\0';
+ cp++;
+ port.pt_users[j] = cp;
+ j++;
+ }
+ }
+ port.pt_users[j] = 0;
+ } else {
+ port.pt_users = 0;
+ }
+
+ if (':' != *cp) {
+ goto again;
+ }
+
+ *cp = '\0';
+ cp++;
+
+ /*
+ * Get the list of valid times. The times field is the third
+ * colon separated field and is a list of days of the week and
+ * times during which this port may be used by this user. The
+ * valid days are 'Su', 'Mo', 'Tu', 'We', 'Th', 'Fr', and 'Sa'.
+ *
+ * In addition, the value 'Al' represents all 7 days, and 'Wk'
+ * represents the 5 weekdays.
+ *
+ * Times are given as HHMM-HHMM. The ending time may be before
+ * the starting time. Days are presumed to wrap at 0000.
+ */
+
+ if ('\0' == *cp) {
+ port.pt_times = 0;
+ return &port;
+ }
+
+ port.pt_times = ptimes;
+
+ /*
+ * Get the next comma separated entry
+ */
+
+ for (j = 0; ('\0' != *cp) && (j < PORT_TIMES); j++) {
+
+ /*
+ * Start off with no days of the week
+ */
+
+ port.pt_times[j].t_days = 0;
+
+ /*
+ * Check each two letter sequence to see if it is
+ * one of the abbreviations for the days of the
+ * week or the other two values.
+ */
+
+ for (i = 0;
+ ('\0' != cp[i]) && ('\0' != cp[i + 1]) && isalpha (cp[i]);
+ i += 2) {
+ switch ((cp[i] << 8) | (cp[i + 1])) {
+ case ('S' << 8) | 'u':
+ port.pt_times[j].t_days |= 01;
+ break;
+ case ('M' << 8) | 'o':
+ port.pt_times[j].t_days |= 02;
+ break;
+ case ('T' << 8) | 'u':
+ port.pt_times[j].t_days |= 04;
+ break;
+ case ('W' << 8) | 'e':
+ port.pt_times[j].t_days |= 010;
+ break;
+ case ('T' << 8) | 'h':
+ port.pt_times[j].t_days |= 020;
+ break;
+ case ('F' << 8) | 'r':
+ port.pt_times[j].t_days |= 040;
+ break;
+ case ('S' << 8) | 'a':
+ port.pt_times[j].t_days |= 0100;
+ break;
+ case ('W' << 8) | 'k':
+ port.pt_times[j].t_days |= 076;
+ break;
+ case ('A' << 8) | 'l':
+ port.pt_times[j].t_days |= 0177;
+ break;
+ default:
+ errno = EINVAL;
+ return 0;
+ }
+ }
+
+ /*
+ * The default is 'Al' if no days were seen.
+ */
+
+ if (0 == i) {
+ port.pt_times[j].t_days = 0177;
+ }
+
+ /*
+ * The start and end times are separated from each
+ * other by a '-'. The times are four digit numbers
+ * representing the times of day.
+ */
+
+ for (dtime = 0; ('\0' != cp[i]) && isdigit (cp[i]); i++) {
+ dtime = dtime * 10 + cp[i] - '0';
+ }
+
+ if (('-' != cp[i]) || (dtime > 2400) || ((dtime % 100) > 59)) {
+ goto again;
+ }
+ port.pt_times[j].t_start = dtime;
+ cp = cp + i + 1;
+
+ for (dtime = 0, i = 0;
+ ('\0' != cp[i]) && isdigit (cp[i]);
+ i++) {
+ dtime = dtime * 10 + cp[i] - '0';
+ }
+
+ if ( ((',' != cp[i]) && ('\0' != cp[i]))
+ || (dtime > 2400)
+ || ((dtime % 100) > 59)) {
+ goto again;
+ }
+
+ port.pt_times[j].t_end = dtime;
+ cp = cp + i + 1;
+ }
+
+ /*
+ * The end of the list is indicated by a pair of -1's for the
+ * start and end times.
+ */
+
+ port.pt_times[j].t_start = port.pt_times[j].t_end = -1;
+
+ return &port;
+}
+
+/*
+ * getttyuser - get ports information for user and tty
+ *
+ * getttyuser() searches the ports file for an entry with a TTY
+ * and user field both of which match the supplied TTY and
+ * user name. The file is searched from the beginning, so the
+ * entries are treated as an ordered list.
+ */
+
+static struct port *getttyuser (const char *tty, const char *user)
+{
+ int i, j;
+ struct port *port;
+
+ setportent ();
+
+ while ((port = getportent ()) != NULL) {
+ if ( (0 == port->pt_names)
+ || (0 == port->pt_users)) {
+ continue;
+ }
+
+ for (i = 0; NULL != port->pt_names[i]; i++) {
+ if (portcmp (port->pt_names[i], tty) == 0) {
+ break;
+ }
+ }
+
+ if (port->pt_names[i] == 0) {
+ continue;
+ }
+
+ for (j = 0; NULL != port->pt_users[j]; j++) {
+ if ( (strcmp (user, port->pt_users[j]) == 0)
+ || (strcmp (port->pt_users[j], "*") == 0)) {
+ break;
+ }
+ }
+
+ if (port->pt_users[j] != 0) {
+ break;
+ }
+ }
+ endportent ();
+ return port;
+}
+
+/*
+ * isttytime - tell if a given user may login at a particular time
+ *
+ * isttytime searches the ports file for an entry which matches
+ * the user name and TTY given.
+ */
+
+bool isttytime (const char *id, const char *port, time_t when)
+{
+ int i;
+ int dtime;
+ struct port *pp;
+ struct tm *tm;
+
+ /*
+ * Try to find a matching entry for this user. Default to
+ * letting the user in - there are plenty of ways to have an
+ * entry to match all users.
+ */
+
+ pp = getttyuser (port, id);
+ if (NULL == pp) {
+ return true;
+ }
+
+ /*
+ * The entry is there, but has no time entries - don't
+ * ever let them login.
+ */
+
+ if (0 == pp->pt_times) {
+ return false;
+ }
+
+ /*
+ * The current time is converted to HHMM format for
+ * comparison against the time values in the TTY entry.
+ */
+
+ tm = localtime (&when);
+ dtime = tm->tm_hour * 100 + tm->tm_min;
+
+ /*
+ * Each time entry is compared against the current
+ * time. For entries with the start after the end time,
+ * the comparison is made so that the time is between
+ * midnight and either the start or end time.
+ */
+
+ for (i = 0; pp->pt_times[i].t_start != -1; i++) {
+ if (!(pp->pt_times[i].t_days & PORT_DAY (tm->tm_wday))) {
+ continue;
+ }
+
+ if (pp->pt_times[i].t_start <= pp->pt_times[i].t_end) {
+ if ( (dtime >= pp->pt_times[i].t_start)
+ && (dtime <= pp->pt_times[i].t_end)) {
+ return true;
+ }
+ } else {
+ if ( (dtime >= pp->pt_times[i].t_start)
+ || (dtime <= pp->pt_times[i].t_end)) {
+ return true;
+ }
+ }
+ }
+
+ /*
+ * No matching time entry was found, user shouldn't
+ * be let in right now.
+ */
+
+ return false;
+}
+
diff --git a/lib/port.h b/lib/port.h
new file mode 100644
index 0000000..2da56b2
--- /dev/null
+++ b/lib/port.h
@@ -0,0 +1,83 @@
+/*
+ * Copyright (c) 1989 - 1991, Julianne Frances Haugh
+ * Copyright (c) 1996 - 1997, Marek Michałkiewicz
+ * Copyright (c) 2005 , Tomasz Kłoczko
+ * 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. The name of the copyright holders or contributors may not be used to
+ * endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 THE COPYRIGHT
+ * HOLDERS 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.
+ */
+
+/*
+ * port.h - structure of /etc/porttime
+ *
+ * $Id$
+ *
+ * Each entry in /etc/porttime consists of a TTY device
+ * name or "*" to indicate all TTY devices, followed by
+ * a list of 1 or more user IDs or "*" to indicate all
+ * user names, followed by a list of zero or more valid
+ * login times. Login time entries consist of zero or
+ * more day names (Su, Mo, Tu, We, Th, Fr, Sa, Wk, Al)
+ * followed by a pair of time values in HHMM format
+ * separated by a "-".
+ */
+
+/*
+ * PORTS - Name of system port access time file.
+ * PORT_IDS - Allowable number of IDs per entry.
+ * PORT_TTY - Allowable number of TTYs per entry.
+ * PORT_TIMES - Allowable number of time entries per entry.
+ * PORT_DAY - Day of the week to a bit value (0 = Sunday).
+ */
+
+#define PORTS "/etc/porttime"
+#define PORT_IDS 64
+#define PORT_TTY 64
+#define PORT_TIMES 24
+#define PORT_DAY(day) (1<<(day))
+
+/*
+ * pt_names - pointer to array of device names in /dev/
+ * pt_users - pointer to array of applicable user IDs.
+ * pt_times - pointer to list of allowable time periods.
+ */
+
+struct port {
+ char **pt_names;
+ char **pt_users;
+ struct pt_time *pt_times;
+};
+
+/*
+ * t_days - bit array for each day of the week (0 = Sunday)
+ * t_start - starting time for this entry
+ * t_end - ending time for this entry
+ */
+
+struct pt_time {
+ short t_days;
+ short t_start;
+ short t_end;
+};
diff --git a/lib/prototypes.h b/lib/prototypes.h
new file mode 100644
index 0000000..4808d5d
--- /dev/null
+++ b/lib/prototypes.h
@@ -0,0 +1,440 @@
+/*
+ * Copyright (c) 1990 - 1994, Julianne Frances Haugh
+ * Copyright (c) 1996 - 2000, Marek Michałkiewicz
+ * Copyright (c) 2003 - 2006, Tomasz Kłoczko
+ * Copyright (c) 2007 - 2010, Nicolas François
+ * 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. The name of the copyright holders or contributors may not be used to
+ * endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 THE COPYRIGHT
+ * HOLDERS 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.
+ */
+
+/*
+ * prototypes.h
+ *
+ * prototypes of libmisc functions, and private lib functions.
+ *
+ * $Id$
+ *
+ */
+
+#ifndef _PROTOTYPES_H
+#define _PROTOTYPES_H
+
+#include <config.h>
+
+#include <sys/stat.h>
+#ifdef USE_UTMPX
+#include <utmpx.h>
+#else
+#include <utmp.h>
+#endif
+#include <sys/types.h>
+#include <pwd.h>
+#include <grp.h>
+#include <shadow.h>
+#include <lastlog.h>
+
+#include "defines.h"
+#include "commonio.h"
+
+extern /*@observer@*/ const char *Prog;
+
+/* addgrps.c */
+#if defined (HAVE_SETGROUPS) && ! defined (USE_PAM)
+extern int add_groups (const char *);
+#endif
+
+/* age.c */
+extern void agecheck (/*@null@*/const struct spwd *);
+extern int expire (const struct passwd *, /*@null@*/const struct spwd *);
+/* isexpired.c */
+extern int isexpired (const struct passwd *, /*@null@*/const struct spwd *);
+
+/* basename() renamed to Basename() to avoid libc name space confusion */
+/* basename.c */
+extern /*@observer@*/const char *Basename (const char *str);
+
+/* chowndir.c */
+extern int chown_tree (const char *root,
+ uid_t old_uid, uid_t new_uid,
+ gid_t old_gid, gid_t new_gid);
+
+/* chowntty.c */
+extern void chown_tty (const struct passwd *);
+
+/* cleanup.c */
+typedef /*@null@*/void (*cleanup_function) (/*@null@*/void *arg);
+void add_cleanup (/*@notnull@*/cleanup_function pcf, /*@null@*/void *arg);
+void del_cleanup (/*@notnull@*/cleanup_function pcf);
+void do_cleanups (void);
+
+/* cleanup_group.c */
+struct cleanup_info_mod {
+ char *audit_msg;
+ char *action;
+ /*@observer@*/const char *name;
+};
+void cleanup_report_add_group (void *group_name);
+void cleanup_report_add_group_group (void *group_name);
+#ifdef SHADOWGRP
+void cleanup_report_add_group_gshadow (void *group_name);
+#endif
+void cleanup_report_del_group (void *group_name);
+void cleanup_report_del_group_group (void *group_name);
+#ifdef SHADOWGRP
+void cleanup_report_del_group_gshadow (void *group_name);
+#endif
+void cleanup_report_mod_passwd (void *cleanup_info);
+void cleanup_report_mod_group (void *cleanup_info);
+void cleanup_report_mod_gshadow (void *cleanup_info);
+void cleanup_unlock_group (/*@null@*/void *unused);
+#ifdef SHADOWGRP
+void cleanup_unlock_gshadow (/*@null@*/void *unused);
+#endif
+void cleanup_unlock_passwd (/*@null@*/void *unused);
+
+/* console.c */
+extern bool console (const char *);
+
+/* copydir.c */
+extern int copy_tree (const char *src_root, const char *dst_root,
+ bool copy_root,
+ bool reset_selinux,
+ uid_t old_uid, uid_t new_uid,
+ gid_t old_gid, gid_t new_gid);
+
+/* encrypt.c */
+extern /*@exposed@*//*@null@*/char *pw_encrypt (const char *, const char *);
+
+/* entry.c */
+extern void pw_entry (const char *, struct passwd *);
+
+/* env.c */
+extern void addenv (const char *, /*@null@*/const char *);
+extern void initenv (void);
+extern void set_env (int, char *const *);
+extern void sanitize_env (void);
+
+/* fields.c */
+extern void change_field (char *, size_t, const char *);
+extern int valid_field (const char *, const char *);
+
+/* find_new_gid.c */
+extern int find_new_gid (bool sys_group,
+ gid_t *gid,
+ /*@null@*/gid_t const *preferred_gid);
+
+/* find_new_uid.c */
+extern int find_new_uid (bool sys_user,
+ uid_t *uid,
+ /*@null@*/uid_t const *preferred_uid);
+
+#ifdef ENABLE_SUBIDS
+/* find_new_sub_gids.c */
+extern int find_new_sub_gids (const char *owner,
+ gid_t *range_start, unsigned long *range_count);
+
+/* find_new_sub_uids.c */
+extern int find_new_sub_uids (const char *owner,
+ uid_t *range_start, unsigned long *range_count);
+#endif /* ENABLE_SUBIDS */
+
+
+/* get_gid.c */
+extern int get_gid (const char *gidstr, gid_t *gid);
+
+/* getgr_nam_gid.c */
+extern /*@only@*//*@null@*/struct group *getgr_nam_gid (/*@null@*/const char *grname);
+
+/* getlong.c */
+extern int getlong (const char *numstr, /*@out@*/long int *result);
+
+/* get_pid.c */
+extern int get_pid (const char *pidstr, pid_t *pid);
+
+/* getrange */
+extern int getrange (char *range,
+ unsigned long *min, bool *has_min,
+ unsigned long *max, bool *has_max);
+
+/* gettime.c */
+extern time_t gettime ();
+
+/* get_uid.c */
+extern int get_uid (const char *uidstr, uid_t *uid);
+
+/* getulong.c */
+extern int getulong (const char *numstr, /*@out@*/unsigned long int *result);
+
+/* fputsx.c */
+extern /*@null@*/char *fgetsx (/*@returned@*/ /*@out@*/char *, int, FILE *);
+extern int fputsx (const char *, FILE *);
+
+/* groupio.c */
+extern void __gr_del_entry (const struct commonio_entry *ent);
+extern /*@observer@*/const struct commonio_db *__gr_get_db (void);
+extern /*@dependent@*/ /*@null@*/struct commonio_entry *__gr_get_head (void);
+extern void __gr_set_changed (void);
+
+/* groupmem.c */
+extern /*@null@*/ /*@only@*/struct group *__gr_dup (const struct group *grent);
+extern void gr_free (/*@out@*/ /*@only@*/struct group *grent);
+
+/* hushed.c */
+extern bool hushed (const char *username);
+
+/* audit_help.c */
+#ifdef WITH_AUDIT
+extern int audit_fd;
+extern void audit_help_open (void);
+/* Use AUDIT_NO_ID when a name is provided to audit_logger instead of an ID */
+#define AUDIT_NO_ID ((unsigned int) -1)
+typedef enum {
+ SHADOW_AUDIT_FAILURE = 0,
+ SHADOW_AUDIT_SUCCESS = 1} shadow_audit_result;
+extern void audit_logger (int type, const char *pgname, const char *op,
+ const char *name, unsigned int id,
+ shadow_audit_result result);
+void audit_logger_message (const char *message, shadow_audit_result result);
+#endif
+
+/* limits.c */
+#ifndef USE_PAM
+extern void setup_limits (const struct passwd *);
+#endif
+
+/* list.c */
+extern /*@only@*/ /*@out@*/char **add_list (/*@returned@*/ /*@only@*/char **, const char *);
+extern /*@only@*/ /*@out@*/char **del_list (/*@returned@*/ /*@only@*/char **, const char *);
+extern /*@only@*/ /*@out@*/char **dup_list (char *const *);
+extern bool is_on_list (char *const *list, const char *member);
+extern /*@only@*/char **comma_to_list (const char *);
+
+/* log.c */
+extern void dolastlog (
+ struct lastlog *ll,
+ const struct passwd *pw,
+ /*@unique@*/const char *line,
+ /*@unique@*/const char *host);
+
+/* login_nopam.c */
+extern int login_access (const char *user, const char *from);
+
+/* loginprompt.c */
+extern void login_prompt (const char *, char *, int);
+
+/* mail.c */
+extern void mailcheck (void);
+
+/* motd.c */
+extern void motd (void);
+
+/* myname.c */
+extern /*@null@*//*@only@*/struct passwd *get_my_pwent (void);
+
+/* pam_pass_non_interractive.c */
+#ifdef USE_PAM
+extern int do_pam_passwd_non_interractive (const char *pam_service,
+ const char *username,
+ const char* password);
+#endif /* USE_PAM */
+
+/* obscure.c */
+#ifndef USE_PAM
+extern bool obscure (const char *, const char *, const struct passwd *);
+#endif
+
+/* pam_pass.c */
+#ifdef USE_PAM
+extern void do_pam_passwd (const char *user, bool silent, bool change_expired);
+#endif
+
+/* port.c */
+extern bool isttytime (const char *, const char *, time_t);
+
+/* pwd2spwd.c */
+#ifndef USE_PAM
+extern struct spwd *pwd_to_spwd (const struct passwd *);
+#endif
+
+/* pwdcheck.c */
+#ifndef USE_PAM
+extern void passwd_check (const char *, const char *, const char *);
+#endif
+
+/* pwd_init.c */
+extern void pwd_init (void);
+
+/* pwio.c */
+extern void __pw_del_entry (const struct commonio_entry *ent);
+extern struct commonio_db *__pw_get_db (void);
+extern /*@dependent@*/ /*@null@*/struct commonio_entry *__pw_get_head (void);
+
+/* pwmem.c */
+extern /*@null@*/ /*@only@*/struct passwd *__pw_dup (const struct passwd *pwent);
+extern void pw_free (/*@out@*/ /*@only@*/struct passwd *pwent);
+
+/* remove_tree.c */
+extern int remove_tree (const char *root, bool remove_root);
+
+/* rlogin.c */
+extern int do_rlogin (const char *remote_host, char *name, size_t namelen,
+ char *term, size_t termlen);
+
+/* root_flag.c */
+extern void process_root_flag (const char* short_opt, int argc, char **argv);
+
+/* salt.c */
+extern /*@observer@*/const char *crypt_make_salt (/*@null@*//*@observer@*/const char *meth, /*@null@*/void *arg);
+
+/* selinux.c */
+#ifdef WITH_SELINUX
+extern int set_selinux_file_context (const char *dst_name);
+extern int reset_selinux_file_context (void);
+#endif
+
+/* semanage.c */
+#ifdef WITH_SELINUX
+extern int set_seuser(const char *login_name, const char *seuser_name);
+extern int del_seuser(const char *login_name);
+#endif
+
+/* setugid.c */
+extern int setup_groups (const struct passwd *info);
+extern int change_uid (const struct passwd *info);
+#if (defined HAVE_INITGROUPS) && (! defined USE_PAM)
+extern int setup_uid_gid (const struct passwd *info, bool is_console);
+#else
+extern int setup_uid_gid (const struct passwd *info);
+#endif
+
+/* setup.c */
+extern void setup (struct passwd *);
+
+/* setupenv.c */
+extern void setup_env (struct passwd *);
+
+/* sgetgrent.c */
+extern struct group *sgetgrent (const char *buf);
+
+/* sgetpwent.c */
+extern struct passwd *sgetpwent (const char *buf);
+
+/* sgetspent.c */
+#ifndef HAVE_SGETSPENT
+extern struct spwd *sgetspent (const char *string);
+#endif
+
+/* sgroupio.c */
+extern void __sgr_del_entry (const struct commonio_entry *ent);
+extern /*@null@*/ /*@only@*/struct sgrp *__sgr_dup (const struct sgrp *sgent);
+extern void sgr_free (/*@out@*/ /*@only@*/struct sgrp *sgent);
+extern /*@dependent@*/ /*@null@*/struct commonio_entry *__sgr_get_head (void);
+extern void __sgr_set_changed (void);
+
+/* shadowio.c */
+extern /*@dependent@*/ /*@null@*/struct commonio_entry *__spw_get_head (void);
+extern void __spw_del_entry (const struct commonio_entry *ent);
+
+/* shadowmem.c */
+extern /*@null@*/ /*@only@*/struct spwd *__spw_dup (const struct spwd *spent);
+extern void spw_free (/*@out@*/ /*@only@*/struct spwd *spent);
+
+/* shell.c */
+extern int shell (const char *file, /*@null@*/const char *arg, char *const envp[]);
+
+/* spawn.c */
+extern int run_command (const char *cmd, const char *argv[],
+ /*@null@*/const char *envp[], /*@out@*/int *status);
+
+/* strtoday.c */
+extern long strtoday (const char *);
+
+/* suauth.c */
+extern int check_su_auth (const char *actual_id,
+ const char *wanted_id,
+ bool su_to_root);
+
+/* sulog.c */
+extern void sulog (const char *tty,
+ bool success,
+ const char *oldname,
+ const char *name);
+
+/* sub.c */
+extern void subsystem (const struct passwd *);
+
+/* ttytype.c */
+extern void ttytype (const char *);
+
+/* tz.c */
+#ifndef USE_PAM
+extern /*@observer@*/const char *tz (const char *);
+#endif
+
+/* ulimit.c */
+extern int set_filesize_limit (int blocks);
+
+/* user_busy.c */
+extern int user_busy (const char *name, uid_t uid);
+
+/* utmp.c */
+extern /*@null@*/struct utmp *get_current_utmp (void);
+extern struct utmp *prepare_utmp (const char *name,
+ const char *line,
+ const char *host,
+ /*@null@*/const struct utmp *ut);
+extern int setutmp (struct utmp *ut);
+#ifdef USE_UTMPX
+extern struct utmpx *prepare_utmpx (const char *name,
+ const char *line,
+ const char *host,
+ /*@null@*/const struct utmp *ut);
+extern int setutmpx (struct utmpx *utx);
+#endif /* USE_UTMPX */
+
+/* valid.c */
+extern bool valid (const char *, const struct passwd *);
+
+/* xmalloc.c */
+extern /*@maynotreturn@*/ /*@only@*//*@out@*//*@notnull@*/char *xmalloc (size_t size)
+ /*@ensures MaxSet(result) == (size - 1); @*/;
+extern /*@maynotreturn@*/ /*@only@*//*@notnull@*/char *xstrdup (const char *);
+
+/* xgetpwnam.c */
+extern /*@null@*/ /*@only@*/struct passwd *xgetpwnam (const char *);
+/* xgetpwuid.c */
+extern /*@null@*/ /*@only@*/struct passwd *xgetpwuid (uid_t);
+/* xgetgrnam.c */
+extern /*@null@*/ /*@only@*/struct group *xgetgrnam (const char *);
+/* xgetgrgid.c */
+extern /*@null@*/ /*@only@*/struct group *xgetgrgid (gid_t);
+/* xgetspnam.c */
+extern /*@null@*/ /*@only@*/struct spwd *xgetspnam(const char *);
+
+/* yesno.c */
+extern bool yes_or_no (bool read_only);
+
+#endif /* _PROTOTYPES_H */
diff --git a/lib/pwauth.c b/lib/pwauth.c
new file mode 100644
index 0000000..9e24fbf
--- /dev/null
+++ b/lib/pwauth.c
@@ -0,0 +1,234 @@
+/*
+ * Copyright (c) 1992 - 1994, Julianne Frances Haugh
+ * Copyright (c) 1996 - 2000, Marek Michałkiewicz
+ * Copyright (c) 2003 - 2006, Tomasz Kłoczko
+ * Copyright (c) 2008 - 2009, Nicolas François
+ * 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. The name of the copyright holders or contributors may not be used to
+ * endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 THE COPYRIGHT
+ * HOLDERS 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.
+ */
+
+#include <config.h>
+
+#ifndef USE_PAM
+#ident "$Id$"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include "prototypes.h"
+#include "defines.h"
+#include "pwauth.h"
+#include "getdef.h"
+#ifdef SKEY
+#include <skey.h>
+#endif
+#ifdef __linux__ /* standard password prompt by default */
+static const char *PROMPT = gettext_noop ("Password: ");
+#else
+static const char *PROMPT = gettext_noop ("%s's Password: ");
+#endif
+
+bool wipe_clear_pass = true;
+/*@null@*/char *clear_pass = NULL;
+
+/*
+ * pw_auth - perform getpass/crypt authentication
+ *
+ * pw_auth gets the user's cleartext password and encrypts it
+ * using the salt in the encrypted password. The results are
+ * compared.
+ */
+
+int pw_auth (const char *cipher,
+ const char *user,
+ int reason,
+ /*@null@*/const char *input)
+{
+ char prompt[1024];
+ char *clear = NULL;
+ const char *cp;
+ const char *encrypted;
+ int retval;
+
+#ifdef SKEY
+ bool use_skey = false;
+ char challenge_info[40];
+ struct skey skey;
+#endif
+
+ /*
+ * There are programs for adding and deleting authentication data.
+ */
+
+ if ((PW_ADD == reason) || (PW_DELETE == reason)) {
+ return 0;
+ }
+
+ /*
+ * There are even programs for changing the user name ...
+ */
+
+ if ((PW_CHANGE == reason) && (NULL != input)) {
+ return 0;
+ }
+
+ /*
+ * WARNING:
+ *
+ * When we change a password and we are root, we don't prompt.
+ * This is so root can change any password without having to
+ * know it. This is a policy decision that might have to be
+ * revisited.
+ */
+
+ if ((PW_CHANGE == reason) && (getuid () == 0)) {
+ return 0;
+ }
+
+ /*
+ * WARNING:
+ *
+ * When we are logging in a user with no ciphertext password,
+ * we don't prompt for the password or anything. In reality
+ * the user could just hit <ENTER>, so it doesn't really
+ * matter.
+ */
+
+ if ((NULL == cipher) || ('\0' == *cipher)) {
+ return 0;
+ }
+
+#ifdef SKEY
+ /*
+ * If the user has an S/KEY entry show them the pertinent info
+ * and then we can try validating the created cyphertext and the SKEY.
+ * If there is no SKEY information we default to not using SKEY.
+ */
+
+# ifdef SKEY_BSD_STYLE
+ /*
+ * Some BSD updates to the S/KEY API adds a fourth parameter; the
+ * sizeof of the challenge info buffer.
+ */
+# define skeychallenge(s,u,c) skeychallenge(s,u,c,sizeof(c))
+# endif
+
+ if (skeychallenge (&skey, user, challenge_info) == 0) {
+ use_skey = true;
+ }
+#endif
+
+ /*
+ * Prompt for the password as required. FTPD and REXECD both
+ * get the cleartext password for us.
+ */
+
+ if ((PW_FTP != reason) && (PW_REXEC != reason) && (NULL == input)) {
+ cp = getdef_str ("LOGIN_STRING");
+ if (NULL == cp) {
+ cp = _(PROMPT);
+ }
+#ifdef SKEY
+ if (use_skey) {
+ printf ("[%s]\n", challenge_info);
+ }
+#endif
+
+ snprintf (prompt, sizeof prompt, cp, user);
+ clear = getpass (prompt);
+ if (NULL == clear) {
+ static char c[1];
+
+ c[0] = '\0';
+ clear = c;
+ }
+ input = clear;
+ }
+
+ /*
+ * Convert the cleartext password into a ciphertext string.
+ * If the two match, the return value will be zero, which is
+ * SUCCESS. Otherwise we see if SKEY is being used and check
+ * the results there as well.
+ */
+
+ encrypted = pw_encrypt (input, cipher);
+ if (NULL != encrypted) {
+ retval = strcmp (encrypted, cipher);
+ } else {
+ retval = -1;
+ }
+
+#ifdef SKEY
+ /*
+ * If (1) The password fails to match, and
+ * (2) The password is empty and
+ * (3) We are using OPIE or S/Key, then
+ * ...Re-prompt, with echo on.
+ * -- AR 8/22/1999
+ */
+ if ((0 != retval) && ('\0' == input[0]) && use_skey) {
+ clear = getpass (prompt);
+ if (NULL == clear) {
+ static char c[1];
+
+ c[0] = '\0';
+ clear = c;
+ }
+ input = clear;
+ }
+
+ if ((0 != retval) && use_skey) {
+ int passcheck = -1;
+
+ if (skeyverify (&skey, input) == 0) {
+ passcheck = skey.n;
+ }
+ if (passcheck > 0) {
+ retval = 0;
+ }
+ }
+#endif
+
+ /*
+ * Things like RADIUS authentication may need the password -
+ * if the external variable wipe_clear_pass is zero, we will
+ * not wipe it (the caller should wipe clear_pass when it is
+ * no longer needed). --marekm
+ */
+
+ clear_pass = clear;
+ if (wipe_clear_pass && (NULL != clear) && ('\0' != *clear)) {
+ strzero (clear);
+ }
+ return retval;
+}
+#else /* !USE_PAM */
+extern int errno; /* warning: ANSI C forbids an empty source file */
+#endif /* !USE_PAM */
diff --git a/lib/pwauth.h b/lib/pwauth.h
new file mode 100644
index 0000000..d6c71dd
--- /dev/null
+++ b/lib/pwauth.h
@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) 1992 - 1993, Julianne Frances Haugh
+ * Copyright (c) 1996 - 1997, Marek Michałkiewicz
+ * Copyright (c) 2003 - 2005, Tomasz Kłoczko
+ * Copyright (c) 2009 , Nicolas François
+ * 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. The name of the copyright holders or contributors may not be used to
+ * endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 THE COPYRIGHT
+ * HOLDERS 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.
+ */
+
+/*
+ * $Id$
+ */
+
+#ifndef USE_PAM
+int pw_auth (const char *cipher,
+ const char *user,
+ int flag,
+ /*@null@*/const char *input);
+#endif /* !USE_PAM */
+
+/*
+ * Local access
+ */
+
+#define PW_SU 1
+#define PW_LOGIN 2
+
+/*
+ * Administrative functions
+ */
+
+#define PW_ADD 101
+#define PW_CHANGE 102
+#define PW_DELETE 103
+
+/*
+ * Network access
+ */
+
+#define PW_TELNET 201
+#define PW_RLOGIN 202
+#define PW_FTP 203
+#define PW_REXEC 204
diff --git a/lib/pwio.c b/lib/pwio.c
new file mode 100644
index 0000000..7ee8537
--- /dev/null
+++ b/lib/pwio.c
@@ -0,0 +1,226 @@
+/*
+ * Copyright (c) 1990 - 1994, Julianne Frances Haugh
+ * Copyright (c) 1996 - 2000, Marek Michałkiewicz
+ * Copyright (c) 2001 , Michał Moskal
+ * Copyright (c) 2003 - 2005, Tomasz Kłoczko
+ * Copyright (c) 2007 - 2009, Nicolas François
+ * 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. The name of the copyright holders or contributors may not be used to
+ * endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 THE COPYRIGHT
+ * HOLDERS 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.
+ */
+
+#include <config.h>
+
+#ident "$Id$"
+
+#include "prototypes.h"
+#include "defines.h"
+#include <pwd.h>
+#include <stdio.h>
+#include "commonio.h"
+#include "pwio.h"
+
+static /*@null@*/ /*@only@*/void *passwd_dup (const void *ent)
+{
+ const struct passwd *pw = ent;
+
+ return __pw_dup (pw);
+}
+
+static void passwd_free (/*@out@*/ /*@only@*/void *ent)
+{
+ struct passwd *pw = ent;
+
+ pw_free (pw);
+}
+
+static const char *passwd_getname (const void *ent)
+{
+ const struct passwd *pw = ent;
+
+ return pw->pw_name;
+}
+
+static void *passwd_parse (const char *line)
+{
+ return (void *) sgetpwent (line);
+}
+
+static int passwd_put (const void *ent, FILE * file)
+{
+ const struct passwd *pw = ent;
+
+ if ( (NULL == pw)
+ || (valid_field (pw->pw_name, ":\n") == -1)
+ || (valid_field (pw->pw_passwd, ":\n") == -1)
+ || (pw->pw_uid == (uid_t)-1)
+ || (pw->pw_gid == (gid_t)-1)
+ || (valid_field (pw->pw_gecos, ":\n") == -1)
+ || (valid_field (pw->pw_dir, ":\n") == -1)
+ || (valid_field (pw->pw_shell, ":\n") == -1)) {
+ return -1;
+ }
+
+ return (putpwent (pw, file) == -1) ? -1 : 0;
+}
+
+static struct commonio_ops passwd_ops = {
+ passwd_dup,
+ passwd_free,
+ passwd_getname,
+ passwd_parse,
+ passwd_put,
+ fgets,
+ fputs,
+ NULL, /* open_hook */
+ NULL /* close_hook */
+};
+
+static struct commonio_db passwd_db = {
+ PASSWD_FILE, /* filename */
+ &passwd_ops, /* ops */
+ NULL, /* fp */
+#ifdef WITH_SELINUX
+ NULL, /* scontext */
+#endif
+ 0644, /* st_mode */
+ 0, /* st_uid */
+ 0, /* st_gid */
+ NULL, /* head */
+ NULL, /* tail */
+ NULL, /* cursor */
+ false, /* changed */
+ false, /* isopen */
+ false, /* locked */
+ false /* readonly */
+};
+
+int pw_setdbname (const char *filename)
+{
+ return commonio_setname (&passwd_db, filename);
+}
+
+/*@observer@*/const char *pw_dbname (void)
+{
+ return passwd_db.filename;
+}
+
+int pw_lock (void)
+{
+ return commonio_lock (&passwd_db);
+}
+
+int pw_open (int mode)
+{
+ return commonio_open (&passwd_db, mode);
+}
+
+/*@observer@*/ /*@null@*/const struct passwd *pw_locate (const char *name)
+{
+ return commonio_locate (&passwd_db, name);
+}
+
+/*@observer@*/ /*@null@*/const struct passwd *pw_locate_uid (uid_t uid)
+{
+ const struct passwd *pwd;
+
+ pw_rewind ();
+ while ( ((pwd = pw_next ()) != NULL)
+ && (pwd->pw_uid != uid)) {
+ }
+
+ return pwd;
+}
+
+int pw_update (const struct passwd *pw)
+{
+ return commonio_update (&passwd_db, (const void *) pw);
+}
+
+int pw_remove (const char *name)
+{
+ return commonio_remove (&passwd_db, name);
+}
+
+int pw_rewind (void)
+{
+ return commonio_rewind (&passwd_db);
+}
+
+/*@observer@*/ /*@null@*/const struct passwd *pw_next (void)
+{
+ return commonio_next (&passwd_db);
+}
+
+int pw_close (void)
+{
+ return commonio_close (&passwd_db);
+}
+
+int pw_unlock (void)
+{
+ return commonio_unlock (&passwd_db);
+}
+
+/*@null@*/struct commonio_entry *__pw_get_head (void)
+{
+ return passwd_db.head;
+}
+
+void __pw_del_entry (const struct commonio_entry *ent)
+{
+ commonio_del_entry (&passwd_db, ent);
+}
+
+struct commonio_db *__pw_get_db (void)
+{
+ return &passwd_db;
+}
+
+static int pw_cmp (const void *p1, const void *p2)
+{
+ uid_t u1, u2;
+
+ if ((*(struct commonio_entry **) p1)->eptr == NULL)
+ return 1;
+ if ((*(struct commonio_entry **) p2)->eptr == NULL)
+ return -1;
+
+ u1 = ((struct passwd *) (*(struct commonio_entry **) p1)->eptr)->pw_uid;
+ u2 = ((struct passwd *) (*(struct commonio_entry **) p2)->eptr)->pw_uid;
+
+ if (u1 < u2)
+ return -1;
+ else if (u1 > u2)
+ return 1;
+ else
+ return 0;
+}
+
+/* Sort entries by UID */
+int pw_sort ()
+{
+ return commonio_sort (&passwd_db, pw_cmp);
+}
diff --git a/lib/pwio.h b/lib/pwio.h
new file mode 100644
index 0000000..2db85e0
--- /dev/null
+++ b/lib/pwio.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 1990 - 1994, Julianne Frances Haugh
+ * Copyright (c) 1996 - 2000, Marek Michałkiewicz
+ * Copyright (c) 2005 , Michał Moskal
+ * Copyright (c) 2005 , Tomasz Kłoczko
+ * Copyright (c) 2008 , Nicolas François
+ * 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. The name of the copyright holders or contributors may not be used to
+ * endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 THE COPYRIGHT
+ * HOLDERS 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.
+ */
+
+/* $Id$ */
+#ifndef _PWIO_H
+#define _PWIO_H
+
+#include <sys/types.h>
+#include <pwd.h>
+
+extern int pw_close (void);
+extern /*@observer@*/ /*@null@*/const struct passwd *pw_locate (const char *name);
+extern /*@observer@*/ /*@null@*/const struct passwd *pw_locate_uid (uid_t uid);
+extern int pw_lock (void);
+extern int pw_setdbname (const char *filename);
+extern /*@observer@*/const char *pw_dbname (void);
+extern /*@observer@*/ /*@null@*/const struct passwd *pw_next (void);
+extern int pw_open (int mode);
+extern int pw_remove (const char *name);
+extern int pw_rewind (void);
+extern int pw_unlock (void);
+extern int pw_update (const struct passwd *pw);
+extern int pw_sort (void);
+
+#endif
diff --git a/lib/pwmem.c b/lib/pwmem.c
new file mode 100644
index 0000000..17d2eb2
--- /dev/null
+++ b/lib/pwmem.c
@@ -0,0 +1,106 @@
+/*
+ * Copyright (c) 1990 - 1994, Julianne Frances Haugh
+ * Copyright (c) 1996 - 2000, Marek Michałkiewicz
+ * Copyright (c) 2001 , Michał Moskal
+ * Copyright (c) 2003 - 2005, Tomasz Kłoczko
+ * Copyright (c) 2007 - 2013, Nicolas François
+ * 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. The name of the copyright holders or contributors may not be used to
+ * endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 THE COPYRIGHT
+ * HOLDERS 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.
+ */
+
+#include <config.h>
+
+#ident "$Id$"
+
+#include <stdio.h>
+#include "defines.h"
+#include "prototypes.h"
+#include "pwio.h"
+
+/*@null@*/ /*@only@*/struct passwd *__pw_dup (const struct passwd *pwent)
+{
+ struct passwd *pw;
+
+ pw = (struct passwd *) malloc (sizeof *pw);
+ if (NULL == pw) {
+ return NULL;
+ }
+ /* The libc might define other fields. They won't be copied. */
+ memset (pw, 0, sizeof *pw);
+ pw->pw_uid = pwent->pw_uid;
+ pw->pw_gid = pwent->pw_gid;
+ /*@-mustfreeonly@*/
+ pw->pw_name = strdup (pwent->pw_name);
+ /*@=mustfreeonly@*/
+ if (NULL == pw->pw_name) {
+ pw_free(pw);
+ return NULL;
+ }
+ /*@-mustfreeonly@*/
+ pw->pw_passwd = strdup (pwent->pw_passwd);
+ /*@=mustfreeonly@*/
+ if (NULL == pw->pw_passwd) {
+ pw_free(pw);
+ return NULL;
+ }
+ /*@-mustfreeonly@*/
+ pw->pw_gecos = strdup (pwent->pw_gecos);
+ /*@=mustfreeonly@*/
+ if (NULL == pw->pw_gecos) {
+ pw_free(pw);
+ return NULL;
+ }
+ /*@-mustfreeonly@*/
+ pw->pw_dir = strdup (pwent->pw_dir);
+ /*@=mustfreeonly@*/
+ if (NULL == pw->pw_dir) {
+ pw_free(pw);
+ return NULL;
+ }
+ /*@-mustfreeonly@*/
+ pw->pw_shell = strdup (pwent->pw_shell);
+ /*@=mustfreeonly@*/
+ if (NULL == pw->pw_shell) {
+ pw_free(pw);
+ return NULL;
+ }
+
+ return pw;
+}
+
+void pw_free (/*@out@*/ /*@only@*/struct passwd *pwent)
+{
+ free (pwent->pw_name);
+ if (pwent->pw_passwd) {
+ memzero (pwent->pw_passwd, strlen (pwent->pw_passwd));
+ free (pwent->pw_passwd);
+ }
+ free (pwent->pw_gecos);
+ free (pwent->pw_dir);
+ free (pwent->pw_shell);
+ free (pwent);
+}
+
diff --git a/lib/selinux.c b/lib/selinux.c
new file mode 100644
index 0000000..d751329
--- /dev/null
+++ b/lib/selinux.c
@@ -0,0 +1,103 @@
+/*
+ * Copyright (c) 2011 , Peter Vrabec <pvrabec@redhat.com>
+ * 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. The name of the copyright holders or contributors may not be used to
+ * endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 THE COPYRIGHT
+ * HOLDERS 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.
+ */
+
+#include <config.h>
+
+#ifdef WITH_SELINUX
+
+#include "defines.h"
+
+#include <selinux/selinux.h>
+#include "prototypes.h"
+
+
+static bool selinux_checked = false;
+static bool selinux_enabled;
+
+/*
+ * set_selinux_file_context - Set the security context before any file or
+ * directory creation.
+ *
+ * set_selinux_file_context () should be called before any creation
+ * of file, symlink, directory, ...
+ *
+ * Callers may have to Reset SELinux to create files with default
+ * contexts with reset_selinux_file_context
+ */
+int set_selinux_file_context (const char *dst_name)
+{
+ /*@null@*/security_context_t scontext = NULL;
+
+ if (!selinux_checked) {
+ selinux_enabled = is_selinux_enabled () > 0;
+ selinux_checked = true;
+ }
+
+ if (selinux_enabled) {
+ /* Get the default security context for this file */
+ if (matchpathcon (dst_name, 0, &scontext) < 0) {
+ if (security_getenforce () != 0) {
+ return 1;
+ }
+ }
+ /* Set the security context for the next created file */
+ if (setfscreatecon (scontext) < 0) {
+ if (security_getenforce () != 0) {
+ return 1;
+ }
+ }
+ freecon (scontext);
+ }
+ return 0;
+}
+
+/*
+ * reset_selinux_file_context - Reset the security context to the default
+ * policy behavior
+ *
+ * reset_selinux_file_context () should be called after the context
+ * was changed with set_selinux_file_context ()
+ */
+int reset_selinux_file_context (void)
+{
+ if (!selinux_checked) {
+ selinux_enabled = is_selinux_enabled () > 0;
+ selinux_checked = true;
+ }
+ if (selinux_enabled) {
+ if (setfscreatecon (NULL) != 0) {
+ return 1;
+ }
+ }
+ return 0;
+}
+
+#else /* !WITH_SELINUX */
+extern int errno; /* warning: ANSI C forbids an empty source file */
+#endif /* !WITH_SELINUX */
diff --git a/lib/semanage.c b/lib/semanage.c
new file mode 100644
index 0000000..e983f5f
--- /dev/null
+++ b/lib/semanage.c
@@ -0,0 +1,378 @@
+/*
+ * Copyright (c) 2010 , Jakub Hrozek <jhrozek@redhat.com>
+ * Copyright (c) 2011 , Peter Vrabec <pvrabec@redhat.com>
+ * 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. The name of the copyright holders or contributors may not be used to
+ * endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 THE COPYRIGHT
+ * HOLDERS 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.
+ */
+
+#include <config.h>
+
+#ifdef WITH_SELINUX
+
+#include "defines.h"
+
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE
+#endif
+#include <stdio.h>
+#include <stdarg.h>
+#include <selinux/selinux.h>
+#include <semanage/semanage.h>
+#include "prototypes.h"
+
+
+#ifndef DEFAULT_SERANGE
+#define DEFAULT_SERANGE "s0"
+#endif
+
+
+static void semanage_error_callback (unused void *varg,
+ semanage_handle_t *handle,
+ const char *fmt, ...)
+{
+ int ret;
+ char * message = NULL;
+ va_list ap;
+
+
+ va_start (ap, fmt);
+ ret = vasprintf (&message, fmt, ap);
+ va_end (ap);
+ if (ret < 0) {
+ /* ENOMEM */
+ return;
+ }
+
+ switch (semanage_msg_get_level (handle)) {
+ case SEMANAGE_MSG_ERR:
+ case SEMANAGE_MSG_WARN:
+ fprintf (stderr, _("[libsemanage]: %s\n"), message);
+ break;
+ case SEMANAGE_MSG_INFO:
+ /* nop */
+ break;
+ }
+
+ free (message);
+}
+
+
+static semanage_handle_t *semanage_init (void)
+{
+ int ret;
+ semanage_handle_t *handle = NULL;
+
+ handle = semanage_handle_create ();
+ if (NULL == handle) {
+ fprintf (stderr,
+ _("Cannot create SELinux management handle\n"));
+ return NULL;
+ }
+
+ semanage_msg_set_callback (handle, semanage_error_callback, NULL);
+
+ ret = semanage_is_managed (handle);
+ if (ret != 1) {
+ fprintf (stderr, _("SELinux policy not managed\n"));
+ goto fail;
+ }
+
+ ret = semanage_access_check (handle);
+ if (ret < SEMANAGE_CAN_READ) {
+ fprintf (stderr, _("Cannot read SELinux policy store\n"));
+ goto fail;
+ }
+
+ ret = semanage_connect (handle);
+ if (ret != 0) {
+ fprintf (stderr,
+ _("Cannot establish SELinux management connection\n"));
+ goto fail;
+ }
+
+ ret = semanage_begin_transaction (handle);
+ if (ret != 0) {
+ fprintf (stderr, _("Cannot begin SELinux transaction\n"));
+ goto fail;
+ }
+
+ return handle;
+
+fail:
+ semanage_handle_destroy (handle);
+ return NULL;
+}
+
+
+static int semanage_user_mod (semanage_handle_t *handle,
+ semanage_seuser_key_t *key,
+ const char *login_name,
+ const char *seuser_name)
+{
+ int ret;
+ semanage_seuser_t *seuser = NULL;
+
+ semanage_seuser_query (handle, key, &seuser);
+ if (NULL == seuser) {
+ fprintf (stderr,
+ _("Could not query seuser for %s\n"), login_name);
+ ret = 1;
+ goto done;
+ }
+
+ ret = semanage_seuser_set_mlsrange (handle, seuser, DEFAULT_SERANGE);
+ if (ret != 0) {
+ fprintf (stderr,
+ _("Could not set serange for %s\n"), login_name);
+ ret = 1;
+ goto done;
+ }
+
+ ret = semanage_seuser_set_sename (handle, seuser, seuser_name);
+ if (ret != 0) {
+ fprintf (stderr,
+ _("Could not set sename for %s\n"),
+ login_name);
+ ret = 1;
+ goto done;
+ }
+
+ ret = semanage_seuser_modify_local (handle, key, seuser);
+ if (ret != 0) {
+ fprintf (stderr,
+ _("Could not modify login mapping for %s\n"),
+ login_name);
+ ret = 1;
+ goto done;
+ }
+
+ ret = 0;
+done:
+ semanage_seuser_free (seuser);
+ return ret;
+}
+
+
+static int semanage_user_add (semanage_handle_t *handle,
+ semanage_seuser_key_t *key,
+ const char *login_name,
+ const char *seuser_name)
+{
+ int ret;
+ semanage_seuser_t *seuser = NULL;
+
+ ret = semanage_seuser_create (handle, &seuser);
+ if (ret != 0) {
+ fprintf (stderr,
+ _("Cannot create SELinux login mapping for %s\n"),
+ login_name);
+ ret = 1;
+ goto done;
+ }
+
+ ret = semanage_seuser_set_name (handle, seuser, login_name);
+ if (ret != 0) {
+ fprintf (stderr, _("Could not set name for %s\n"), login_name);
+ ret = 1;
+ goto done;
+ }
+
+ ret = semanage_seuser_set_mlsrange (handle, seuser, DEFAULT_SERANGE);
+ if (ret != 0) {
+ fprintf (stderr,
+ _("Could not set serange for %s\n"),
+ login_name);
+ ret = 1;
+ goto done;
+ }
+
+ ret = semanage_seuser_set_sename (handle, seuser, seuser_name);
+ if (ret != 0) {
+ fprintf (stderr,
+ _("Could not set SELinux user for %s\n"),
+ login_name);
+ ret = 1;
+ goto done;
+ }
+
+ ret = semanage_seuser_modify_local (handle, key, seuser);
+ if (ret != 0) {
+ fprintf (stderr,
+ _("Could not add login mapping for %s\n"),
+ login_name);
+ ret = 1;
+ goto done;
+ }
+
+ ret = 0;
+done:
+ semanage_seuser_free (seuser);
+ return ret;
+}
+
+
+int set_seuser (const char *login_name, const char *seuser_name)
+{
+ semanage_handle_t *handle = NULL;
+ semanage_seuser_key_t *key = NULL;
+ int ret;
+ int seuser_exists = 0;
+
+ if (NULL == seuser_name) {
+ /* don't care, just let system pick the defaults */
+ return 0;
+ }
+
+ handle = semanage_init ();
+ if (NULL == handle) {
+ fprintf (stderr, _("Cannot init SELinux management\n"));
+ ret = 1;
+ goto done;
+ }
+
+ ret = semanage_seuser_key_create (handle, login_name, &key);
+ if (ret != 0) {
+ fprintf (stderr, _("Cannot create SELinux user key\n"));
+ ret = 1;
+ goto done;
+ }
+
+ ret = semanage_seuser_exists (handle, key, &seuser_exists);
+ if (ret < 0) {
+ fprintf (stderr, _("Cannot verify the SELinux user\n"));
+ ret = 1;
+ goto done;
+ }
+
+ if (0 != seuser_exists) {
+ ret = semanage_user_mod (handle, key, login_name, seuser_name);
+ if (ret != 0) {
+ fprintf (stderr,
+ _("Cannot modify SELinux user mapping\n"));
+ ret = 1;
+ goto done;
+ }
+ } else {
+ ret = semanage_user_add (handle, key, login_name, seuser_name);
+ if (ret != 0) {
+ fprintf (stderr,
+ _("Cannot add SELinux user mapping\n"));
+ ret = 1;
+ goto done;
+ }
+ }
+
+ ret = semanage_commit (handle);
+ if (ret < 0) {
+ fprintf (stderr, _("Cannot commit SELinux transaction\n"));
+ ret = 1;
+ goto done;
+ }
+
+ ret = 0;
+
+done:
+ semanage_seuser_key_free (key);
+ semanage_handle_destroy (handle);
+ return ret;
+}
+
+
+int del_seuser (const char *login_name)
+{
+ semanage_handle_t *handle = NULL;
+ semanage_seuser_key_t *key = NULL;
+ int ret;
+ int exists = 0;
+
+ handle = semanage_init ();
+ if (NULL == handle) {
+ fprintf (stderr, _("Cannot init SELinux management\n"));
+ ret = 1;
+ goto done;
+ }
+
+ ret = semanage_seuser_key_create (handle, login_name, &key);
+ if (ret != 0) {
+ fprintf (stderr, _("Cannot create SELinux user key\n"));
+ ret = 1;
+ goto done;
+ }
+
+ ret = semanage_seuser_exists (handle, key, &exists);
+ if (ret < 0) {
+ fprintf (stderr, _("Cannot verify the SELinux user\n"));
+ ret = 1;
+ goto done;
+ }
+
+ if (0 == exists) {
+ fprintf (stderr,
+ _("Login mapping for %s is not defined, OK if default mapping was used\n"),
+ login_name);
+ ret = 0; /* probably default mapping */
+ goto done;
+ }
+
+ ret = semanage_seuser_exists_local (handle, key, &exists);
+ if (ret < 0) {
+ fprintf (stderr, _("Cannot verify the SELinux user\n"));
+ ret = 1;
+ goto done;
+ }
+
+ if (0 == exists) {
+ fprintf (stderr,
+ _("Login mapping for %s is defined in policy, cannot be deleted\n"),
+ login_name);
+ ret = 0; /* Login mapping defined in policy can't be deleted */
+ goto done;
+ }
+
+ ret = semanage_seuser_del_local (handle, key);
+ if (ret != 0) {
+ fprintf (stderr,
+ _("Could not delete login mapping for %s"),
+ login_name);
+ ret = 1;
+ goto done;
+ }
+
+ ret = semanage_commit (handle);
+ if (ret < 0) {
+ fprintf (stderr, _("Cannot commit SELinux transaction\n"));
+ ret = 1;
+ goto done;
+ }
+
+ ret = 0;
+done:
+ semanage_handle_destroy (handle);
+ return ret;
+}
+#else /* !WITH_SELINUX */
+extern int errno; /* warning: ANSI C forbids an empty source file */
+#endif /* !WITH_SELINUX */
diff --git a/lib/sgetgrent.c b/lib/sgetgrent.c
new file mode 100644
index 0000000..206f1c5
--- /dev/null
+++ b/lib/sgetgrent.c
@@ -0,0 +1,154 @@
+/*
+ * Copyright (c) 1990 - 1994, Julianne Frances Haugh
+ * Copyright (c) 1996 - 1998, Marek Michałkiewicz
+ * Copyright (c) 2005 , Tomasz Kłoczko
+ * Copyright (c) 2008 , Nicolas François
+ * 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. The name of the copyright holders or contributors may not be used to
+ * endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 THE COPYRIGHT
+ * HOLDERS 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.
+ */
+
+#include <config.h>
+
+#ident "$Id$"
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <grp.h>
+#include "defines.h"
+#include "prototypes.h"
+
+#define NFIELDS 4
+
+/*
+ * list - turn a comma-separated string into an array of (char *)'s
+ *
+ * list() converts the comma-separated list of member names into
+ * an array of character pointers.
+ *
+ * WARNING: I profiled this once with and without strchr() calls
+ * and found that using a register variable and an explicit loop
+ * works best. For large /etc/group files, this is a major win.
+ *
+ * FINALLY added dynamic allocation. Still need to fix sgetsgent().
+ * --marekm
+ */
+static char **list (char *s)
+{
+ static char **members = 0;
+ static int size = 0; /* max members + 1 */
+ int i;
+ char **rbuf;
+
+ i = 0;
+ for (;;) {
+ /* check if there is room for another pointer (to a group
+ member name, or terminating NULL). */
+ if (i >= size) {
+ size = i + 100; /* at least: i + 1 */
+ if (members) {
+ rbuf =
+ realloc (members, size * sizeof (char *));
+ } else {
+ /* for old (before ANSI C) implementations of
+ realloc() that don't handle NULL properly */
+ rbuf = malloc (size * sizeof (char *));
+ }
+ if (!rbuf) {
+ if (members)
+ free (members);
+ members = 0;
+ size = 0;
+ return (char **) 0;
+ }
+ members = rbuf;
+ }
+ if (!s || s[0] == '\0')
+ break;
+ members[i++] = s;
+ while (('\0' != *s) && (',' != *s)) {
+ s++;
+ }
+ if ('\0' != *s) {
+ *s++ = '\0';
+ }
+ }
+ members[i] = (char *) 0;
+ return members;
+}
+
+
+struct group *sgetgrent (const char *buf)
+{
+ static char *grpbuf = 0;
+ static size_t size = 0;
+ static char *grpfields[NFIELDS];
+ static struct group grent;
+ int i;
+ char *cp;
+
+ if (strlen (buf) + 1 > size) {
+ /* no need to use realloc() here - just free it and
+ allocate a larger block */
+ if (grpbuf)
+ free (grpbuf);
+ size = strlen (buf) + 1000; /* at least: strlen(buf) + 1 */
+ grpbuf = malloc (size);
+ if (!grpbuf) {
+ size = 0;
+ return 0;
+ }
+ }
+ strcpy (grpbuf, buf);
+
+ cp = strrchr (grpbuf, '\n');
+ if (NULL != cp) {
+ *cp = '\0';
+ }
+
+ for (cp = grpbuf, i = 0; (i < NFIELDS) && (NULL != cp); i++) {
+ grpfields[i] = cp;
+ cp = strchr (cp, ':');
+ if (NULL != cp) {
+ *cp = '\0';
+ cp++;
+ }
+ }
+ if (i < (NFIELDS - 1) || *grpfields[2] == '\0') {
+ return (struct group *) 0;
+ }
+ grent.gr_name = grpfields[0];
+ grent.gr_passwd = grpfields[1];
+ if (get_gid (grpfields[2], &grent.gr_gid) == 0) {
+ return (struct group *) 0;
+ }
+ grent.gr_mem = list (grpfields[3]);
+ if (NULL == grent.gr_mem) {
+ return (struct group *) 0; /* out of memory */
+ }
+
+ return &grent;
+}
+
diff --git a/lib/sgetpwent.c b/lib/sgetpwent.c
new file mode 100644
index 0000000..293aabe
--- /dev/null
+++ b/lib/sgetpwent.c
@@ -0,0 +1,122 @@
+/*
+ * Copyright (c) 1989 - 1994, Julianne Frances Haugh
+ * Copyright (c) 1996 - 1998, Marek Michałkiewicz
+ * Copyright (c) 2003 - 2005, Tomasz Kłoczko
+ * Copyright (c) 2008 , Nicolas François
+ * 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. The name of the copyright holders or contributors may not be used to
+ * endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 THE COPYRIGHT
+ * HOLDERS 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.
+ */
+
+#include <config.h>
+
+#ident "$Id$"
+
+#include <sys/types.h>
+#include "defines.h"
+#include <stdio.h>
+#include <pwd.h>
+#include "prototypes.h"
+
+#define NFIELDS 7
+
+/*
+ * sgetpwent - convert a string to a (struct passwd)
+ *
+ * sgetpwent() parses a string into the parts required for a password
+ * structure. Strict checking is made for the UID and GID fields and
+ * presence of the correct number of colons. Any failing tests result
+ * in a NULL pointer being returned.
+ *
+ * NOTE: This function uses hard-coded string scanning functions for
+ * performance reasons. I am going to come up with some conditional
+ * compilation glarp to improve on this in the future.
+ */
+struct passwd *sgetpwent (const char *buf)
+{
+ static struct passwd pwent;
+ static char pwdbuf[1024];
+ register int i;
+ register char *cp;
+ char *fields[NFIELDS];
+
+ /*
+ * Copy the string to a static buffer so the pointers into
+ * the password structure remain valid.
+ */
+
+ if (strlen (buf) >= sizeof pwdbuf)
+ return 0; /* fail if too long */
+ strcpy (pwdbuf, buf);
+
+ /*
+ * Save a pointer to the start of each colon separated
+ * field. The fields are converted into NUL terminated strings.
+ */
+
+ for (cp = pwdbuf, i = 0; (i < NFIELDS) && (NULL != cp); i++) {
+ fields[i] = cp;
+ while (('\0' != *cp) && (':' != *cp)) {
+ cp++;
+ }
+
+ if ('\0' != *cp) {
+ *cp = '\0';
+ cp++;
+ } else {
+ cp = NULL;
+ }
+ }
+
+ /*
+ * There must be exactly NFIELDS colon separated fields or
+ * the entry is invalid. Also, the UID and GID must be non-blank.
+ */
+
+ if (i != NFIELDS || *fields[2] == '\0' || *fields[3] == '\0')
+ return NULL;
+
+ /*
+ * Each of the fields is converted the appropriate data type
+ * and the result assigned to the password structure. If the
+ * UID or GID does not convert to an integer value, a NULL
+ * pointer is returned.
+ */
+
+ pwent.pw_name = fields[0];
+ pwent.pw_passwd = fields[1];
+ if (get_uid (fields[2], &pwent.pw_uid) == 0) {
+ return NULL;
+ }
+ if (get_gid (fields[3], &pwent.pw_gid) == 0) {
+ return NULL;
+ }
+ pwent.pw_gecos = fields[4];
+ pwent.pw_dir = fields[5];
+ pwent.pw_shell = fields[6];
+
+ return &pwent;
+}
+
diff --git a/lib/sgetspent.c b/lib/sgetspent.c
new file mode 100644
index 0000000..20531eb
--- /dev/null
+++ b/lib/sgetspent.c
@@ -0,0 +1,208 @@
+/*
+ * Copyright (c) 1989 - 1994, Julianne Frances Haugh
+ * Copyright (c) 1996 - 1998, Marek Michałkiewicz
+ * Copyright (c) 2003 - 2005, Tomasz Kłoczko
+ * Copyright (c) 2009 , Nicolas François
+ * 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. The name of the copyright holders or contributors may not be used to
+ * endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 THE COPYRIGHT
+ * HOLDERS 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.
+ */
+
+#include <config.h>
+
+/* Newer versions of Linux libc already have shadow support. */
+#ifndef HAVE_SGETSPENT
+
+#ident "$Id$"
+
+#include <sys/types.h>
+#include "prototypes.h"
+#include "defines.h"
+#include <stdio.h>
+#define FIELDS 9
+#define OFIELDS 5
+/*
+ * sgetspent - convert string in shadow file format to (struct spwd *)
+ */
+struct spwd *sgetspent (const char *string)
+{
+ static char spwbuf[1024];
+ static struct spwd spwd;
+ char *fields[FIELDS];
+ char *cp;
+ char *cpp;
+ int i;
+
+ /*
+ * Copy string to local buffer. It has to be tokenized and we
+ * have to do that to our private copy.
+ */
+
+ if (strlen (string) >= sizeof spwbuf) {
+ return 0; /* fail if too long */
+ }
+ strcpy (spwbuf, string);
+
+ cp = strrchr (spwbuf, '\n');
+ if (NULL != cp) {
+ *cp = '\0';
+ }
+
+ /*
+ * Tokenize the string into colon separated fields. Allow up to
+ * FIELDS different fields.
+ */
+
+ for (cp = spwbuf, i = 0; ('\0' != *cp) && (i < FIELDS); i++) {
+ fields[i] = cp;
+ while (('\0' != *cp) && (':' != *cp)) {
+ cp++;
+ }
+
+ if ('\0' != *cp) {
+ *cp = '\0';
+ cp++;
+ }
+ }
+
+ if (i == (FIELDS - 1)) {
+ fields[i++] = cp;
+ }
+
+ if ( ((NULL != cp) && ('\0' != *cp)) ||
+ ((i != FIELDS) && (i != OFIELDS)) ) {
+ return 0;
+ }
+
+ /*
+ * Start populating the structure. The fields are all in
+ * static storage, as is the structure we pass back.
+ */
+
+ spwd.sp_namp = fields[0];
+ spwd.sp_pwdp = fields[1];
+
+ /*
+ * Get the last changed date. For all of the integer fields,
+ * we check for proper format. It is an error to have an
+ * incorrectly formatted number.
+ */
+
+ if (fields[2][0] == '\0') {
+ spwd.sp_lstchg = -1;
+ } else if ( (getlong (fields[2], &spwd.sp_lstchg) == 0)
+ || (spwd.sp_lstchg < 0)) {
+ return 0;
+ }
+
+ /*
+ * Get the minimum period between password changes.
+ */
+
+ if (fields[3][0] == '\0') {
+ spwd.sp_min = -1;
+ } else if ( (getlong (fields[3], &spwd.sp_min) == 0)
+ || (spwd.sp_min < 0)) {
+ return 0;
+ }
+
+ /*
+ * Get the maximum number of days a password is valid.
+ */
+
+ if (fields[4][0] == '\0') {
+ spwd.sp_max = -1;
+ } else if ( (getlong (fields[4], &spwd.sp_max) == 0)
+ || (spwd.sp_max < 0)) {
+ return 0;
+ }
+
+ /*
+ * If there are only OFIELDS fields (this is a SVR3.2 /etc/shadow
+ * formatted file), initialize the other field members to -1.
+ */
+
+ if (i == OFIELDS) {
+ spwd.sp_warn = -1;
+ spwd.sp_inact = -1;
+ spwd.sp_expire = -1;
+ spwd.sp_flag = SHADOW_SP_FLAG_UNSET;
+
+ return &spwd;
+ }
+
+ /*
+ * Get the number of days of password expiry warning.
+ */
+
+ if (fields[5][0] == '\0') {
+ spwd.sp_warn = -1;
+ } else if ( (getlong (fields[5], &spwd.sp_warn) == 0)
+ || (spwd.sp_warn < 0)) {
+ return 0;
+ }
+
+ /*
+ * Get the number of days of inactivity before an account is
+ * disabled.
+ */
+
+ if (fields[6][0] == '\0') {
+ spwd.sp_inact = -1;
+ } else if ( (getlong (fields[6], &spwd.sp_inact) == 0)
+ || (spwd.sp_inact < 0)) {
+ return 0;
+ }
+
+ /*
+ * Get the number of days after the epoch before the account is
+ * set to expire.
+ */
+
+ if (fields[7][0] == '\0') {
+ spwd.sp_expire = -1;
+ } else if ( (getlong (fields[7], &spwd.sp_expire) == 0)
+ || (spwd.sp_expire < 0)) {
+ return 0;
+ }
+
+ /*
+ * This field is reserved for future use. But it isn't supposed
+ * to have anything other than a valid integer in it.
+ */
+
+ if (fields[8][0] == '\0') {
+ spwd.sp_flag = SHADOW_SP_FLAG_UNSET;
+ } else if (getlong (fields[8], &spwd.sp_flag) == 0) {
+ /* FIXME: add a getulong function */
+ return 0;
+ }
+
+ return (&spwd);
+}
+#else
+extern int errno; /* warning: ANSI C forbids an empty source file */
+#endif
+
diff --git a/lib/sgroupio.c b/lib/sgroupio.c
new file mode 100644
index 0000000..5423626
--- /dev/null
+++ b/lib/sgroupio.c
@@ -0,0 +1,328 @@
+/*
+ * Copyright (c) 1990 - 1994, Julianne Frances Haugh
+ * Copyright (c) 1996 - 2000, Marek Michałkiewicz
+ * Copyright (c) 2001 , Michał Moskal
+ * Copyright (c) 2005 , Tomasz Kłoczko
+ * Copyright (c) 2007 - 2013, Nicolas François
+ * 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. The name of the copyright holders or contributors may not be used to
+ * endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 THE COPYRIGHT
+ * HOLDERS 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.
+ */
+
+#include <config.h>
+
+#ifdef SHADOWGRP
+
+#ident "$Id$"
+
+#include "prototypes.h"
+#include "defines.h"
+#include "commonio.h"
+#include "getdef.h"
+#include "sgroupio.h"
+
+/*@null@*/ /*@only@*/struct sgrp *__sgr_dup (const struct sgrp *sgent)
+{
+ struct sgrp *sg;
+ int i;
+
+ sg = (struct sgrp *) malloc (sizeof *sg);
+ if (NULL == sg) {
+ return NULL;
+ }
+ /* Do the same as the other _dup function, even if we know the
+ * structure. */
+ memset (sg, 0, sizeof *sg);
+ /*@-mustfreeonly@*/
+ sg->sg_name = strdup (sgent->sg_name);
+ /*@=mustfreeonly@*/
+ if (NULL == sg->sg_name) {
+ free (sg);
+ return NULL;
+ }
+ /*@-mustfreeonly@*/
+ sg->sg_passwd = strdup (sgent->sg_passwd);
+ /*@=mustfreeonly@*/
+ if (NULL == sg->sg_passwd) {
+ free (sg->sg_name);
+ free (sg);
+ return NULL;
+ }
+
+ for (i = 0; NULL != sgent->sg_adm[i]; i++);
+ /*@-mustfreeonly@*/
+ sg->sg_adm = (char **) malloc ((i + 1) * sizeof (char *));
+ /*@=mustfreeonly@*/
+ if (NULL == sg->sg_adm) {
+ free (sg->sg_passwd);
+ free (sg->sg_name);
+ free (sg);
+ return NULL;
+ }
+ for (i = 0; NULL != sgent->sg_adm[i]; i++) {
+ sg->sg_adm[i] = strdup (sgent->sg_adm[i]);
+ if (NULL == sg->sg_adm[i]) {
+ for (i = 0; NULL != sg->sg_adm[i]; i++) {
+ free (sg->sg_adm[i]);
+ }
+ free (sg->sg_adm);
+ free (sg->sg_passwd);
+ free (sg->sg_name);
+ free (sg);
+ return NULL;
+ }
+ }
+ sg->sg_adm[i] = NULL;
+
+ for (i = 0; NULL != sgent->sg_mem[i]; i++);
+ /*@-mustfreeonly@*/
+ sg->sg_mem = (char **) malloc ((i + 1) * sizeof (char *));
+ /*@=mustfreeonly@*/
+ if (NULL == sg->sg_mem) {
+ for (i = 0; NULL != sg->sg_adm[i]; i++) {
+ free (sg->sg_adm[i]);
+ }
+ free (sg->sg_adm);
+ free (sg->sg_passwd);
+ free (sg->sg_name);
+ free (sg);
+ return NULL;
+ }
+ for (i = 0; NULL != sgent->sg_mem[i]; i++) {
+ sg->sg_mem[i] = strdup (sgent->sg_mem[i]);
+ if (NULL == sg->sg_mem[i]) {
+ for (i = 0; NULL != sg->sg_mem[i]; i++) {
+ free (sg->sg_mem[i]);
+ }
+ free (sg->sg_mem);
+ for (i = 0; NULL != sg->sg_adm[i]; i++) {
+ free (sg->sg_adm[i]);
+ }
+ free (sg->sg_adm);
+ free (sg->sg_passwd);
+ free (sg->sg_name);
+ free (sg);
+ return NULL;
+ }
+ }
+ sg->sg_mem[i] = NULL;
+
+ return sg;
+}
+
+static /*@null@*/ /*@only@*/void *gshadow_dup (const void *ent)
+{
+ const struct sgrp *sg = ent;
+
+ return __sgr_dup (sg);
+}
+
+static void gshadow_free (/*@out@*/ /*@only@*/void *ent)
+{
+ struct sgrp *sg = ent;
+
+ sgr_free (sg);
+}
+
+void sgr_free (/*@out@*/ /*@only@*/struct sgrp *sgent)
+{
+ size_t i;
+ free (sgent->sg_name);
+ if (NULL != sgent->sg_passwd) {
+ memzero (sgent->sg_passwd, strlen (sgent->sg_passwd));
+ free (sgent->sg_passwd);
+ }
+ for (i = 0; NULL != sgent->sg_adm[i]; i++) {
+ free (sgent->sg_adm[i]);
+ }
+ free (sgent->sg_adm);
+ for (i = 0; NULL != sgent->sg_mem[i]; i++) {
+ free (sgent->sg_mem[i]);
+ }
+ free (sgent->sg_mem);
+ free (sgent);
+}
+
+static const char *gshadow_getname (const void *ent)
+{
+ const struct sgrp *gr = ent;
+
+ return gr->sg_name;
+}
+
+static void *gshadow_parse (const char *line)
+{
+ return (void *) sgetsgent (line);
+}
+
+static int gshadow_put (const void *ent, FILE * file)
+{
+ const struct sgrp *sg = ent;
+
+ if ( (NULL == sg)
+ || (valid_field (sg->sg_name, ":\n") == -1)
+ || (valid_field (sg->sg_passwd, ":\n") == -1)) {
+ return -1;
+ }
+
+ /* FIXME: fail also if sg->sg_adm == NULL ?*/
+ if (NULL != sg->sg_adm) {
+ size_t i;
+ for (i = 0; NULL != sg->sg_adm[i]; i++) {
+ if (valid_field (sg->sg_adm[i], ",:\n") == -1) {
+ return -1;
+ }
+ }
+ }
+
+ /* FIXME: fail also if sg->sg_mem == NULL ?*/
+ if (NULL != sg->sg_mem) {
+ size_t i;
+ for (i = 0; NULL != sg->sg_mem[i]; i++) {
+ if (valid_field (sg->sg_mem[i], ",:\n") == -1) {
+ return -1;
+ }
+ }
+ }
+
+ return (putsgent (sg, file) == -1) ? -1 : 0;
+}
+
+static struct commonio_ops gshadow_ops = {
+ gshadow_dup,
+ gshadow_free,
+ gshadow_getname,
+ gshadow_parse,
+ gshadow_put,
+ fgetsx,
+ fputsx,
+ NULL, /* open_hook */
+ NULL /* close_hook */
+};
+
+static struct commonio_db gshadow_db = {
+ SGROUP_FILE, /* filename */
+ &gshadow_ops, /* ops */
+ NULL, /* fp */
+#ifdef WITH_SELINUX
+ NULL, /* scontext */
+#endif
+ 0400, /* st_mode */
+ 0, /* st_uid */
+ 0, /* st_gid */
+ NULL, /* head */
+ NULL, /* tail */
+ NULL, /* cursor */
+ false, /* changed */
+ false, /* isopen */
+ false, /* locked */
+ false /* readonly */
+};
+
+int sgr_setdbname (const char *filename)
+{
+ return commonio_setname (&gshadow_db, filename);
+}
+
+/*@observer@*/const char *sgr_dbname (void)
+{
+ return gshadow_db.filename;
+}
+
+bool sgr_file_present (void)
+{
+ if (getdef_bool ("FORCE_SHADOW"))
+ return true;
+ return commonio_present (&gshadow_db);
+}
+
+int sgr_lock (void)
+{
+ return commonio_lock (&gshadow_db);
+}
+
+int sgr_open (int mode)
+{
+ return commonio_open (&gshadow_db, mode);
+}
+
+/*@observer@*/ /*@null@*/const struct sgrp *sgr_locate (const char *name)
+{
+ return commonio_locate (&gshadow_db, name);
+}
+
+int sgr_update (const struct sgrp *sg)
+{
+ return commonio_update (&gshadow_db, (const void *) sg);
+}
+
+int sgr_remove (const char *name)
+{
+ return commonio_remove (&gshadow_db, name);
+}
+
+int sgr_rewind (void)
+{
+ return commonio_rewind (&gshadow_db);
+}
+
+/*@null@*/const struct sgrp *sgr_next (void)
+{
+ return commonio_next (&gshadow_db);
+}
+
+int sgr_close (void)
+{
+ return commonio_close (&gshadow_db);
+}
+
+int sgr_unlock (void)
+{
+ return commonio_unlock (&gshadow_db);
+}
+
+void __sgr_set_changed (void)
+{
+ gshadow_db.changed = true;
+}
+
+/*@dependent@*/ /*@null@*/struct commonio_entry *__sgr_get_head (void)
+{
+ return gshadow_db.head;
+}
+
+void __sgr_del_entry (const struct commonio_entry *ent)
+{
+ commonio_del_entry (&gshadow_db, ent);
+}
+
+/* Sort with respect to group ordering. */
+int sgr_sort ()
+{
+ return commonio_sort_wrt (&gshadow_db, __gr_get_db ());
+}
+#else
+extern int errno; /* warning: ANSI C forbids an empty source file */
+#endif
diff --git a/lib/sgroupio.h b/lib/sgroupio.h
new file mode 100644
index 0000000..163243a
--- /dev/null
+++ b/lib/sgroupio.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 1990 - 1994, Julianne Frances Haugh
+ * Copyright (c) 1996 - 2000, Marek Michałkiewicz
+ * Copyright (c) 2001 , Michał Moskal
+ * Copyright (c) 2005 , Tomasz Kłoczko
+ * Copyright (c) 2008 , Nicolas François
+ * 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. The name of the copyright holders or contributors may not be used to
+ * endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 THE COPYRIGHT
+ * HOLDERS 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.
+ */
+
+/* $Id$ */
+#ifndef _SGROUPIO_H
+#define _SGROUPIO_H
+
+extern int sgr_close (void);
+extern bool sgr_file_present (void);
+extern /*@observer@*/ /*@null@*/const struct sgrp *sgr_locate (const char *name);
+extern int sgr_lock (void);
+extern int sgr_setdbname (const char *filename);
+extern /*@observer@*/const char *sgr_dbname (void);
+extern /*@null@*/const struct sgrp *sgr_next (void);
+extern int sgr_open (int mode);
+extern int sgr_remove (const char *name);
+extern int sgr_rewind (void);
+extern int sgr_unlock (void);
+extern int sgr_update (const struct sgrp *sg);
+extern int sgr_sort (void);
+
+#endif
diff --git a/lib/shadow.c b/lib/shadow.c
new file mode 100644
index 0000000..05cb0e4
--- /dev/null
+++ b/lib/shadow.c
@@ -0,0 +1,557 @@
+/*
+ * Copyright (c) 1989 - 1994, Julianne Frances Haugh
+ * Copyright (c) 1996 - 1998, Marek Michałkiewicz
+ * Copyright (c) 2003 - 2005, Tomasz Kłoczko
+ * Copyright (c) 2009 , Nicolas François
+ * 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. The name of the copyright holders or contributors may not be used to
+ * endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 THE COPYRIGHT
+ * HOLDERS 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.
+ */
+
+#include <config.h>
+
+/* Newer versions of Linux libc already have shadow support. */
+#ifndef HAVE_GETSPNAM
+
+#ident "$Id$"
+
+#include <sys/types.h>
+#include "prototypes.h"
+#include "defines.h"
+#include <stdio.h>
+#ifdef USE_NIS
+static bool nis_used;
+static bool nis_ignore;
+static enum { native, start, middle, native2 } nis_state;
+static bool nis_bound;
+static char *nis_domain;
+static char *nis_key;
+static int nis_keylen;
+static char *nis_val;
+static int nis_vallen;
+
+#define IS_NISCHAR(c) ((c)=='+')
+#endif
+
+static FILE *shadow;
+
+#define FIELDS 9
+#define OFIELDS 5
+
+#ifdef USE_NIS
+
+/*
+ * __setspNIS - turn on or off NIS searches
+ */
+
+void __setspNIS (bool flag)
+{
+ nis_ignore = !flag;
+
+ if (nis_ignore) {
+ nis_used = false;
+ }
+}
+
+/*
+ * bind_nis - bind to NIS server
+ */
+
+static int bind_nis (void)
+{
+ if (yp_get_default_domain (&nis_domain)) {
+ return -1;
+ }
+
+ nis_bound = true;
+ return 0;
+}
+#endif
+
+/*
+ * setspent - initialize access to shadow text and DBM files
+ */
+
+void setspent (void)
+{
+ if (NULL != shadow) {
+ rewind (shadow);
+ }else {
+ shadow = fopen (SHADOW_FILE, "r");
+ }
+
+#ifdef USE_NIS
+ nis_state = native;
+#endif
+}
+
+/*
+ * endspent - terminate access to shadow text and DBM files
+ */
+
+void endspent (void)
+{
+ if (NULL != shadow) {
+ (void) fclose (shadow);
+ }
+
+ shadow = (FILE *) 0;
+}
+
+/*
+ * my_sgetspent - convert string in shadow file format to (struct spwd *)
+ */
+
+static struct spwd *my_sgetspent (const char *string)
+{
+ static char spwbuf[BUFSIZ];
+ static struct spwd spwd;
+ char *fields[FIELDS];
+ char *cp;
+ char *cpp;
+ int i;
+
+ /*
+ * Copy string to local buffer. It has to be tokenized and we
+ * have to do that to our private copy.
+ */
+
+ if (strlen (string) >= sizeof spwbuf)
+ return 0;
+ strcpy (spwbuf, string);
+
+ cp = strrchr (spwbuf, '\n');
+ if (NULL != cp)
+ *cp = '\0';
+
+ /*
+ * Tokenize the string into colon separated fields. Allow up to
+ * FIELDS different fields.
+ */
+
+ for (cp = spwbuf, i = 0; *cp && i < FIELDS; i++) {
+ fields[i] = cp;
+ while (*cp && *cp != ':')
+ cp++;
+
+ if (*cp)
+ *cp++ = '\0';
+ }
+
+ if (i == (FIELDS - 1))
+ fields[i++] = cp;
+
+ if ((cp && *cp) || (i != FIELDS && i != OFIELDS))
+ return 0;
+
+ /*
+ * Start populating the structure. The fields are all in
+ * static storage, as is the structure we pass back. If we
+ * ever see a name with '+' as the first character, we try
+ * to turn on NIS processing.
+ */
+
+ spwd.sp_namp = fields[0];
+#ifdef USE_NIS
+ if (IS_NISCHAR (fields[0][0])) {
+ nis_used = true;
+ }
+#endif
+ spwd.sp_pwdp = fields[1];
+
+ /*
+ * Get the last changed date. For all of the integer fields,
+ * we check for proper format. It is an error to have an
+ * incorrectly formatted number, unless we are using NIS.
+ */
+
+ if (fields[2][0] == '\0') {
+ spwd.sp_lstchg = -1;
+ } else {
+ if (getlong (fields[2], &spwd.sp_lstchg) == 0) {
+#ifdef USE_NIS
+ if (nis_used) {
+ spwd.sp_lstchg = -1;
+ } else
+#endif
+ return 0;
+ } else if (spwd.sp_lstchg < 0) {
+ return 0;
+ }
+ }
+
+ /*
+ * Get the minimum period between password changes.
+ */
+
+ if (fields[3][0] == '\0') {
+ spwd.sp_min = -1;
+ } else {
+ if (getlong (fields[3], &spwd.sp_min) == 0) {
+#ifdef USE_NIS
+ if (nis_used) {
+ spwd.sp_min = -1;
+ } else
+#endif
+ {
+ return 0;
+ }
+ } else if (spwd.sp_min < 0) {
+ return 0;
+ }
+ }
+
+ /*
+ * Get the maximum number of days a password is valid.
+ */
+
+ if (fields[4][0] == '\0') {
+ spwd.sp_max = -1;
+ } else {
+ if (getlong (fields[4], &spwd.sp_max) == 0) {
+#ifdef USE_NIS
+ if (nis_used) {
+ spwd.sp_max = -1;
+ } else
+#endif
+ return 0;
+ } else if (spwd.sp_max < 0) {
+ return 0;
+ }
+ }
+
+ /*
+ * If there are only OFIELDS fields (this is a SVR3.2 /etc/shadow
+ * formatted file), initialize the other field members to -1.
+ */
+
+ if (i == OFIELDS) {
+ spwd.sp_warn = -1;
+ spwd.sp_inact = -1;
+ spwd.sp_expire = -1;
+ spwd.sp_flag = SHADOW_SP_FLAG_UNSET;
+
+ return &spwd;
+ }
+
+ /*
+ * Get the number of days of password expiry warning.
+ */
+
+ if (fields[5][0] == '\0') {
+ spwd.sp_warn = -1;
+ } else {
+ if (getlong (fields[5], &spwd.sp_warn) == 0) {
+#ifdef USE_NIS
+ if (nis_used) {
+ spwd.sp_warn = -1;
+ } else
+#endif
+ {
+ return 0;
+ }
+ } else if (spwd.sp_warn < 0) {
+ return 0;
+ }
+ }
+
+ /*
+ * Get the number of days of inactivity before an account is
+ * disabled.
+ */
+
+ if (fields[6][0] == '\0') {
+ spwd.sp_inact = -1;
+ } else {
+ if (getlong (fields[6], &spwd.sp_inact) == 0) {
+#ifdef USE_NIS
+ if (nis_used) {
+ spwd.sp_inact = -1;
+ } else
+#endif
+ {
+ return 0;
+ }
+ } else if (spwd.sp_inact < 0) {
+ return 0;
+ }
+ }
+
+ /*
+ * Get the number of days after the epoch before the account is
+ * set to expire.
+ */
+
+ if (fields[7][0] == '\0') {
+ spwd.sp_expire = -1;
+ } else {
+ if (getlong (fields[7], &spwd.sp_expire) == 0) {
+#ifdef USE_NIS
+ if (nis_used) {
+ spwd.sp_expire = -1;
+ } else
+#endif
+ {
+ return 0;
+ }
+ } else if (spwd.sp_expire < 0) {
+ return 0;
+ }
+ }
+
+ /*
+ * This field is reserved for future use. But it isn't supposed
+ * to have anything other than a valid integer in it.
+ */
+
+ if (fields[8][0] == '\0') {
+ spwd.sp_flag = SHADOW_SP_FLAG_UNSET;
+ } else {
+ if (getlong (fields[8], &spwd.sp_flag) == 0) {
+ /* FIXME: add a getulong function */
+#ifdef USE_NIS
+ if (nis_used) {
+ spwd.sp_flag = SHADOW_SP_FLAG_UNSET;
+ } else
+#endif
+ {
+ return 0;
+ }
+ } else if (spwd.sp_flag < 0) {
+ return 0;
+ }
+ }
+
+ return (&spwd);
+}
+
+/*
+ * fgetspent - get an entry from a /etc/shadow formatted stream
+ */
+
+struct spwd *fgetspent (FILE * fp)
+{
+ char buf[BUFSIZ];
+ char *cp;
+
+ if (NULL == fp) {
+ return (0);
+ }
+
+#ifdef USE_NIS
+ while (fgets (buf, (int) sizeof buf, fp) != (char *) 0)
+#else
+ if (fgets (buf, (int) sizeof buf, fp) != (char *) 0)
+#endif
+ {
+ cp = strchr (buf, '\n');
+ if (NULL != cp) {
+ *cp = '\0';
+ }
+#ifdef USE_NIS
+ if (nis_ignore && IS_NISCHAR (buf[0])) {
+ continue;
+ }
+#endif
+ return my_sgetspent (buf);
+ }
+ return 0;
+}
+
+/*
+ * getspent - get a (struct spwd *) from the current shadow file
+ */
+
+struct spwd *getspent (void)
+{
+#ifdef USE_NIS
+ int nis_1_user = 0;
+ struct spwd *val;
+ char buf[BUFSIZ];
+#endif
+ if (NULL == shadow) {
+ setspent ();
+ }
+
+#ifdef USE_NIS
+ again:
+ /*
+ * See if we are reading from the local file.
+ */
+
+ if (nis_state == native || nis_state == native2) {
+
+ /*
+ * Get the next entry from the shadow file. Return NULL
+ * right away if there is none.
+ */
+
+ val = fgetspent (shadow);
+ if (NULL == val)
+ return 0;
+
+ /*
+ * If this entry began with a NIS escape character, we have
+ * to see if this is just a single user, or if the entire
+ * map is being asked for.
+ */
+
+ if (IS_NISCHAR (val->sp_namp[0])) {
+ if (val->sp_namp[1])
+ nis_1_user = 1;
+ else
+ nis_state = start;
+ }
+
+ /*
+ * If this isn't a NIS user and this isn't an escape to go
+ * use a NIS map, it must be a regular local user.
+ */
+
+ if (nis_1_user == 0 && nis_state != start)
+ return val;
+
+ /*
+ * If this is an escape to use an NIS map, switch over to
+ * that bunch of code.
+ */
+
+ if (nis_state == start)
+ goto again;
+
+ /*
+ * NEEDSWORK. Here we substitute pieces-parts of this entry.
+ */
+
+ return 0;
+ } else {
+ if (!nis_bound) {
+ if (bind_nis ()) {
+ nis_state = native2;
+ goto again;
+ }
+ }
+ if (nis_state == start) {
+ if (yp_first (nis_domain, "shadow.bynam", &nis_key,
+ &nis_keylen, &nis_val, &nis_vallen)) {
+ nis_state = native2;
+ goto again;
+ }
+ nis_state = middle;
+ } else if (nis_state == middle) {
+ if (yp_next (nis_domain, "shadow.bynam", nis_key,
+ nis_keylen, &nis_key, &nis_keylen,
+ &nis_val, &nis_vallen)) {
+ nis_state = native2;
+ goto again;
+ }
+ }
+ return my_sgetspent (nis_val);
+ }
+#else
+ return (fgetspent (shadow));
+#endif
+}
+
+/*
+ * getspnam - get a shadow entry by name
+ */
+
+struct spwd *getspnam (const char *name)
+{
+ struct spwd *sp;
+
+#ifdef USE_NIS
+ char buf[BUFSIZ];
+ static char save_name[16];
+ bool nis_disabled = false;
+#endif
+
+ setspent ();
+
+#ifdef USE_NIS
+ /*
+ * Search the shadow.byname map for this user.
+ */
+
+ if (!nis_ignore && !nis_bound) {
+ bind_nis ();
+ }
+
+ if (!nis_ignore && nis_bound) {
+ char *cp;
+
+ if (yp_match (nis_domain, "shadow.byname", name,
+ strlen (name), &nis_val, &nis_vallen) == 0) {
+
+ cp = strchr (nis_val, '\n');
+ if (NULL != cp) {
+ *cp = '\0';
+ }
+
+ nis_state = middle;
+ sp = my_sgetspent (nis_val);
+ if (NULL != sp) {
+ strcpy (save_name, sp->sp_namp);
+ nis_key = save_name;
+ nis_keylen = strlen (save_name);
+ }
+ endspent ();
+ return sp;
+ } else {
+ nis_state = native2;
+ }
+ }
+#endif
+#ifdef USE_NIS
+ /*
+ * NEEDSWORK -- this is a mess, and it is the same mess in the
+ * other three files. I can't just blindly turn off NIS because
+ * this might be the first pass through the local files. In
+ * that case, I never discover that NIS is present.
+ */
+
+ if (nis_used) {
+ nis_ignore = true;
+ nis_disabled = true;
+ }
+#endif
+ while ((sp = getspent ()) != (struct spwd *) 0) {
+ if (strcmp (name, sp->sp_namp) == 0) {
+ break;
+ }
+ }
+#ifdef USE_NIS
+ if (nis_disabled) {
+ nis_ignore = false;
+ }
+#endif
+ endspent ();
+ return (sp);
+}
+#else
+extern int errno; /* warning: ANSI C forbids an empty source file */
+#endif
+
diff --git a/lib/shadowio.c b/lib/shadowio.c
new file mode 100644
index 0000000..5fa3d31
--- /dev/null
+++ b/lib/shadowio.c
@@ -0,0 +1,267 @@
+/*
+ * Copyright (c) 1990 - 1994, Julianne Frances Haugh
+ * Copyright (c) 1996 - 2000, Marek Michałkiewicz
+ * Copyright (c) 2001 , Michał Moskal
+ * Copyright (c) 2005 , Tomasz Kłoczko
+ * Copyright (c) 2007 - 2009, Nicolas François
+ * 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. The name of the copyright holders or contributors may not be used to
+ * endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 THE COPYRIGHT
+ * HOLDERS 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.
+ */
+
+#include <config.h>
+
+#ident "$Id$"
+
+#include "prototypes.h"
+#include "defines.h"
+#include <shadow.h>
+#include <stdio.h>
+#include "commonio.h"
+#include "getdef.h"
+#include "shadowio.h"
+#ifdef WITH_TCB
+#include <tcb.h>
+#include "tcbfuncs.h"
+#endif /* WITH_TCB */
+
+static /*@null@*/ /*@only@*/void *shadow_dup (const void *ent)
+{
+ const struct spwd *sp = ent;
+
+ return __spw_dup (sp);
+}
+
+static void shadow_free (/*@out@*//*@only@*/void *ent)
+{
+ struct spwd *sp = ent;
+
+ spw_free (sp);
+}
+
+static const char *shadow_getname (const void *ent)
+{
+ const struct spwd *sp = ent;
+
+ return sp->sp_namp;
+}
+
+static void *shadow_parse (const char *line)
+{
+ return (void *) sgetspent (line);
+}
+
+static int shadow_put (const void *ent, FILE * file)
+{
+ const struct spwd *sp = ent;
+
+ if ( (NULL == sp)
+ || (valid_field (sp->sp_namp, ":\n") == -1)
+ || (valid_field (sp->sp_pwdp, ":\n") == -1)) {
+ return -1;
+ }
+
+ return (putspent (sp, file) == -1) ? -1 : 0;
+}
+
+static struct commonio_ops shadow_ops = {
+ shadow_dup,
+ shadow_free,
+ shadow_getname,
+ shadow_parse,
+ shadow_put,
+ fgets,
+ fputs,
+ NULL, /* open_hook */
+ NULL /* close_hook */
+};
+
+static struct commonio_db shadow_db = {
+ SHADOW_FILE, /* filename */
+ &shadow_ops, /* ops */
+ NULL, /* fp */
+#ifdef WITH_SELINUX
+ NULL, /* scontext */
+#endif /* WITH_SELINUX */
+ 0400, /* st_mode */
+ 0, /* st_uid */
+ 0, /* st_gid */
+ NULL, /* head */
+ NULL, /* tail */
+ NULL, /* cursor */
+ false, /* changed */
+ false, /* isopen */
+ false, /* locked */
+ false /* readonly */
+};
+
+int spw_setdbname (const char *filename)
+{
+ return commonio_setname (&shadow_db, filename);
+}
+
+/*@observer@*/const char *spw_dbname (void)
+{
+ return shadow_db.filename;
+}
+
+bool spw_file_present (void)
+{
+ if (getdef_bool ("FORCE_SHADOW"))
+ return true;
+ return commonio_present (&shadow_db);
+}
+
+int spw_lock (void)
+{
+#ifdef WITH_TCB
+ int retval = 0;
+
+ if (!getdef_bool ("USE_TCB")) {
+#endif /* WITH_TCB */
+ return commonio_lock (&shadow_db);
+#ifdef WITH_TCB
+ }
+ if (shadowtcb_drop_priv () == SHADOWTCB_FAILURE) {
+ return 0;
+ }
+ if (lckpwdf_tcb (shadow_db.filename) == 0) {
+ shadow_db.locked = 1;
+ retval = 1;
+ }
+ if (shadowtcb_gain_priv () == SHADOWTCB_FAILURE) {
+ return 0;
+ }
+ return retval;
+#endif /* WITH_TCB */
+}
+
+int spw_open (int mode)
+{
+ int retval = 0;
+#ifdef WITH_TCB
+ bool use_tcb = getdef_bool ("USE_TCB");
+
+ if (use_tcb && (shadowtcb_drop_priv () == SHADOWTCB_FAILURE)) {
+ return 0;
+ }
+#endif /* WITH_TCB */
+ retval = commonio_open (&shadow_db, mode);
+#ifdef WITH_TCB
+ if (use_tcb && (shadowtcb_gain_priv () == SHADOWTCB_FAILURE)) {
+ return 0;
+ }
+#endif /* WITH_TCB */
+ return retval;
+}
+
+/*@observer@*/ /*@null@*/const struct spwd *spw_locate (const char *name)
+{
+ return commonio_locate (&shadow_db, name);
+}
+
+int spw_update (const struct spwd *sp)
+{
+ return commonio_update (&shadow_db, (const void *) sp);
+}
+
+int spw_remove (const char *name)
+{
+ return commonio_remove (&shadow_db, name);
+}
+
+int spw_rewind (void)
+{
+ return commonio_rewind (&shadow_db);
+}
+
+/*@observer@*/ /*@null@*/const struct spwd *spw_next (void)
+{
+ return commonio_next (&shadow_db);
+}
+
+int spw_close (void)
+{
+ int retval = 0;
+#ifdef WITH_TCB
+ bool use_tcb = getdef_bool ("USE_TCB");
+
+ if (use_tcb && (shadowtcb_drop_priv () == SHADOWTCB_FAILURE)) {
+ return 0;
+ }
+#endif /* WITH_TCB */
+ retval = commonio_close (&shadow_db);
+#ifdef WITH_TCB
+ if (use_tcb && (shadowtcb_gain_priv () == SHADOWTCB_FAILURE)) {
+ return 0;
+ }
+#endif /* WITH_TCB */
+ return retval;
+}
+
+int spw_unlock (void)
+{
+#ifdef WITH_TCB
+ int retval = 0;
+
+ if (!getdef_bool ("USE_TCB")) {
+#endif /* WITH_TCB */
+ return commonio_unlock (&shadow_db);
+#ifdef WITH_TCB
+ }
+ if (shadowtcb_drop_priv () == SHADOWTCB_FAILURE) {
+ return 0;
+ }
+ if (ulckpwdf_tcb () == 0) {
+ shadow_db.locked = 0;
+ retval = 1;
+ }
+ if (shadowtcb_gain_priv () == SHADOWTCB_FAILURE) {
+ return 0;
+ }
+ return retval;
+#endif /* WITH_TCB */
+}
+
+struct commonio_entry *__spw_get_head (void)
+{
+ return shadow_db.head;
+}
+
+void __spw_del_entry (const struct commonio_entry *ent)
+{
+ commonio_del_entry (&shadow_db, ent);
+}
+
+/* Sort with respect to passwd ordering. */
+int spw_sort ()
+{
+#ifdef WITH_TCB
+ if (getdef_bool ("USE_TCB")) {
+ return 0;
+ }
+#endif /* WITH_TCB */
+ return commonio_sort_wrt (&shadow_db, __pw_get_db ());
+}
diff --git a/lib/shadowio.h b/lib/shadowio.h
new file mode 100644
index 0000000..5384b1d
--- /dev/null
+++ b/lib/shadowio.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 1990 - 1994, Julianne Frances Haugh
+ * Copyright (c) 1996 - 2000, Marek Michałkiewicz
+ * Copyright (c) 2001 - 2005, Tomasz Kłoczko
+ * Copyright (c) 2008 , Nicolas François
+ * 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. The name of the copyright holders or contributors may not be used to
+ * endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 THE COPYRIGHT
+ * HOLDERS 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.
+ */
+
+/* $Id$ */
+#ifndef SHADOWIO_H
+#define SHADOWIO_H
+
+#include "defines.h"
+
+extern int spw_close (void);
+extern bool spw_file_present (void);
+extern /*@observer@*/ /*@null@*/const struct spwd *spw_locate (const char *name);
+extern int spw_lock (void);
+extern int spw_setdbname (const char *filename);
+extern /*@observer@*/const char *spw_dbname (void);
+extern /*@observer@*/ /*@null@*/const struct spwd *spw_next (void);
+extern int spw_open (int mode);
+extern int spw_remove (const char *name);
+extern int spw_rewind (void);
+extern int spw_unlock (void);
+extern int spw_update (const struct spwd *sp);
+extern int spw_sort (void);
+
+#endif
diff --git a/lib/shadowmem.c b/lib/shadowmem.c
new file mode 100644
index 0000000..8989598
--- /dev/null
+++ b/lib/shadowmem.c
@@ -0,0 +1,89 @@
+/*
+ * Copyright (c) 1990 - 1994, Julianne Frances Haugh
+ * Copyright (c) 1996 - 2000, Marek Michałkiewicz
+ * Copyright (c) 2001 , Michał Moskal
+ * Copyright (c) 2005 , Tomasz Kłoczko
+ * Copyright (c) 2007 - 2013, Nicolas François
+ * 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. The name of the copyright holders or contributors may not be used to
+ * endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 THE COPYRIGHT
+ * HOLDERS 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.
+ */
+
+#include <config.h>
+
+#ident "$Id$"
+
+#include "prototypes.h"
+#include "defines.h"
+#include <shadow.h>
+#include <stdio.h>
+#include "shadowio.h"
+
+/*@null@*/ /*@only@*/struct spwd *__spw_dup (const struct spwd *spent)
+{
+ struct spwd *sp;
+
+ sp = (struct spwd *) malloc (sizeof *sp);
+ if (NULL == sp) {
+ return NULL;
+ }
+ /* The libc might define other fields. They won't be copied. */
+ memset (sp, 0, sizeof *sp);
+ sp->sp_lstchg = spent->sp_lstchg;
+ sp->sp_min = spent->sp_min;
+ sp->sp_max = spent->sp_max;
+ sp->sp_warn = spent->sp_warn;
+ sp->sp_inact = spent->sp_inact;
+ sp->sp_expire = spent->sp_expire;
+ sp->sp_flag = spent->sp_flag;
+ /*@-mustfreeonly@*/
+ sp->sp_namp = strdup (spent->sp_namp);
+ /*@=mustfreeonly@*/
+ if (NULL == sp->sp_namp) {
+ free(sp);
+ return NULL;
+ }
+ /*@-mustfreeonly@*/
+ sp->sp_pwdp = strdup (spent->sp_pwdp);
+ /*@=mustfreeonly@*/
+ if (NULL == sp->sp_pwdp) {
+ free(sp->sp_namp);
+ free(sp);
+ return NULL;
+ }
+
+ return sp;
+}
+
+void spw_free (/*@out@*/ /*@only@*/struct spwd *spent)
+{
+ free (spent->sp_namp);
+ if (NULL != spent->sp_pwdp) {
+ memzero (spent->sp_pwdp, strlen (spent->sp_pwdp));
+ free (spent->sp_pwdp);
+ }
+ free (spent);
+}
+
diff --git a/lib/spawn.c b/lib/spawn.c
new file mode 100644
index 0000000..da98401
--- /dev/null
+++ b/lib/spawn.c
@@ -0,0 +1,82 @@
+/*
+ * Copyright (c) 2011 , Jonathan Nieder
+ * 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. The name of the copyright holders or contributors may not be used to
+ * endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 THE COPYRIGHT
+ * HOLDERS 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.
+ */
+
+#include <config.h>
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <errno.h>
+#include <string.h>
+#include "exitcodes.h"
+#include "prototypes.h"
+
+int run_command (const char *cmd, const char *argv[],
+ /*@null@*/const char *envp[], /*@out@*/int *status)
+{
+ pid_t pid, wpid;
+
+ if (NULL == envp) {
+ envp = (const char **)environ;
+ }
+
+ (void) fflush (stdout);
+ (void) fflush (stderr);
+
+ pid = fork ();
+ if (0 == pid) {
+ (void) execve (cmd, (char * const *) argv,
+ (char * const *) envp);
+ if (ENOENT == errno) {
+ exit (E_CMD_NOTFOUND);
+ }
+ fprintf (stderr, "%s: cannot execute %s: %s\n",
+ Prog, cmd, strerror (errno));
+ exit (E_CMD_NOEXEC);
+ } else if ((pid_t)-1 == pid) {
+ fprintf (stderr, "%s: cannot execute %s: %s\n",
+ Prog, cmd, strerror (errno));
+ return -1;
+ }
+
+ do {
+ wpid = waitpid (pid, status, 0);
+ } while ( ((pid_t)-1 == wpid && errno == EINTR)
+ || (wpid != pid));
+
+ if ((pid_t)-1 == wpid) {
+ fprintf (stderr, "%s: waitpid (status: %d): %s\n",
+ Prog, *status, strerror (errno));
+ return -1;
+ }
+
+ return 0;
+}
+
diff --git a/lib/subordinateio.c b/lib/subordinateio.c
new file mode 100644
index 0000000..0d64a91
--- /dev/null
+++ b/lib/subordinateio.c
@@ -0,0 +1,701 @@
+/*
+ * Copyright (c) 2012 - Eric Biederman
+ */
+
+#include <config.h>
+
+#ifdef ENABLE_SUBIDS
+
+#include "prototypes.h"
+#include "defines.h"
+#include <stdio.h>
+#include "commonio.h"
+#include "subordinateio.h"
+#include <sys/types.h>
+#include <pwd.h>
+
+struct subordinate_range {
+ const char *owner;
+ unsigned long start;
+ unsigned long count;
+};
+
+#define NFIELDS 3
+
+/*
+ * subordinate_dup: create a duplicate range
+ *
+ * @ent: a pointer to a subordinate_range struct
+ *
+ * Returns a pointer to a newly allocated duplicate subordinate_range struct
+ * or NULL on failure
+ */
+static /*@null@*/ /*@only@*/void *subordinate_dup (const void *ent)
+{
+ const struct subordinate_range *rangeent = ent;
+ struct subordinate_range *range;
+
+ range = (struct subordinate_range *) malloc (sizeof *range);
+ if (NULL == range) {
+ return NULL;
+ }
+ range->owner = strdup (rangeent->owner);
+ if (NULL == range->owner) {
+ free(range);
+ return NULL;
+ }
+ range->start = rangeent->start;
+ range->count = rangeent->count;
+
+ return range;
+}
+
+/*
+ * subordinate_free: free a subordinate_range struct
+ *
+ * @ent: pointer to a subordinate_range struct to free.
+ */
+static void subordinate_free (/*@out@*/ /*@only@*/void *ent)
+{
+ struct subordinate_range *rangeent = ent;
+
+ free ((void *)(rangeent->owner));
+ free (rangeent);
+}
+
+/*
+ * subordinate_parse:
+ *
+ * @line: a line to parse
+ *
+ * Returns a pointer to a subordinate_range struct representing the values
+ * in @line, or NULL on failure. Note that the returned value should not
+ * be freed by the caller.
+ */
+static void *subordinate_parse (const char *line)
+{
+ static struct subordinate_range range;
+ static char rangebuf[1024];
+ int i;
+ char *cp;
+ char *fields[NFIELDS];
+
+ /*
+ * Copy the string to a temporary buffer so the substrings can
+ * be modified to be NULL terminated.
+ */
+ if (strlen (line) >= sizeof rangebuf)
+ return NULL; /* fail if too long */
+ strcpy (rangebuf, line);
+
+ /*
+ * Save a pointer to the start of each colon separated
+ * field. The fields are converted into NUL terminated strings.
+ */
+
+ for (cp = rangebuf, i = 0; (i < NFIELDS) && (NULL != cp); i++) {
+ fields[i] = cp;
+ while (('\0' != *cp) && (':' != *cp)) {
+ cp++;
+ }
+
+ if ('\0' != *cp) {
+ *cp = '\0';
+ cp++;
+ } else {
+ cp = NULL;
+ }
+ }
+
+ /*
+ * There must be exactly NFIELDS colon separated fields or
+ * the entry is invalid. Also, fields must be non-blank.
+ */
+ if (i != NFIELDS || *fields[0] == '\0' || *fields[1] == '\0' || *fields[2] == '\0')
+ return NULL;
+ range.owner = fields[0];
+ if (getulong (fields[1], &range.start) == 0)
+ return NULL;
+ if (getulong (fields[2], &range.count) == 0)
+ return NULL;
+
+ return &range;
+}
+
+/*
+ * subordinate_put: print a subordinate_range value to a file
+ *
+ * @ent: a pointer to a subordinate_range struct to print out.
+ * @file: file to which to print.
+ *
+ * Returns 0 on success, -1 on error.
+ */
+static int subordinate_put (const void *ent, FILE * file)
+{
+ const struct subordinate_range *range = ent;
+
+ return fprintf(file, "%s:%lu:%lu\n",
+ range->owner,
+ range->start,
+ range->count) < 0 ? -1 : 0;
+}
+
+static struct commonio_ops subordinate_ops = {
+ subordinate_dup, /* dup */
+ subordinate_free, /* free */
+ NULL, /* getname */
+ subordinate_parse, /* parse */
+ subordinate_put, /* put */
+ fgets, /* fgets */
+ fputs, /* fputs */
+ NULL, /* open_hook */
+ NULL, /* close_hook */
+};
+
+static /*@observer@*/ /*@null*/const struct subordinate_range *subordinate_next(struct commonio_db *db)
+{
+ return (const struct subordinate_range *)commonio_next (db);
+}
+
+/*
+ * range_exists: Check whether @owner owns any ranges
+ *
+ * @db: database to query
+ * @owner: owner being queried
+ *
+ * Returns true if @owner owns any subuid ranges, false otherwise.
+ */
+static const bool range_exists(struct commonio_db *db, const char *owner)
+{
+ const struct subordinate_range *range;
+ commonio_rewind(db);
+ while ((range = commonio_next(db)) != NULL) {
+ if (0 == strcmp(range->owner, owner))
+ return true;
+ }
+ return false;
+}
+
+/*
+ * find_range: find a range which @owner is authorized to use which includes
+ * subuid @val.
+ *
+ * @db: database to query
+ * @owner: owning uid being queuried
+ * @val: subuid being searched for.
+ *
+ * Returns a range of subuids belonging to @owner and including the subuid
+ * @val, or NULL if no such range exists.
+ */
+static const struct subordinate_range *find_range(struct commonio_db *db,
+ const char *owner, unsigned long val)
+{
+ const struct subordinate_range *range;
+
+ /*
+ * Search for exact username/group specification
+ *
+ * This is the original method - go fast through the db, doing only
+ * exact username/group string comparison. Therefore we leave it as-is
+ * for the time being, in order to keep it equally fast as it was
+ * before.
+ */
+ commonio_rewind(db);
+ while ((range = commonio_next(db)) != NULL) {
+ unsigned long first = range->start;
+ unsigned long last = first + range->count - 1;
+
+ if (0 != strcmp(range->owner, owner))
+ continue;
+
+ if ((val >= first) && (val <= last))
+ return range;
+ }
+
+
+ /*
+ * We only do special handling for these two files
+ */
+ if ((0 != strcmp(db->filename, "/etc/subuid")) && (0 != strcmp(db->filename, "/etc/subgid")))
+ return NULL;
+
+ /*
+ * Search loop above did not produce any result. Let's rerun it,
+ * but this time try to matcha actual UIDs. The first entry that
+ * matches is considered a success.
+ * (It may be specified as literal UID or as another username which
+ * has the same UID as the username we are looking for.)
+ */
+ struct passwd *pwd;
+ uid_t owner_uid;
+ char owner_uid_string[33] = "";
+
+
+ /* Get UID of the username we are looking for */
+ pwd = getpwnam(owner);
+ if (NULL == pwd) {
+ /* Username not defined in /etc/passwd, or error occured during lookup */
+ return NULL;
+ }
+ owner_uid = pwd->pw_uid;
+ sprintf(owner_uid_string, "%lu", (unsigned long int)owner_uid);
+
+ commonio_rewind(db);
+ while ((range = commonio_next(db)) != NULL) {
+ unsigned long first = range->start;
+ unsigned long last = first + range->count - 1;
+
+ /* For performance reasons check range before using getpwnam() */
+ if ((val < first) || (val > last)) {
+ continue;
+ }
+
+ /*
+ * Range matches. Check if range owner is specified
+ * as numeric UID and if it matches.
+ */
+ if (0 == strcmp(range->owner, owner_uid_string)) {
+ return range;
+ }
+
+ /*
+ * Ok, this range owner is not specified as numeric UID
+ * we are looking for. It may be specified as another
+ * UID or as a literal username.
+ *
+ * If specified as another UID, the call to getpwnam()
+ * will return NULL.
+ *
+ * If specified as literal username, we will get its
+ * UID and compare that to UID we are looking for.
+ */
+ const struct passwd *range_owner_pwd;
+
+ range_owner_pwd = getpwnam(range->owner);
+ if (NULL == range_owner_pwd) {
+ continue;
+ }
+
+ if (owner_uid == range_owner_pwd->pw_uid) {
+ return range;
+ }
+ }
+
+ return NULL;
+}
+
+/*
+ * have_range: check whether @owner is authorized to use the range
+ * (@start .. @start+@count-1).
+ * @db: database to check
+ * @owner: owning uid being queried
+ * @start: start of range
+ * @count: number of uids in range
+ *
+ * Returns true if @owner is authorized to use the range, false otherwise.
+ */
+static bool have_range(struct commonio_db *db,
+ const char *owner, unsigned long start, unsigned long count)
+{
+ const struct subordinate_range *range;
+ unsigned long end;
+
+ if (count == 0)
+ return false;
+
+ end = start + count - 1;
+ range = find_range (db, owner, start);
+ while (range) {
+ unsigned long last;
+
+ last = range->start + range->count - 1;
+ if (last >= (start + count - 1))
+ return true;
+
+ count = end - last;
+ start = last + 1;
+ range = find_range(db, owner, start);
+ }
+ return false;
+}
+
+/*
+ * subordinate_range_cmp: compare uid ranges
+ *
+ * @p1: pointer to a commonio_entry struct to compare
+ * @p2: pointer to second commonio_entry struct to compare
+ *
+ * Returns 0 if the entries are the same. Otherwise return -1
+ * if the range in p1 is lower than that in p2, or (if the ranges are
+ * equal) if the owning uid in p1 is lower than p2's. Return 1 if p1's
+ * range or owning uid is great than p2's.
+ */
+static int subordinate_range_cmp (const void *p1, const void *p2)
+{
+ struct subordinate_range *range1, *range2;
+
+ if ((*(struct commonio_entry **) p1)->eptr == NULL)
+ return 1;
+ if ((*(struct commonio_entry **) p2)->eptr == NULL)
+ return -1;
+
+ range1 = ((struct subordinate_range *) (*(struct commonio_entry **) p1)->eptr);
+ range2 = ((struct subordinate_range *) (*(struct commonio_entry **) p2)->eptr);
+
+ if (range1->start < range2->start)
+ return -1;
+ else if (range1->start > range2->start)
+ return 1;
+ else if (range1->count < range2->count)
+ return -1;
+ else if (range1->count > range2->count)
+ return 1;
+ else
+ return strcmp(range1->owner, range2->owner);
+}
+
+/*
+ * find_free_range: find an unused consecutive sequence of ids to allocate
+ * to a user.
+ * @db: database to search
+ * @min: the first uid in the range to find
+ * @max: the highest uid to find
+ * @count: the number of uids needed
+ *
+ * Return the lowest new uid, or ULONG_MAX on failure.
+ */
+static unsigned long find_free_range(struct commonio_db *db,
+ unsigned long min, unsigned long max,
+ unsigned long count)
+{
+ const struct subordinate_range *range;
+ unsigned long low, high;
+
+ /* When given invalid parameters fail */
+ if ((count == 0) || (max < min))
+ goto fail;
+
+ /* Sort by range then by owner */
+ commonio_sort (db, subordinate_range_cmp);
+ commonio_rewind(db);
+
+ low = min;
+ while ((range = commonio_next(db)) != NULL) {
+ unsigned long first = range->start;
+ unsigned long last = first + range->count - 1;
+
+ /* Find the top end of the hole before this range */
+ high = first;
+
+ /* Don't allocate IDs after max (included) */
+ if (high > max + 1) {
+ high = max + 1;
+ }
+
+ /* Is the hole before this range large enough? */
+ if ((high > low) && ((high - low) >= count))
+ return low;
+
+ /* Compute the low end of the next hole */
+ if (low < (last + 1))
+ low = last + 1;
+ if (low > max)
+ goto fail;
+ }
+
+ /* Is the remaining unclaimed area large enough? */
+ if (((max - low) + 1) >= count)
+ return low;
+fail:
+ return ULONG_MAX;
+}
+
+/*
+ * add_range: add a subuid range to an owning uid's list of authorized
+ * subuids.
+ * @db: database to which to add
+ * @owner: uid which owns the subuid
+ * @start: the first uid in the owned range
+ * @count: the number of uids in the range
+ *
+ * Return 1 if the range is already present or on succcess. On error
+ * return 0 and set errno appropriately.
+ */
+static int add_range(struct commonio_db *db,
+ const char *owner, unsigned long start, unsigned long count)
+{
+ struct subordinate_range range;
+ range.owner = owner;
+ range.start = start;
+ range.count = count;
+
+ /* See if the range is already present */
+ if (have_range(db, owner, start, count))
+ return 1;
+
+ /* Otherwise append the range */
+ return commonio_append(db, &range);
+}
+
+/*
+ * remove_range: remove a range of subuids from an owning uid's list
+ * of authorized subuids.
+ * @db: database to work on
+ * @owner: owning uid whose range is being removed
+ * @start: start of the range to be removed
+ * @count: number of uids in the range.
+ *
+ * Returns 0 on failure, 1 on success. Failure means that we needed to
+ * create a new range to represent the new limits, and failed doing so.
+ */
+static int remove_range (struct commonio_db *db,
+ const char *owner,
+ unsigned long start, unsigned long count)
+{
+ struct commonio_entry *ent;
+ unsigned long end;
+
+ if (count == 0) {
+ return 1;
+ }
+
+ end = start + count - 1;
+ for (ent = db->head; NULL != ent; ent = ent->next) {
+ struct subordinate_range *range = ent->eptr;
+ unsigned long first;
+ unsigned long last;
+
+ /* Skip unparsed entries */
+ if (NULL == range) {
+ continue;
+ }
+
+ first = range->start;
+ last = first + range->count - 1;
+
+ /* Skip entries with a different owner */
+ if (0 != strcmp (range->owner, owner)) {
+ continue;
+ }
+
+ /* Skip entries outside of the range to remove */
+ if ((end < first) || (start > last)) {
+ continue;
+ }
+
+ if (start <= first) {
+ if (end >= last) {
+ /* to be removed: [start, end]
+ * range: [first, last] */
+ /* entry completely contained in the
+ * range to remove */
+ commonio_del_entry (db, ent);
+ } else {
+ /* to be removed: [start, end]
+ * range: [first, last] */
+ /* Remove only the start of the entry */
+ range->start = end + 1;
+ range->count = (last - range->start) + 1;
+
+ ent->changed = true;
+ db->changed = true;
+ }
+ } else {
+ if (end >= last) {
+ /* to be removed: [start, end]
+ * range: [first, last] */
+ /* Remove only the end of the entry */
+ range->count = start - range->start;
+
+ ent->changed = true;
+ db->changed = true;
+ } else {
+ /* to be removed: [start, end]
+ * range: [first, last] */
+ /* Remove the middle of the range
+ * This requires to create a new range */
+ struct subordinate_range tail;
+ tail.owner = range->owner;
+ tail.start = end + 1;
+ tail.count = (last - tail.start) + 1;
+
+ if (commonio_append (db, &tail) == 0) {
+ return 0;
+ }
+
+ range->count = start - range->start;
+
+ ent->changed = true;
+ db->changed = true;
+ }
+ }
+ }
+
+ return 1;
+}
+
+static struct commonio_db subordinate_uid_db = {
+ "/etc/subuid", /* filename */
+ &subordinate_ops, /* ops */
+ NULL, /* fp */
+#ifdef WITH_SELINUX
+ NULL, /* scontext */
+#endif
+ 0644, /* st_mode */
+ 0, /* st_uid */
+ 0, /* st_gid */
+ NULL, /* head */
+ NULL, /* tail */
+ NULL, /* cursor */
+ false, /* changed */
+ false, /* isopen */
+ false, /* locked */
+ false /* readonly */
+};
+
+int sub_uid_setdbname (const char *filename)
+{
+ return commonio_setname (&subordinate_uid_db, filename);
+}
+
+/*@observer@*/const char *sub_uid_dbname (void)
+{
+ return subordinate_uid_db.filename;
+}
+
+bool sub_uid_file_present (void)
+{
+ return commonio_present (&subordinate_uid_db);
+}
+
+int sub_uid_lock (void)
+{
+ return commonio_lock (&subordinate_uid_db);
+}
+
+int sub_uid_open (int mode)
+{
+ return commonio_open (&subordinate_uid_db, mode);
+}
+
+bool sub_uid_assigned(const char *owner)
+{
+ return range_exists (&subordinate_uid_db, owner);
+}
+
+bool have_sub_uids(const char *owner, uid_t start, unsigned long count)
+{
+ return have_range (&subordinate_uid_db, owner, start, count);
+}
+
+int sub_uid_add (const char *owner, uid_t start, unsigned long count)
+{
+ return add_range (&subordinate_uid_db, owner, start, count);
+}
+
+int sub_uid_remove (const char *owner, uid_t start, unsigned long count)
+{
+ return remove_range (&subordinate_uid_db, owner, start, count);
+}
+
+int sub_uid_close (void)
+{
+ return commonio_close (&subordinate_uid_db);
+}
+
+int sub_uid_unlock (void)
+{
+ return commonio_unlock (&subordinate_uid_db);
+}
+
+uid_t sub_uid_find_free_range(uid_t min, uid_t max, unsigned long count)
+{
+ unsigned long start;
+ start = find_free_range (&subordinate_uid_db, min, max, count);
+ return start == ULONG_MAX ? (uid_t) -1 : start;
+}
+
+static struct commonio_db subordinate_gid_db = {
+ "/etc/subgid", /* filename */
+ &subordinate_ops, /* ops */
+ NULL, /* fp */
+#ifdef WITH_SELINUX
+ NULL, /* scontext */
+#endif
+ 0644, /* st_mode */
+ 0, /* st_uid */
+ 0, /* st_gid */
+ NULL, /* head */
+ NULL, /* tail */
+ NULL, /* cursor */
+ false, /* changed */
+ false, /* isopen */
+ false, /* locked */
+ false /* readonly */
+};
+
+int sub_gid_setdbname (const char *filename)
+{
+ return commonio_setname (&subordinate_gid_db, filename);
+}
+
+/*@observer@*/const char *sub_gid_dbname (void)
+{
+ return subordinate_gid_db.filename;
+}
+
+bool sub_gid_file_present (void)
+{
+ return commonio_present (&subordinate_gid_db);
+}
+
+int sub_gid_lock (void)
+{
+ return commonio_lock (&subordinate_gid_db);
+}
+
+int sub_gid_open (int mode)
+{
+ return commonio_open (&subordinate_gid_db, mode);
+}
+
+bool have_sub_gids(const char *owner, gid_t start, unsigned long count)
+{
+ return have_range(&subordinate_gid_db, owner, start, count);
+}
+
+bool sub_gid_assigned(const char *owner)
+{
+ return range_exists (&subordinate_gid_db, owner);
+}
+
+int sub_gid_add (const char *owner, gid_t start, unsigned long count)
+{
+ return add_range (&subordinate_gid_db, owner, start, count);
+}
+
+int sub_gid_remove (const char *owner, gid_t start, unsigned long count)
+{
+ return remove_range (&subordinate_gid_db, owner, start, count);
+}
+
+int sub_gid_close (void)
+{
+ return commonio_close (&subordinate_gid_db);
+}
+
+int sub_gid_unlock (void)
+{
+ return commonio_unlock (&subordinate_gid_db);
+}
+
+gid_t sub_gid_find_free_range(gid_t min, gid_t max, unsigned long count)
+{
+ unsigned long start;
+ start = find_free_range (&subordinate_gid_db, min, max, count);
+ return start == ULONG_MAX ? (gid_t) -1 : start;
+}
+#else /* !ENABLE_SUBIDS */
+extern int errno; /* warning: ANSI C forbids an empty source file */
+#endif /* !ENABLE_SUBIDS */
+
diff --git a/lib/subordinateio.h b/lib/subordinateio.h
new file mode 100644
index 0000000..a21d72b
--- /dev/null
+++ b/lib/subordinateio.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2012- Eric W. Biederman
+ */
+
+#ifndef _SUBORDINATEIO_H
+#define _SUBORDINATEIO_H
+
+#include <config.h>
+
+#ifdef ENABLE_SUBIDS
+
+#include <sys/types.h>
+
+extern int sub_uid_close(void);
+extern bool have_sub_uids(const char *owner, uid_t start, unsigned long count);
+extern bool sub_uid_file_present (void);
+extern bool sub_uid_assigned(const char *owner);
+extern int sub_uid_lock (void);
+extern int sub_uid_setdbname (const char *filename);
+extern /*@observer@*/const char *sub_uid_dbname (void);
+extern int sub_uid_open (int mode);
+extern int sub_uid_unlock (void);
+extern int sub_uid_add (const char *owner, uid_t start, unsigned long count);
+extern int sub_uid_remove (const char *owner, uid_t start, unsigned long count);
+extern uid_t sub_uid_find_free_range(uid_t min, uid_t max, unsigned long count);
+
+extern int sub_gid_close(void);
+extern bool have_sub_gids(const char *owner, gid_t start, unsigned long count);
+extern bool sub_gid_file_present (void);
+extern bool sub_gid_assigned(const char *owner);
+extern int sub_gid_lock (void);
+extern int sub_gid_setdbname (const char *filename);
+extern /*@observer@*/const char *sub_gid_dbname (void);
+extern int sub_gid_open (int mode);
+extern int sub_gid_unlock (void);
+extern int sub_gid_add (const char *owner, gid_t start, unsigned long count);
+extern int sub_gid_remove (const char *owner, gid_t start, unsigned long count);
+extern uid_t sub_gid_find_free_range(gid_t min, gid_t max, unsigned long count);
+#endif /* ENABLE_SUBIDS */
+
+#endif
diff --git a/lib/tcbfuncs.c b/lib/tcbfuncs.c
new file mode 100644
index 0000000..2f694bd
--- /dev/null
+++ b/lib/tcbfuncs.c
@@ -0,0 +1,613 @@
+/*
+ * Copyright (c) 2001 Rafal Wojtczuk, Solar Designer
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 THE COPYRIGHT
+ * HOLDERS 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.
+ */
+
+#define _GNU_SOURCE
+
+#include <errno.h>
+#include <fcntl.h>
+#include <grp.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <tcb.h>
+#include <unistd.h>
+
+#include "config.h"
+
+#include "defines.h"
+#include "prototypes.h"
+#include "getdef.h"
+#include "shadowio.h"
+#include "tcbfuncs.h"
+
+#define SHADOWTCB_HASH_BY 1000
+#define SHADOWTCB_LOCK_SUFFIX ".lock"
+
+static /*@null@*//*@only@*/char *stored_tcb_user = NULL;
+
+shadowtcb_status shadowtcb_drop_priv (void)
+{
+ if (!getdef_bool ("USE_TCB")) {
+ return SHADOWTCB_SUCCESS;
+ }
+
+ if (NULL != stored_tcb_user) {
+ if (tcb_drop_priv (stored_tcb_user) == 0) {
+ return SHADOWTCB_SUCCESS;
+ }
+ }
+
+ return SHADOWTCB_FAILURE;
+}
+
+shadowtcb_status shadowtcb_gain_priv (void)
+{
+ if (!getdef_bool ("USE_TCB")) {
+ return SHADOWTCB_SUCCESS;
+ }
+
+ return (tcb_gain_priv () == 0) ? SHADOWTCB_SUCCESS : SHADOWTCB_FAILURE;
+}
+
+/* In case something goes wrong, we return immediately, not polluting the
+ * code with free(). All errors are fatal, so the application is expected
+ * to exit soon.
+ */
+#define OUT_OF_MEMORY do { \
+ fprintf (stderr, _("%s: out of memory\n"), Prog); \
+ (void) fflush (stderr); \
+} while (false)
+
+/* Returns user's tcb directory path relative to TCB_DIR. */
+static /*@null@*/ char *shadowtcb_path_rel (const char *name, uid_t uid)
+{
+ char *ret;
+
+ if (!getdef_bool ("TCB_SYMLINKS") || uid < SHADOWTCB_HASH_BY) {
+ if (asprintf (&ret, "%s", name) == -1) {
+ OUT_OF_MEMORY;
+ return NULL;
+ }
+ } else if (uid < SHADOWTCB_HASH_BY * SHADOWTCB_HASH_BY) {
+ if (asprintf (&ret, ":%dK/%s",
+ uid / SHADOWTCB_HASH_BY, name) == -1) {
+ OUT_OF_MEMORY;
+ return NULL;
+ }
+ } else {
+ if (asprintf (&ret, ":%dM/:%dK/%s",
+ uid / (SHADOWTCB_HASH_BY * SHADOWTCB_HASH_BY),
+ (uid % (SHADOWTCB_HASH_BY * SHADOWTCB_HASH_BY)) / SHADOWTCB_HASH_BY,
+ name) == -1) {
+ OUT_OF_MEMORY;
+ return NULL;
+ }
+ }
+ return ret;
+}
+
+static /*@null@*/ char *shadowtcb_path_rel_existing (const char *name)
+{
+ char *path, *rval;
+ struct stat st;
+ char link[8192];
+ ssize_t ret;
+
+ if (asprintf (&path, TCB_DIR "/%s", name) == -1) {
+ OUT_OF_MEMORY;
+ return NULL;
+ }
+ if (lstat (path, &st) != 0) {
+ fprintf (stderr,
+ _("%s: Cannot stat %s: %s\n"),
+ Prog, path, strerror (errno));
+ free (path);
+ return NULL;
+ }
+ if (S_ISDIR (st.st_mode)) {
+ free (path);
+ rval = strdup (name);
+ if (NULL == rval) {
+ OUT_OF_MEMORY;
+ return NULL;
+ }
+ return rval;
+ }
+ if (!S_ISLNK (st.st_mode)) {
+ fprintf (stderr,
+ _("%s: %s is neither a directory, nor a symlink.\n"),
+ Prog, path);
+ free (path);
+ return NULL;
+ }
+ ret = readlink (path, link, sizeof (link) - 1);
+ if (-1 == ret) {
+ fprintf (stderr,
+ _("%s: Cannot read symbolic link %s: %s\n"),
+ Prog, path, strerror (errno));
+ free (path);
+ return NULL;
+ }
+ free (path);
+ if ((size_t)ret >= sizeof(link) - 1) {
+ link[sizeof(link) - 1] = '\0';
+ fprintf (stderr,
+ _("%s: Suspiciously long symlink: %s\n"),
+ Prog, link);
+ return NULL;
+ }
+ link[(size_t)ret] = '\0';
+ rval = strdup (link);
+ if (NULL == rval) {
+ OUT_OF_MEMORY;
+ return NULL;
+ }
+ return rval;
+}
+
+static /*@null@*/ char *shadowtcb_path (const char *name, uid_t uid)
+{
+ char *ret, *rel;
+
+ rel = shadowtcb_path_rel (name, uid);
+ if (NULL == rel) {
+ return NULL;
+ }
+ if (asprintf (&ret, TCB_DIR "/%s", rel) == -1) {
+ OUT_OF_MEMORY;
+ free (rel);
+ return NULL;
+ }
+ free (rel);
+ return ret;
+}
+
+static /*@null@*/ char *shadowtcb_path_existing (const char *name)
+{
+ char *ret, *rel;
+
+ rel = shadowtcb_path_rel_existing (name);
+ if (NULL == rel) {
+ return NULL;
+ }
+ if (asprintf (&ret, TCB_DIR "/%s", rel) == -1) {
+ OUT_OF_MEMORY;
+ free (rel);
+ return NULL;
+ }
+ free (rel);
+ return ret;
+}
+
+static shadowtcb_status mkdir_leading (const char *name, uid_t uid)
+{
+ char *ind, *dir, *ptr, *path = shadowtcb_path_rel (name, uid);
+ struct stat st;
+
+ if (NULL == path) {
+ return SHADOWTCB_FAILURE;
+ }
+ ptr = path;
+ if (stat (TCB_DIR, &st) != 0) {
+ fprintf (stderr,
+ _("%s: Cannot stat %s: %s\n"),
+ Prog, TCB_DIR, strerror (errno));
+ goto out_free_path;
+ }
+ while ((ind = strchr (ptr, '/'))) {
+ *ind = '\0';
+ if (asprintf (&dir, TCB_DIR "/%s", path) == -1) {
+ OUT_OF_MEMORY;
+ return SHADOWTCB_FAILURE;
+ }
+ if ((mkdir (dir, 0700) != 0) && (errno != EEXIST)) {
+ fprintf (stderr,
+ _("%s: Cannot create directory %s: %s\n"),
+ Prog, dir, strerror (errno));
+ goto out_free_dir;
+ }
+ if (chown (dir, 0, st.st_gid) != 0) {
+ fprintf (stderr,
+ _("%s: Cannot change owner of %s: %s\n"),
+ Prog, dir, strerror (errno));
+ goto out_free_dir;
+ }
+ if (chmod (dir, 0711) != 0) {
+ fprintf (stderr,
+ _("%s: Cannot change mode of %s: %s\n"),
+ Prog, dir, strerror (errno));
+ goto out_free_dir;
+ }
+ free (dir);
+ *ind = '/';
+ ptr = ind + 1;
+ }
+ free (path);
+ return SHADOWTCB_SUCCESS;
+out_free_dir:
+ free (dir);
+out_free_path:
+ free (path);
+ return SHADOWTCB_FAILURE;
+}
+
+static shadowtcb_status unlink_suffs (const char *user)
+{
+ static char *suffs[] = { "+", "-", SHADOWTCB_LOCK_SUFFIX };
+ char *tmp;
+ int i;
+
+ for (i = 0; i < 3; i++) {
+ if (asprintf (&tmp, TCB_FMT "%s", user, suffs[i]) == -1) {
+ OUT_OF_MEMORY;
+ return SHADOWTCB_FAILURE;
+ }
+ if ((unlink (tmp) != 0) && (errno != ENOENT)) {
+ fprintf (stderr,
+ _("%s: unlink: %s: %s\n"),
+ Prog, tmp, strerror (errno));
+ free (tmp);
+ return SHADOWTCB_FAILURE;
+ }
+ free (tmp);
+ }
+
+ return SHADOWTCB_SUCCESS;
+}
+
+/* path should be a relative existing tcb directory */
+static shadowtcb_status rmdir_leading (char *path)
+{
+ char *ind, *dir;
+ shadowtcb_status ret = SHADOWTCB_SUCCESS;
+ while ((ind = strrchr (path, '/'))) {
+ *ind = '\0';
+ if (asprintf (&dir, TCB_DIR "/%s", path) == -1) {
+ OUT_OF_MEMORY;
+ return SHADOWTCB_FAILURE;
+ }
+ if (rmdir (dir) != 0) {
+ if (errno != ENOTEMPTY) {
+ fprintf (stderr,
+ _("%s: Cannot remove directory %s: %s\n"),
+ Prog, dir, strerror (errno));
+ ret = SHADOWTCB_FAILURE;
+ }
+ free (dir);
+ break;
+ }
+ free (dir);
+ }
+ return ret;
+}
+
+static shadowtcb_status move_dir (const char *user_newname, uid_t user_newid)
+{
+ char *olddir = NULL, *newdir = NULL;
+ char *real_old_dir = NULL, *real_new_dir = NULL;
+ char *real_old_dir_rel = NULL, *real_new_dir_rel = NULL;
+ uid_t old_uid, the_newid;
+ struct stat oldmode;
+ shadowtcb_status ret = SHADOWTCB_FAILURE;
+
+ if (NULL == stored_tcb_user) {
+ return SHADOWTCB_FAILURE;
+ }
+ if (asprintf (&olddir, TCB_DIR "/%s", stored_tcb_user) == -1) {
+ goto out_free_nomem;
+ }
+ if (stat (olddir, &oldmode) != 0) {
+ fprintf (stderr,
+ _("%s: Cannot stat %s: %s\n"),
+ Prog, olddir, strerror (errno));
+ goto out_free;
+ }
+ old_uid = oldmode.st_uid;
+ the_newid = (user_newid == -1) ? old_uid : user_newid;
+ real_old_dir = shadowtcb_path_existing (stored_tcb_user);
+ if (NULL == real_old_dir) {
+ goto out_free;
+ }
+ real_new_dir = shadowtcb_path (user_newname, the_newid);
+ if (NULL == real_new_dir) {
+ goto out_free;
+ }
+ if (strcmp (real_old_dir, real_new_dir) == 0) {
+ ret = SHADOWTCB_SUCCESS;
+ goto out_free;
+ }
+ real_old_dir_rel = shadowtcb_path_rel_existing (stored_tcb_user);
+ if (NULL == real_old_dir_rel) {
+ goto out_free;
+ }
+ if (mkdir_leading (user_newname, the_newid) == SHADOWTCB_FAILURE) {
+ goto out_free;
+ }
+ if (rename (real_old_dir, real_new_dir) != 0) {
+ fprintf (stderr,
+ _("%s: Cannot rename %s to %s: %s\n"),
+ Prog, real_old_dir, real_new_dir, strerror (errno));
+ goto out_free;
+ }
+ if (rmdir_leading (real_old_dir_rel) == SHADOWTCB_FAILURE) {
+ goto out_free;
+ }
+ if ((unlink (olddir) != 0) && (errno != ENOENT)) {
+ fprintf (stderr,
+ _("%s: Cannot remove %s: %s\n"),
+ Prog, olddir, strerror (errno));
+ goto out_free;
+ }
+ if (asprintf (&newdir, TCB_DIR "/%s", user_newname) == -1) {
+ goto out_free_nomem;
+ }
+ real_new_dir_rel = shadowtcb_path_rel (user_newname, the_newid);
+ if (NULL == real_new_dir_rel) {
+ goto out_free;
+ }
+ if ( (strcmp (real_new_dir, newdir) != 0)
+ && (symlink (real_new_dir_rel, newdir) != 0)) {
+ fprintf (stderr,
+ _("%s: Cannot create symbolic link %s: %s\n"),
+ Prog, real_new_dir_rel, strerror (errno));
+ goto out_free;
+ }
+ ret = SHADOWTCB_SUCCESS;
+ goto out_free;
+out_free_nomem:
+ OUT_OF_MEMORY;
+out_free:
+ free (olddir);
+ free (newdir);
+ free (real_old_dir);
+ free (real_new_dir);
+ free (real_old_dir_rel);
+ free (real_new_dir_rel);
+ return ret;
+}
+
+shadowtcb_status shadowtcb_set_user (const char* name)
+{
+ char *buf;
+ shadowtcb_status retval;
+
+ if (!getdef_bool ("USE_TCB")) {
+ return SHADOWTCB_SUCCESS;
+ }
+
+ if (NULL != stored_tcb_user) {
+ free (stored_tcb_user);
+ }
+
+ stored_tcb_user = strdup (name);
+ if (NULL == stored_tcb_user) {
+ OUT_OF_MEMORY;
+ return SHADOWTCB_FAILURE;
+ }
+ if (asprintf (&buf, TCB_FMT, name) == -1) {
+ OUT_OF_MEMORY;
+ return SHADOWTCB_FAILURE;
+ }
+
+ retval = (spw_setdbname (buf) != 0) ? SHADOWTCB_SUCCESS : SHADOWTCB_FAILURE;
+ free (buf);
+ return retval;
+}
+
+/* tcb directory must be empty before shadowtcb_remove is called. */
+shadowtcb_status shadowtcb_remove (const char *name)
+{
+ shadowtcb_status ret = SHADOWTCB_SUCCESS;
+ char *path = shadowtcb_path_existing (name);
+ char *rel = shadowtcb_path_rel_existing (name);
+ if ((NULL == path) || (NULL == rel) || (rmdir (path) != 0)) {
+ return SHADOWTCB_FAILURE;
+ }
+ if (rmdir_leading (rel) == SHADOWTCB_FAILURE) {
+ return SHADOWTCB_FAILURE;
+ }
+ free (path);
+ free (rel);
+ if (asprintf (&path, TCB_DIR "/%s", name) == -1) {
+ OUT_OF_MEMORY;
+ return SHADOWTCB_FAILURE;
+ }
+ if ((unlink (path) != 0) && (errno != ENOENT)) {
+ ret = SHADOWTCB_FAILURE;
+ }
+ free (path);
+ return ret;
+}
+
+shadowtcb_status shadowtcb_move (/*@NULL@*/const char *user_newname, uid_t user_newid)
+{
+ struct stat dirmode, filemode;
+ char *tcbdir, *shadow;
+ shadowtcb_status ret = SHADOWTCB_FAILURE;
+
+ if (!getdef_bool ("USE_TCB")) {
+ return SHADOWTCB_SUCCESS;
+ }
+ if (NULL == stored_tcb_user) {
+ return SHADOWTCB_FAILURE;
+ }
+ if (NULL == user_newname) {
+ user_newname = stored_tcb_user;
+ }
+ if (move_dir (user_newname, user_newid) == SHADOWTCB_FAILURE) {
+ return SHADOWTCB_FAILURE;
+ }
+ if (-1 == user_newid) {
+ return SHADOWTCB_SUCCESS;
+ }
+ if ( (asprintf (&tcbdir, TCB_DIR "/%s", user_newname) == -1)
+ || (asprintf (&shadow, TCB_FMT, user_newname) == -1)) {
+ OUT_OF_MEMORY;
+ return SHADOWTCB_FAILURE;
+ }
+ if (stat (tcbdir, &dirmode) != 0) {
+ fprintf (stderr,
+ _("%s: Cannot stat %s: %s\n"),
+ Prog, tcbdir, strerror (errno));
+ goto out_free;
+ }
+ if (chown (tcbdir, 0, 0) != 0) {
+ fprintf (stderr,
+ _("%s: Cannot change owners of %s: %s\n"),
+ Prog, tcbdir, strerror (errno));
+ goto out_free;
+ }
+ if (chmod (tcbdir, 0700) != 0) {
+ fprintf (stderr,
+ _("%s: Cannot change mode of %s: %s\n"),
+ Prog, tcbdir, strerror (errno));
+ goto out_free;
+ }
+ if (lstat (shadow, &filemode) != 0) {
+ if (errno != ENOENT) {
+ fprintf (stderr,
+ _("%s: Cannot lstat %s: %s\n"),
+ Prog, shadow, strerror (errno));
+ goto out_free;
+ }
+ fprintf (stderr,
+ _("%s: Warning, user %s has no tcb shadow file.\n"),
+ Prog, user_newname);
+ } else {
+ if (!S_ISREG (filemode.st_mode) ||
+ filemode.st_nlink != 1) {
+ fprintf (stderr,
+ _("%s: Emergency: %s's tcb shadow is not a "
+ "regular file with st_nlink=1.\n"
+ "The account is left locked.\n"),
+ Prog, user_newname);
+ goto out_free;
+ }
+ if (chown (shadow, user_newid, filemode.st_gid) != 0) {
+ fprintf (stderr,
+ _("%s: Cannot change owner of %s: %s\n"),
+ Prog, shadow, strerror (errno));
+ goto out_free;
+ }
+ if (chmod (shadow, filemode.st_mode & 07777) != 0) {
+ fprintf (stderr,
+ _("%s: Cannot change mode of %s: %s\n"),
+ Prog, shadow, strerror (errno));
+ goto out_free;
+ }
+ }
+ if (unlink_suffs (user_newname) == SHADOWTCB_FAILURE) {
+ goto out_free;
+ }
+ if (chown (tcbdir, user_newid, dirmode.st_gid) != 0) {
+ fprintf (stderr,
+ _("%s: Cannot change owner of %s: %s\n"),
+ Prog, tcbdir, strerror (errno));
+ goto out_free;
+ }
+ ret = SHADOWTCB_SUCCESS;
+out_free:
+ free (tcbdir);
+ free (shadow);
+ return ret;
+}
+
+shadowtcb_status shadowtcb_create (const char *name, uid_t uid)
+{
+ char *dir, *shadow;
+ struct stat tcbdir_stat;
+ gid_t shadowgid, authgid;
+ struct group *gr;
+ int fd;
+ shadowtcb_status ret = SHADOWTCB_FAILURE;
+
+ if (!getdef_bool ("USE_TCB")) {
+ return SHADOWTCB_SUCCESS;
+ }
+ if (stat (TCB_DIR, &tcbdir_stat) != 0) {
+ fprintf (stderr,
+ _("%s: Cannot stat %s: %s\n"),
+ Prog, TCB_DIR, strerror (errno));
+ return SHADOWTCB_FAILURE;
+ }
+ shadowgid = tcbdir_stat.st_gid;
+ authgid = shadowgid;
+ if (getdef_bool ("TCB_AUTH_GROUP")) {
+ gr = getgrnam ("auth");
+ if (NULL != gr) {
+ authgid = gr->gr_gid;
+ }
+ }
+
+ if ( (asprintf (&dir, TCB_DIR "/%s", name) == -1)
+ || (asprintf (&shadow, TCB_FMT, name) == -1)) {
+ OUT_OF_MEMORY;
+ return SHADOWTCB_FAILURE;
+ }
+ if (mkdir (dir, 0700) != 0) {
+ fprintf (stderr,
+ _("%s: mkdir: %s: %s\n"),
+ Prog, dir, strerror (errno));
+ goto out_free;
+ }
+ fd = open (shadow, O_RDWR | O_CREAT | O_TRUNC, 0600);
+ if (fd < 0) {
+ fprintf (stderr,
+ _("%s: Cannot open %s: %s\n"),
+ Prog, shadow, strerror (errno));
+ goto out_free;
+ }
+ close (fd);
+ if (chown (shadow, 0, authgid) != 0) {
+ fprintf (stderr,
+ _("%s: Cannot change owner of %s: %s\n"),
+ Prog, shadow, strerror (errno));
+ goto out_free;
+ }
+ if (chmod (shadow, (mode_t) ((authgid == shadowgid) ? 0600 : 0640)) != 0) {
+ fprintf (stderr,
+ _("%s: Cannot change mode of %s: %s\n"),
+ Prog, shadow, strerror (errno));
+ goto out_free;
+ }
+ if (chown (dir, 0, authgid) != 0) {
+ fprintf (stderr,
+ _("%s: Cannot change owner of %s: %s\n"),
+ Prog, dir, strerror (errno));
+ goto out_free;
+ }
+ if (chmod (dir, (mode_t) ((authgid == shadowgid) ? 02700 : 02710)) != 0) {
+ fprintf (stderr,
+ _("%s: Cannot change mode of %s: %s\n"),
+ Prog, dir, strerror (errno));
+ goto out_free;
+ }
+ if ( (shadowtcb_set_user (name) == SHADOWTCB_FAILURE)
+ || (shadowtcb_move (NULL, uid) == SHADOWTCB_FAILURE)) {
+ goto out_free;
+ }
+ ret = SHADOWTCB_SUCCESS;
+out_free:
+ free (dir);
+ free (shadow);
+ return ret;
+}
+
diff --git a/lib/tcbfuncs.h b/lib/tcbfuncs.h
new file mode 100644
index 0000000..6324bc1
--- /dev/null
+++ b/lib/tcbfuncs.h
@@ -0,0 +1,19 @@
+#ifndef _TCBFUNCS_H
+#define _TCBFUNCS_H
+
+#include <sys/types.h>
+
+typedef enum {
+ SHADOWTCB_FAILURE = 0,
+ SHADOWTCB_SUCCESS = 1
+} shadowtcb_status;
+
+extern shadowtcb_status shadowtcb_drop_priv (void);
+extern shadowtcb_status shadowtcb_gain_priv (void);
+extern shadowtcb_status shadowtcb_set_user (const char *name);
+extern shadowtcb_status shadowtcb_remove (const char *name);
+extern shadowtcb_status shadowtcb_move (/*@null@*/const char *user_newname,
+ uid_t user_newid);
+extern shadowtcb_status shadowtcb_create (const char *name, uid_t uid);
+
+#endif
diff --git a/lib/utent.c b/lib/utent.c
new file mode 100644
index 0000000..45af260
--- /dev/null
+++ b/lib/utent.c
@@ -0,0 +1,93 @@
+/*
+ * Copyright (c) 1993 - 1994, Julianne Frances Haugh
+ * Copyright (c) 1996 - 1998, Marek Michałkiewicz
+ * Copyright (c) 2005 , Tomasz Kłoczko
+ * 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. The name of the copyright holders or contributors may not be used to
+ * endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 THE COPYRIGHT
+ * HOLDERS 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.
+ */
+
+#include <config.h>
+
+#ifndef HAVE_GETUTENT
+
+#include "defines.h"
+#include <stdio.h>
+#include <fcntl.h>
+#include <utmp.h>
+
+#ifndef lint
+static char rcsid[] = "$Id$";
+#endif
+
+static int utmp_fd = -1;
+static struct utmp utmp_buf;
+
+/*
+ * setutent - open or rewind the utmp file
+ */
+
+void setutent (void)
+{
+ if (utmp_fd == -1)
+ if ((utmp_fd = open (_UTMP_FILE, O_RDWR)) == -1)
+ utmp_fd = open (_UTMP_FILE, O_RDONLY);
+
+ if (utmp_fd != -1)
+ lseek (utmp_fd, (off_t) 0L, SEEK_SET);
+}
+
+/*
+ * endutent - close the utmp file
+ */
+
+void endutent (void)
+{
+ if (utmp_fd != -1)
+ close (utmp_fd);
+
+ utmp_fd = -1;
+}
+
+/*
+ * getutent - get the next record from the utmp file
+ */
+
+struct utmp *getutent (void)
+{
+ if (utmp_fd == -1)
+ setutent ();
+
+ if (utmp_fd == -1)
+ return 0;
+
+ if (read (utmp_fd, &utmp_buf, sizeof utmp_buf) != sizeof utmp_buf)
+ return 0;
+
+ return &utmp_buf;
+}
+#else
+extern int errno; /* warning: ANSI C forbids an empty source file */
+#endif