diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-06 02:22:06 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-06 02:22:06 +0000 |
commit | 741c1ef7a4f2ac316ad6e557ddbe03023413478d (patch) | |
tree | 38890f681daa26c57e865b4feca10d0ca53e1046 /lib | |
parent | Initial commit. (diff) | |
download | shadow-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.pro | 5 | ||||
-rw-r--r-- | lib/Makefile.am | 69 | ||||
-rw-r--r-- | lib/commonio.c | 1271 | ||||
-rw-r--r-- | lib/commonio.h | 170 | ||||
-rw-r--r-- | lib/defines.h | 385 | ||||
-rw-r--r-- | lib/encrypt.c | 95 | ||||
-rw-r--r-- | lib/exitcodes.h | 49 | ||||
-rw-r--r-- | lib/faillog.h | 57 | ||||
-rw-r--r-- | lib/fields.c | 130 | ||||
-rw-r--r-- | lib/fputsx.c | 90 | ||||
-rw-r--r-- | lib/get_gid.c | 54 | ||||
-rw-r--r-- | lib/get_pid.c | 54 | ||||
-rw-r--r-- | lib/get_uid.c | 54 | ||||
-rw-r--r-- | lib/getdef.c | 539 | ||||
-rw-r--r-- | lib/getdef.h | 47 | ||||
-rw-r--r-- | lib/getlong.c | 59 | ||||
-rw-r--r-- | lib/getulong.c | 62 | ||||
-rw-r--r-- | lib/groupio.c | 462 | ||||
-rw-r--r-- | lib/groupio.h | 55 | ||||
-rw-r--r-- | lib/groupmem.c | 106 | ||||
-rw-r--r-- | lib/gshadow.c | 529 | ||||
-rw-r--r-- | lib/gshadow_.h | 75 | ||||
-rw-r--r-- | lib/lockpw.c | 108 | ||||
-rw-r--r-- | lib/nscd.c | 57 | ||||
-rw-r--r-- | lib/nscd.h | 13 | ||||
-rw-r--r-- | lib/pam_defs.h | 58 | ||||
-rw-r--r-- | lib/port.c | 477 | ||||
-rw-r--r-- | lib/port.h | 83 | ||||
-rw-r--r-- | lib/prototypes.h | 440 | ||||
-rw-r--r-- | lib/pwauth.c | 234 | ||||
-rw-r--r-- | lib/pwauth.h | 66 | ||||
-rw-r--r-- | lib/pwio.c | 226 | ||||
-rw-r--r-- | lib/pwio.h | 55 | ||||
-rw-r--r-- | lib/pwmem.c | 106 | ||||
-rw-r--r-- | lib/selinux.c | 103 | ||||
-rw-r--r-- | lib/semanage.c | 378 | ||||
-rw-r--r-- | lib/sgetgrent.c | 154 | ||||
-rw-r--r-- | lib/sgetpwent.c | 122 | ||||
-rw-r--r-- | lib/sgetspent.c | 208 | ||||
-rw-r--r-- | lib/sgroupio.c | 328 | ||||
-rw-r--r-- | lib/sgroupio.h | 52 | ||||
-rw-r--r-- | lib/shadow.c | 557 | ||||
-rw-r--r-- | lib/shadowio.c | 267 | ||||
-rw-r--r-- | lib/shadowio.h | 53 | ||||
-rw-r--r-- | lib/shadowmem.c | 89 | ||||
-rw-r--r-- | lib/spawn.c | 82 | ||||
-rw-r--r-- | lib/subordinateio.c | 701 | ||||
-rw-r--r-- | lib/subordinateio.h | 41 | ||||
-rw-r--r-- | lib/tcbfuncs.c | 613 | ||||
-rw-r--r-- | lib/tcbfuncs.h | 19 | ||||
-rw-r--r-- | lib/utent.c | 93 |
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 ⦥ +} + +/* + * 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 |