* NEWS: Add support for TCB.
* lib/tcbfuncs.h, lib/tcbfuncs.c, lib/Makefile.am: New library to
support TCB.
* lib/prototypes, libmisc/copydir.c (remove_tree): Add boolean
parameter remove_root.
* configure.in: Add conditional WITH_TCB.
* src/userdel.c, src/usermod.c: Add support for TCB. Update call to
remove_tree().
* src/pwconv.c, src/pwunconv.c: Should not be used with TCB enabled.
* src/vipw.c: Add support for TCB. Update call to remove_tree().
* src/useradd.c: Add support for TCB. Open the shadow file outside
of open_files().
* src/chage.c: Add support for TCB.
* src/Makefile.am: Install passwd sgid shadow when TCB is enabled.
* lib/getdefs.c, man/vipw.8.xml, man/login.defs.5.xml,
man/login.defs/TCB_AUTH_GROUP.xml, man/login.defs/USE_TCB.xml,
man/login.defs/TCB_SYMLINKS.xml, man/generate_mans.mak,
man/generate_mans.deps, man/Makefile.am: New configuration
parameters: TCB_AUTH_GROUP, TCB_SYMLINKS, USE_TCB.
* lib/shadowio.c, lib/commonio.c: Add support for TCB.
-2009-01-24 Nicolas François <nicolas.francois@centraliens.net>
+2010-01-30 Paweł Hajdan, Jr. <phajdan.jr@gentoo.org>
+
+ * NEWS: Add support for TCB.
+ * lib/tcbfuncs.h, lib/tcbfuncs.c, lib/Makefile.am: New library to
+ support TCB.
+ * lib/prototypes, libmisc/copydir.c (remove_tree): Add boolean
+ parameter remove_root.
+ * configure.in: Add conditional WITH_TCB.
+ * src/userdel.c, src/usermod.c: Add support for TCB. Update call to
+ remove_tree().
+ * src/pwconv.c, src/pwunconv.c: Should not be used with TCB enabled.
+ * src/vipw.c: Add support for TCB. Update call to remove_tree().
+ * src/useradd.c: Add support for TCB. Open the shadow file outside
+ of open_files().
+ * src/chage.c: Add support for TCB.
+ * src/Makefile.am: Install passwd sgid shadow when TCB is enabled.
+ * lib/getdefs.c, man/vipw.8.xml, man/login.defs.5.xml,
+ man/login.defs/TCB_AUTH_GROUP.xml, man/login.defs/USE_TCB.xml,
+ man/login.defs/TCB_SYMLINKS.xml, man/generate_mans.mak,
+ man/generate_mans.deps, man/Makefile.am: New configuration
+ parameters: TCB_AUTH_GROUP, TCB_SYMLINKS, USE_TCB.
+ * lib/shadowio.c, lib/commonio.c: Add support for TCB.
+
+2010-01-24 Nicolas François <nicolas.francois@centraliens.net>
* libmisc/env.c: Fix sanitize_env() noslash support. This fixes
Alioth#311740.
-2009-01-24 Nicolas François <nicolas.francois@centraliens.net>
+2010-01-24 Nicolas François <nicolas.francois@centraliens.net>
* src/su.c: Do not sanitize the environment. This breaks
--preserve-environment. This sanitation was disabled on Debian
Unixes will handle setuid executables properly. This fixes
Alioth#312287.
-2009-01-24 Nicolas François <nicolas.francois@centraliens.net>
+2010-01-24 Nicolas François <nicolas.francois@centraliens.net>
* libmisc/setupenv.c: Fix typo from 2009-11-01.
-2009-01-24 Paweł Hajdan, Jr. <phajdan.jr@gentoo.org>
+2010-01-24 Paweł Hajdan, Jr. <phajdan.jr@gentoo.org>
* configure.in: Add support for TCB in configure.in. Actual TCB
support will follow.
- general
* report usage error to stderr, but report usage help to stdout (and return
zero) when explicitly requested (e.g. with --help).
+ * initial support for tcb (http://openwall.com/tcb/).
- groupmod
* Fixed groupmod when configured with --enable-account-tools-setuid.
with_tcb="no"
fi
fi
+AM_CONDITIONAL(WITH_TCB, test x$with_tcb = xyes)
AC_SUBST(LIBPAM)
if test "$with_libpam" != "no"; then
shadowmem.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:
#
#ifdef WITH_SELINUX
#include <selinux/selinux.h>
#endif
+#ifdef WITH_TCB
+#include <tcb.h>
+#endif
#include "prototypes.h"
#include "commonio.h"
void *eptr = NULL;
int flags = mode;
size_t buflen;
+ int fd;
int saved_errno;
mode &= ~O_CREAT;
db->cursor = NULL;
db->changed = false;
- db->fp = fopen (db->filename, db->readonly ? "r" : "r+");
+ 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)) {
+ close(fd);
+ errno = EINVAL;
+ return 0;
+ }
+#endif
+ db->fp = fdopen(fd, db->readonly ? "r" : "r+");
+ saved_errno = errno;
+ if (!db->fp)
+ close(fd);
+ }
+ errno = saved_errno;
/*
* If O_CREAT was specified and the file didn't exist, it will be
#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
{NULL, NULL}
};
/* copydir.c */
extern int copy_tree (const char *src_root, const char *dst_root,
long int uid, long int gid);
-extern int remove_tree (const char *root);
+extern int remove_tree (const char *root, bool remove_root);
#ifdef WITH_SELINUX
extern int selinux_file_context (const char *dst_name);
#include <stdio.h>
#include "commonio.h"
#include "shadowio.h"
+#ifdef WITH_TCB
+#include <tcb.h>
+#include "tcbfuncs.h"
+#endif
static /*@null@*/ /*@only@*/void *shadow_dup (const void *ent)
{
int spw_lock (void)
{
- return commonio_lock (&shadow_db);
+#ifdef WITH_TCB
+ int retval = 0;
+
+ if (!getdef_bool("USE_TCB"))
+#endif
+ return commonio_lock (&shadow_db);
+#ifdef WITH_TCB
+ if (!shadowtcb_drop_priv())
+ return 0;
+ if (lckpwdf_tcb(shadow_db.filename) == 0) {
+ shadow_db.locked = 1;
+ retval = 1;
+ }
+ if (!shadowtcb_gain_priv())
+ return 0;
+ return retval;
+#endif
}
int spw_open (int mode)
{
- return commonio_open (&shadow_db, mode);
+ int retval = 0;
+#ifdef WITH_TCB
+ int use_tcb = getdef_bool("USE_TCB");
+
+ if (use_tcb && !shadowtcb_drop_priv() != 0)
+ return 0;
+#endif
+ retval = commonio_open (&shadow_db, mode);
+#ifdef WITH_TCB
+ if (use_tcb && !shadowtcb_gain_priv() != 0)
+ return 0;
+#endif
+ return retval;
}
/*@observer@*/ /*@null@*/const struct spwd *spw_locate (const char *name)
int spw_close (void)
{
- return commonio_close (&shadow_db);
+ int retval = 0;
+#ifdef WITH_TCB
+ int use_tcb = getdef_bool("USE_TCB");
+
+ if (use_tcb && !shadowtcb_drop_priv() != 0)
+ return 0;
+#endif
+ retval = commonio_close (&shadow_db);
+#ifdef WITH_TCB
+ if (use_tcb && !shadowtcb_gain_priv() != 0)
+ return 0;
+#endif
+ return retval;
}
int spw_unlock (void)
{
- return commonio_unlock (&shadow_db);
+#ifdef WITH_TCB
+ int retval = 0;
+
+ if (!getdef_bool("USE_TCB"))
+#endif
+ return commonio_unlock (&shadow_db);
+#ifdef WITH_TCB
+ if (!shadowtcb_drop_priv())
+ return 0;
+ if (ulckpwdf_tcb() == 0) {
+ shadow_db.locked = 0;
+ retval = 1;
+ }
+ if (!shadowtcb_gain_priv())
+ return 0;
+ return retval;
+#endif
}
struct commonio_entry *__spw_get_head (void)
/* Sort with respect to passwd ordering. */
int spw_sort ()
{
+#ifdef WITH_TCB
+ if (getdef_bool("USE_TCB"))
+ return 0;
+#endif
return commonio_sort_wrt (&shadow_db, __pw_get_db ());
}
--- /dev/null
+/*
+ * 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 "getdef.h"
+
+#define SHADOWTCB_HASH_BY 1000
+#define SHADOWTCB_LOCK_SUFFIX ".lock"
+
+static char *stored_tcb_user = NULL;
+
+int shadowtcb_drop_priv()
+{
+ if (!getdef_bool("USE_TCB"))
+ return 1;
+
+ if (stored_tcb_user)
+ return !tcb_drop_priv(stored_tcb_user);
+
+ return 0;
+}
+
+int shadowtcb_gain_priv()
+{
+ if (!getdef_bool("USE_TCB"))
+ return 1;
+ return !tcb_gain_priv();
+}
+
+/* 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, "Out of memory.\n"); \
+ fflush(stderr); \
+ return 0; \
+} while(0)
+
+/* Returns user's tcb directory path relative to TCB_DIR. */
+static char *shadowtcb_path_rel(const char *name, uid_t uid)
+{
+ char *ret;
+
+ if (!getdef_bool("TCB_SYMLINKS") || uid < SHADOWTCB_HASH_BY) {
+ asprintf(&ret, "%s", name);
+ } else if (uid < SHADOWTCB_HASH_BY * SHADOWTCB_HASH_BY) {
+ asprintf(&ret, ":%dK/%s", uid / SHADOWTCB_HASH_BY, name);
+ } else {
+ asprintf(&ret, ":%dM/:%dK/%s",
+ uid / (SHADOWTCB_HASH_BY * SHADOWTCB_HASH_BY),
+ (uid % (SHADOWTCB_HASH_BY * SHADOWTCB_HASH_BY)) / SHADOWTCB_HASH_BY,
+ name);
+ }
+ if (!ret) {
+ OUT_OF_MEMORY;
+ }
+ return ret;
+}
+
+static char *shadowtcb_path_rel_existing(const char *name)
+{
+ char *path, *rval;
+ struct stat st;
+ char link[8192];
+ int ret;
+
+ asprintf(&path, TCB_DIR "/%s", name);
+ if (!path) {
+ OUT_OF_MEMORY;
+ }
+ if (lstat(path, &st)) {
+ fprintf(stderr, "Cannot stat %s: %s\n", path, strerror(errno));
+ free(path);
+ return NULL;
+ }
+ if (S_ISDIR(st.st_mode)) {
+ free(path);
+ rval = strdup(name);
+ if (!rval) {
+ OUT_OF_MEMORY;
+ }
+ return rval;
+ }
+ if (!S_ISLNK(st.st_mode)) {
+ fprintf(stderr, "%s is neither a directory, nor a symlink.\n", path);
+ free(path);
+ return NULL;
+ }
+ ret = readlink(path, link, sizeof(link) - 1);
+ free(path);
+ if (ret == -1) {
+ perror("readlink");
+ return NULL;
+ }
+ if (ret >= sizeof(link) - 1) {
+ link[sizeof(link) - 1] = '\0';
+ fprintf(stderr, "Suspiciously long symlink: %s\n", link);
+ return NULL;
+ }
+ link[ret] = '\0';
+ rval = strdup(link);
+ if (!rval) {
+ OUT_OF_MEMORY;
+ }
+ return rval;
+}
+
+static char *shadowtcb_path(const char *name, uid_t uid)
+{
+ char *ret, *rel;
+
+ if (!(rel = shadowtcb_path_rel(name, uid)))
+ return 0;
+ asprintf(&ret, TCB_DIR "/%s", rel);
+ free(rel);
+ if (!ret) {
+ OUT_OF_MEMORY;
+ }
+ return ret;
+}
+
+static char *shadowtcb_path_existing(const char *name)
+{
+ char *ret, *rel;
+
+ if (!(rel = shadowtcb_path_rel_existing(name)))
+ return 0;
+ asprintf(&ret, TCB_DIR "/%s", rel);
+ free(rel);
+ if (!ret) {
+ OUT_OF_MEMORY;
+ }
+ return ret;
+}
+
+static int mkdir_leading(const char *name, uid_t uid)
+{
+ char *ind, *dir, *ptr, *path = shadowtcb_path_rel(name, uid);
+ struct stat st;
+
+ if (!path)
+ return 0;
+ ptr = path;
+ if (stat(TCB_DIR, &st)) {
+ perror("stat");
+ goto out_free_path;
+ }
+ while ((ind = strchr(ptr, '/'))) {
+ *ind = 0;
+ asprintf(&dir, TCB_DIR "/%s", path);
+ if (!dir) {
+ OUT_OF_MEMORY;
+ }
+ if (mkdir(dir, 0700) && errno != EEXIST) {
+ perror("mkdir");
+ goto out_free_dir;
+ }
+ if (chown(dir, 0, st.st_gid)) {
+ perror("chown");
+ goto out_free_dir;
+ }
+ if (chmod(dir, 0711)) {
+ perror("chmod");
+ goto out_free_dir;
+ }
+ free(dir);
+ *ind = '/';
+ ptr = ind + 1;
+ }
+ free(path);
+ return 1;
+out_free_dir:
+ free(dir);
+out_free_path:
+ free(path);
+ return 0;
+}
+
+static int unlink_suffs(const char *user)
+{
+ static char *suffs[] = { "+", "-", SHADOWTCB_LOCK_SUFFIX };
+ char *tmp;
+ int i;
+
+ for (i = 0; i < 3; i++) {
+ asprintf(&tmp, TCB_FMT "%s", user, suffs[i]);
+ if (!tmp) {
+ OUT_OF_MEMORY;
+ }
+ if (unlink(tmp) && errno != ENOENT) {
+ fprintf(stderr, "unlink: %s: %s\n", tmp,
+ strerror(errno));
+ free(tmp);
+ return 0;
+ }
+ free(tmp);
+ }
+
+ return 1;
+}
+
+/* path should be a relative existing tcb directory */
+static int rmdir_leading(char *path)
+{
+ char *ind, *dir;
+ int ret = 1;
+ while ((ind = strrchr(path, '/'))) {
+ *ind = 0;
+ asprintf(&dir, TCB_DIR "/%s", path);
+ if (!dir) {
+ OUT_OF_MEMORY;
+ }
+ if (rmdir(dir)) {
+ if (errno != ENOTEMPTY) {
+ perror("rmdir");
+ ret = 0;
+ }
+ free(dir);
+ break;
+ }
+ free(dir);
+ }
+ return ret;
+}
+
+static int 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;
+ int ret = 0;
+
+ asprintf(&olddir, TCB_DIR "/%s", stored_tcb_user);
+ if (!olddir)
+ goto out_free_nomem;
+ if (stat(olddir, &oldmode)) {
+ perror("stat");
+ goto out_free;
+ }
+ old_uid = oldmode.st_uid;
+ the_newid = (user_newid == -1) ? old_uid : user_newid;
+ if (!(real_old_dir = shadowtcb_path_existing(stored_tcb_user)))
+ goto out_free;
+ if (!(real_new_dir = shadowtcb_path(user_newname, the_newid)))
+ goto out_free;
+ if (!strcmp(real_old_dir, real_new_dir)) {
+ ret = 1;
+ goto out_free;
+ }
+ if (!(real_old_dir_rel = shadowtcb_path_rel_existing(stored_tcb_user)))
+ goto out_free;
+ if (!mkdir_leading(user_newname, the_newid))
+ goto out_free;
+ if (rename(real_old_dir, real_new_dir)) {
+ perror("rename");
+ goto out_free;
+ }
+ if (!rmdir_leading(real_old_dir_rel))
+ goto out_free;
+ if (unlink(olddir) && errno != ENOENT) {
+ perror("unlink");
+ goto out_free;
+ }
+ asprintf(&newdir, TCB_DIR "/%s", user_newname);
+ if (!newdir)
+ goto out_free_nomem;
+ if (!(real_new_dir_rel = shadowtcb_path_rel(user_newname, the_newid)))
+ goto out_free;
+ if (strcmp(real_new_dir, newdir) && symlink(real_new_dir_rel, newdir)) {
+ perror("symlink");
+ goto out_free;
+ }
+ ret = 1;
+ goto out_free;
+out_free_nomem:
+ fprintf(stderr, "Out of memory\n");
+ fflush(stderr);
+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;
+}
+
+int shadowtcb_set_user(const char* name)
+{
+ char *buf;
+ int retval;
+
+ if (!getdef_bool("USE_TCB"))
+ return 1;
+
+ if (stored_tcb_user)
+ free(stored_tcb_user);
+
+ stored_tcb_user = strdup(name);
+ if (!stored_tcb_user) {
+ OUT_OF_MEMORY;
+ }
+ asprintf(&buf, TCB_FMT, name);
+ if (!buf) {
+ OUT_OF_MEMORY;
+ }
+
+ retval = spw_setdbname(buf);
+ free(buf);
+ return retval;
+}
+
+/* tcb directory must be empty before shadowtcb_remove is called. */
+int shadowtcb_remove(const char *name)
+{
+ int ret = 1;
+ char *path = shadowtcb_path_existing(name);
+ char *rel = shadowtcb_path_rel_existing(name);
+ if (!path || !rel || rmdir(path))
+ return 0;
+ if (!rmdir_leading(rel))
+ return 0;
+ free(path);
+ free(rel);
+ asprintf(&path, TCB_DIR "/%s", name);
+ if (!path) {
+ OUT_OF_MEMORY;
+ }
+ if (unlink(path) && errno != ENOENT)
+ ret = 0;
+ free(path);
+ return ret;
+}
+
+int shadowtcb_move(const char *user_newname, uid_t user_newid)
+{
+ struct stat dirmode, filemode;
+ char *tcbdir, *shadow;
+ int ret = 0;
+
+ if (!getdef_bool("USE_TCB"))
+ return 1;
+ if (!user_newname)
+ user_newname = stored_tcb_user;
+ if (!move_dir(user_newname, user_newid))
+ return 0;
+ if (user_newid == -1)
+ return 1;
+ asprintf(&tcbdir, TCB_DIR "/%s", user_newname);
+ asprintf(&shadow, TCB_FMT, user_newname);
+ if (!tcbdir || !shadow) {
+ OUT_OF_MEMORY;
+ }
+ if (stat(tcbdir, &dirmode)) {
+ perror("stat");
+ goto out_free;
+ }
+ if (chown(tcbdir, 0, 0)) {
+ perror("chown");
+ goto out_free;
+ }
+ if (chmod(tcbdir, 0700)) {
+ perror("chmod");
+ goto out_free;
+ }
+ if (lstat(shadow, &filemode)) {
+ if (errno != ENOENT) {
+ perror("lstat");
+ goto out_free;
+ }
+ fprintf(stderr,
+ "Warning, user %s has no tcb shadow file.\n",
+ user_newname);
+ } else {
+ if (!S_ISREG(filemode.st_mode) ||
+ filemode.st_nlink != 1) {
+ fprintf(stderr,
+ "Emergency: %s's tcb shadow is not a regular file"
+ " with st_nlink=1.\n"
+ "The account is left locked.\n",
+ user_newname);
+ goto out_free;
+ }
+ if (chown(shadow, user_newid, filemode.st_gid)) {
+ perror("chown");
+ goto out_free;
+ }
+ if (chmod(shadow, filemode.st_mode & 07777)) {
+ perror("chmod");
+ goto out_free;
+ }
+ }
+ if (!unlink_suffs(user_newname))
+ goto out_free;
+ if (chown(tcbdir, user_newid, dirmode.st_gid)) {
+ perror("chown");
+ goto out_free;
+ }
+ ret = 1;
+out_free:
+ free(tcbdir);
+ free(shadow);
+ return ret;
+}
+
+int shadowtcb_create(const char *name, uid_t uid)
+{
+ char *dir, *shadow;
+ struct stat tcbdir_stat;
+ gid_t shadowgid, authgid;
+ struct group *gr;
+ int fd, ret = 0;
+
+ if (!getdef_bool("USE_TCB"))
+ return 1;
+ if (stat(TCB_DIR, &tcbdir_stat)) {
+ perror("stat");
+ return 0;
+ }
+ shadowgid = tcbdir_stat.st_gid;
+ if (getdef_bool("TCB_AUTH_GROUP") &&
+ (gr = getgrnam("auth"))) {
+ authgid = gr->gr_gid;
+ } else {
+ authgid = shadowgid;
+ }
+
+ asprintf(&dir, TCB_DIR "/%s", name);
+ asprintf(&shadow, TCB_FMT, name);
+ if (!dir || !shadow) {
+ OUT_OF_MEMORY;
+ }
+ if (mkdir(dir, 0700)) {
+ fprintf(stderr, "mkdir: %s: %s\n", dir, strerror(errno));
+ goto out_free;
+ return 0;
+ }
+ fd = open(shadow, O_RDWR | O_CREAT | O_TRUNC, 0600);
+ if (fd < 0) {
+ perror("open");
+ goto out_free;
+ }
+ close(fd);
+ if (chown(shadow, 0, authgid)) {
+ perror("chown");
+ goto out_free;
+ }
+ if (chmod(shadow, authgid == shadowgid ? 0600 : 0640)) {
+ perror("chmod");
+ goto out_free;
+ }
+ if (chown(dir, 0, authgid)) {
+ perror("chown");
+ goto out_free;
+ }
+ if (chmod(dir, authgid == shadowgid ? 02700 : 02710)) {
+ perror("chmod");
+ goto out_free;
+ }
+ if (!shadowtcb_set_user(name) || !shadowtcb_move(NULL, uid))
+ goto out_free;
+ ret = 1;
+out_free:
+ free(dir);
+ free(shadow);
+ return ret;
+}
--- /dev/null
+#ifndef _TCBFUNCS_H
+#define _TCBFUNCS_H
+
+#include <sys/types.h>
+
+extern int shadowtcb_drop_priv();
+extern int shadowtcb_gain_priv();
+extern int shadowtcb_set_user(const char *name);
+extern int shadowtcb_remove(const char *name);
+extern int shadowtcb_move(const char *user_newname, uid_t user_newid);
+extern int shadowtcb_create(const char *name, uid_t uid);
+
+#endif
* At the end, it deletes the root directory itself.
*/
-int remove_tree (const char *root)
+int remove_tree (const char *root, bool remove_root)
{
char *new_name = NULL;
int err = 0;
/*
* Recursively delete this directory.
*/
- if (remove_tree (new_name) != 0) {
+ if (remove_tree (new_name, true) != 0) {
err = -1;
break;
}
}
(void) closedir (dir);
- if (0 == err) {
+ if (remove_root && 0 == err) {
if (rmdir (root) != 0) {
err = -1;
}
SU_WHEEL_ONLY.xml \
SYSLOG_SG_ENAB.xml \
SYSLOG_SU_ENAB.xml \
+ TCB_AUTH_GROUP.xml \
+ TCB_SYMLINKS.xml \
TTYGROUP.xml \
TTYTYPE_FILE.xml \
UID_MAX.xml \
UMASK.xml \
USERDEL_CMD.xml \
USERGROUPS_ENAB.xml \
+ USE_TCB.xml \
SYS_GID_MAX.xml \
SYS_UID_MAX.xml
login.defs.5: login.defs.d/SYSLOG_SG_ENAB.xml
login.defs.5: login.defs.d/SYSLOG_SU_ENAB.xml
login.defs.5: login.defs.d/SYS_UID_MAX.xml
+login.defs.5: login.defs.d/TCB_AUTH_GROUP.xml
+login.defs.5: login.defs.d/TCB_SYMLINKS.xml
login.defs.5: login.defs.d/TTYGROUP.xml
login.defs.5: login.defs.d/TTYTYPE_FILE.xml
login.defs.5: login.defs.d/UID_MAX.xml
login.defs.5: login.defs.d/UMASK.xml
login.defs.5: login.defs.d/USERDEL_CMD.xml
login.defs.5: login.defs.d/USERGROUPS_ENAB.xml
+login.defs.5: login.defs.d/USE_TCB.xml
newgrp.1: login.defs.d/SYSLOG_SG_ENAB.xml
newusers.8: login.defs.d/ENCRYPT_METHOD.xml
newusers.8: login.defs.d/GID_MAX.xml
else
SHADOWGRP_COND=no_gshadow
endif
+if WITH_TCB
+TCB_COND=tcb
+else
+TCB_COND=no_tcb
+endif
if USE_SHA_CRYPT
SHA_CRYPT_COND=sha_crypt
%: %.xml-config Makefile config.xml
if ENABLE_REGENERATE_MAN
- $(XSLTPROC) --stringparam profile.condition "$(PAM_COND);$(SHADOWGRP_COND);$(SHA_CRYPT_COND)" \
+ $(XSLTPROC) --stringparam profile.condition "$(PAM_COND);$(SHADOWGRP_COND);$(TCB_COND);$(SHA_CRYPT_COND)" \
-nonet http://docbook.sourceforge.net/release/xsl/current/manpages/profile-docbook.xsl $<
else
@echo you need to run configure with --enable-man to generate man pages
<!ENTITY SYSLOG_SG_ENAB SYSTEM "login.defs.d/SYSLOG_SG_ENAB.xml">
<!ENTITY SYSLOG_SU_ENAB SYSTEM "login.defs.d/SYSLOG_SU_ENAB.xml">
<!ENTITY SYS_UID_MAX SYSTEM "login.defs.d/SYS_UID_MAX.xml">
+<!ENTITY TCB_AUTH_GROUP SYSTEM "login.defs.d/TCB_AUTH_GROUP.xml">
+<!ENTITY TCB_SYMLINKS SYSTEM "login.defs.d/TCB_SYMLINKS.xml">
<!ENTITY TTYGROUP SYSTEM "login.defs.d/TTYGROUP.xml">
<!ENTITY TTYTYPE_FILE SYSTEM "login.defs.d/TTYTYPE_FILE.xml">
<!ENTITY UID_MAX SYSTEM "login.defs.d/UID_MAX.xml">
<!ENTITY UMASK SYSTEM "login.defs.d/UMASK.xml">
<!ENTITY USERDEL_CMD SYSTEM "login.defs.d/USERDEL_CMD.xml">
<!ENTITY USERGROUPS_ENAB SYSTEM "login.defs.d/USERGROUPS_ENAB.xml">
+<!ENTITY USE_TCB SYSTEM "login.defs.d/USE_TCB.xml">
]>
<refentry id='login.defs.5'>
&SYS_UID_MAX; <!-- documents also SYS_UID_MIN -->
&SYSLOG_SG_ENAB;
&SYSLOG_SU_ENAB;
+ &TCB_AUTH_GROUP;
+ &TCB_SYMLINKS;
&TTYGROUP;
&TTYTYPE_FILE;
&UID_MAX; <!-- documents also UID_MIN -->
&UMASK;
&USERDEL_CMD;
&USERGROUPS_ENAB;
+ &USE_TCB;
</variablelist>
</refsect1>
<listitem>
<para>
PASS_MAX_DAYS PASS_MIN_DAYS PASS_WARN_AGE
+ <phrase condition="tcb">USE_TCB</phrase>
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>pwconv</term>
<listitem>
- <para>PASS_MAX_DAYS PASS_MIN_DAYS PASS_WARN_AGE</para>
+ <para>
+ PASS_MAX_DAYS PASS_MIN_DAYS PASS_WARN_AGE
+ <phrase condition="tcb">USE_TCB</phrase>
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry condition="tcb">
+ <term>pwunconv</term>
+ <listitem>
+ <para>
+ <phrase condition="tcb">USE_TCB</phrase>
+ </para>
</listitem>
</varlistentry>
- <!-- pwunconv: no variables -->
<varlistentry>
<term>su</term>
<listitem>
PASS_MAX_DAYS PASS_MIN_DAYS PASS_WARN_AGE
SYS_GID_MAX SYS_GID_MIN SYS_UID_MAX SYS_UID_MIN UID_MAX UID_MIN
UMASK
+ <phrase condition="tcb">TCB_AUTH_GROUP TCB_SYMLINK USE_TCB</phrase>
</para>
</listitem>
</varlistentry>
<para>
MAIL_DIR MAIL_FILE MAX_MEMBERS_PER_GROUP USERDEL_CMD
USERGROUPS_ENAB
+ <phrase condition="tcb">USE_TCB</phrase>
</para>
</listitem>
</varlistentry>
<listitem>
<para>
MAIL_DIR MAIL_FILE MAX_MEMBERS_PER_GROUP
+ <phrase condition="tcb">USE_TCB</phrase>
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry condition="tcb">
+ <term>vipw</term>
+ <listitem>
+ <para>
+ <phrase condition="tcb">USE_TCB</phrase>
</para>
</listitem>
</varlistentry>
- <!-- vipw / vigr: no variables (MAX_MEMBERS_PER_GROUP linked but not used) -->
</variablelist>
</refsect1>
--- /dev/null
+<!--
+ Copyright (c) 2010, Pawel Hajdan
+ 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.
+-->
+<varlistentry condition="tcb">
+ <term><option>TCB_AUTH_GROUP</option> (boolean)</term>
+ <listitem>
+ <para>
+ If <replaceable>yes</replaceable>, newly created tcb shadow files
+ will be group owned by the <replaceable>auth</replaceable> group.
+ </para>
+ </listitem>
+</varlistentry>
--- /dev/null
+<!--
+ Copyright (c) 2010, Pawel Hajdan
+ 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.
+-->
+<varlistentry condition="tcb">
+ <term><option>TCB_SYMLINKS</option> (boolean)</term>
+ <listitem>
+ <para>
+ If <replaceable>yes</replaceable>, the location of the user tcb
+ directory to be created will not be automatically set to /etc/tcb/user,
+ but will be computed depending on the UID of the user, according to
+ the following algorithm:
+ <programlisting>
+if ( UID is less than 1000) {
+ use /etc/tcb/user
+} else if ( UID is less than 1000000) {
+ kilos = UID / 1000
+ use /etc/tcb/:kilos/user
+ make symlink /etc/tcb/user to the above directory
+} else {
+ megas = UID / 1000000
+ kilos = ( UID / megas * 1000000 ) / 1000
+ use /etc/tcb/:megas/:kilos/user
+ make symlink /etc/tcb/user to the above directory
+}
+ </programlisting>
+ </para>
+ </listitem>
+</varlistentry>
--- /dev/null
+<!--
+ Copyright (c) 2010, Pawel Hajdan
+ 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.
+-->
+<varlistentry condition="tcb">
+ <term><option>USE_TCB</option> (boolean)</term>
+ <listitem>
+ <para>
+ If <replaceable>yes</replaceable>, the <citerefentry>
+ <refentrytitle>tcb</refentrytitle><manvolnum>5</manvolnum></citerefentry>
+ password shadowing scheme will be used.
+ </para>
+ </listitem>
+</varlistentry>
<para>Edit shadow or gshadow database.</para>
</listitem>
</varlistentry>
+ <varlistentry condition="tcb">
+ <term><option>-u</option>, <option>--user</option></term>
+ <listitem>
+ <para>Indicates which user's tcb shadow file to edit.</para>
+ </listitem>
+ </varlistentry>
</variablelist>
</refsect1>
<citerefentry>
<refentrytitle>passwd</refentrytitle><manvolnum>5</manvolnum>
</citerefentry>,
+ <citerefentry condition="tcb">
+ <refentrytitle>tcb</refentrytitle><manvolnum>5</manvolnum>
+ </citerefentry>,
<citerefentry>
<refentrytitle>shadow</refentrytitle><manvolnum>5</manvolnum>
</citerefentry>.
ubindir = ${prefix}/bin
usbindir = ${prefix}/sbin
suidperms = 4755
+sgidperms = 2755
INCLUDES = \
-I${top_srcdir}/lib \
suidubins += chage chgpasswd chpasswd groupadd groupdel groupmod newusers useradd userdel usermod
endif
+if WITH_TCB
+suidubins -= passwd
+shadowsgidubins = passwd
+endif
+
LDADD = $(INTLLIBS) \
+ $(LIBTCB) \
$(top_builddir)/libmisc/libmisc.a \
$(top_builddir)/lib/libshadow.la
AM_CPPFLAGS = -DLOCALEDIR=\"$(datadir)/locale\"
for i in $(suidubins); do \
chmod -f $(suidperms) $(DESTDIR)$(ubindir)/$$i; \
done
+if WITH_TCB
+ for i in $(shadowsgidubins); do \
+ chown root:shadow $(DESTDIR)$(ubindir)/$$i; \
+ chmod -f $(sgidperms) $(DESTDIR)$(ubindir)/$$i; \
+ done
+endif
#include "defines.h"
#include "pwio.h"
#include "shadowio.h"
+#ifdef WITH_TCB
+#include "tcbfuncs.h"
+#endif
/*@-exitarg@*/
#include "exitcodes.h"
}
STRFCPY (user_name, pw->pw_name);
+#ifdef WITH_TCB
+ if (!shadowtcb_set_user(pw->pw_name))
+ fail_exit(E_NOPERM);
+#endif
user_uid = pw->pw_uid;
sp = spw_locate (argv[optind]);
OPENLOG ("pwconv");
+ if (getdef_bool("USE_TCB")) {
+ fprintf(stderr, _("%s: can't work with tcb enabled\n"), Prog);
+ fail_exit(E_FAILURE);
+ }
+
if (pw_lock () == 0) {
fprintf (stderr,
_("%s: cannot lock %s; try again later.\n"),
OPENLOG ("pwunconv");
+ if (getdef_bool("USE_TCB")) {
+ fprintf(stderr, _("%s: can't work with tcb enabled\n"), Prog);
+ exit(1);
+ }
+
if (!spw_file_present ()) {
/* shadow not installed, do nothing */
exit (0);
#include "sgroupio.h"
#endif
#include "shadowio.h"
+#ifdef WITH_TCB
+#include "tcbfuncs.h"
+#endif
#ifndef SKEL_DIR
#define SKEL_DIR "/etc/skel"
static void process_flags (int argc, char **argv);
static void close_files (void);
static void open_files (void);
+static void open_shadow (void);
static void faillog_reset (uid_t);
static void lastlog_reset (uid_t);
static void usr_update (void);
fprintf (stderr, _("%s: cannot open %s\n"), Prog, pw_dbname ());
fail_exit (E_PW_UPDATE);
}
- if (is_shadow_pwd) {
- if (spw_lock () == 0) {
- fprintf (stderr,
- _("%s: cannot lock %s; try again later.\n"),
- Prog, spw_dbname ());
- fail_exit (E_PW_UPDATE);
- }
- spw_locked = true;
- if (spw_open (O_RDWR) == 0) {
- fprintf (stderr,
- _("%s: cannot open %s\n"),
- Prog, spw_dbname ());
- fail_exit (E_PW_UPDATE);
- }
- }
+
+ /* shadow file will be opened by open_shadow(); */
/*
* Lock and open the group file.
#endif
}
+static void open_shadow (void)
+{
+ if (!is_shadow_pwd)
+ return;
+ if (!spw_lock ()) {
+ fprintf(stderr,
+ _("%s: cannot lock shadow password file\n"),
+ Prog);
+ fail_exit(E_PW_UPDATE);
+ }
+ spw_locked = true;
+ if (!spw_open (O_RDWR)) {
+ fprintf(stderr,
+ _("%s: cannot open shadow password file\n"),
+ Prog);
+ fail_exit(E_PW_UPDATE);
+ }
+}
+
static char *empty_list = NULL;
/*
}
}
+#ifdef WITH_TCB
+ if (getdef_bool("USE_TCB")) {
+ if (!shadowtcb_create(user_name, user_id)) {
+ fprintf(stderr, "Failed to create tcb directory for %s\n", user_name);
+ fail_exit (E_UID_IN_USE);
+ }
+ }
+#endif
+ open_shadow();
+
/* do we have to add a group for that user? This is why we need to
* open the group files in the open_files() function --gafton */
if (Uflg) {
#ifdef SHADOWGRP
#include "sgroupio.h"
#endif
+#ifdef WITH_TCB
+#include <tcb.h>
+#include "tcbfuncs.h"
+#endif
/*@-exitarg@*/
#include "exitcodes.h"
#endif
static int is_owner (uid_t, const char *);
static int remove_mailbox (void);
+#ifdef WITH_TCB
+static int remove_tcbdir (const char *user_name, uid_t user_id);
+#endif
/*
* usage - display usage message and exit
return errors;
}
+#ifdef WITH_TCB
+static int remove_tcbdir (const char *user_name, uid_t user_id)
+{
+ char *buf;
+ int ret = 0;
+
+ if (!getdef_bool("USE_TCB"))
+ return 0;
+
+ buf = malloc(strlen(TCB_DIR) + strlen(user_name) + 2);
+ if (!buf) {
+ fprintf(stderr, "Can't allocate memory, "
+ "tcb entry for %s not removed.\n",
+ user_name);
+ return 1;
+ }
+ snprintf(buf, strlen(TCB_DIR) + strlen(user_name) + 2,
+ TCB_DIR "/%s", user_name);
+ if (!shadowtcb_drop_priv()) {
+ perror("shadowtcb_drop_priv");
+ free(buf);
+ return 1;
+ }
+ /* Only remove directory contents with dropped privileges.
+ * We will regain them and remove the user's tcb directory afterwards.
+ */
+ if (remove_tree(buf, false)) {
+ perror("remove_tree");
+ shadowtcb_gain_priv();
+ free(buf);
+ return 1;
+ }
+ shadowtcb_gain_priv();
+ free(buf);
+ if (!shadowtcb_remove(user_name)) {
+ fprintf(stderr, "Cannot remove tcb files for %s: %s\n",
+ user_name, strerror(errno));
+ ret = 1;
+ }
+ return ret;
+}
+#endif
+
/*
* main - userdel command
*/
user_id = pwd->pw_uid;
user_home = xstrdup (pwd->pw_dir);
}
+#ifdef WITH_TCB
+ if (!shadowtcb_set_user(user_name))
+ exit (E_NOTFOUND);
+#endif
#ifdef USE_NIS
/*
#endif
if (rflg) {
- if (remove_tree (user_home) != 0) {
+ if (remove_tree (user_home, true) != 0) {
fprintf (stderr,
_("%s: error removing directory %s\n"),
Prog, user_home);
user_cancel (user_name);
close_files ();
+#ifdef WITH_TCB
+ errors += remove_tcbdir(user_name, user_id);
+#endif
+
nscd_flush_cache ("passwd");
nscd_flush_cache ("group");
#include "sgroupio.h"
#endif
#include "shadowio.h"
+#ifdef WITH_TCB
+#include "tcbfuncs.h"
+#endif
/*
* exit status values
if (copy_tree (user_home, user_newhome,
uflg ? (long int)user_newid : -1,
gflg ? (long int)user_newgid : -1) == 0) {
- if (remove_tree (user_home) != 0) {
+ if (remove_tree (user_home, true) != 0) {
fprintf (stderr,
_("%s: warning: failed to completely remove old home directory %s"),
Prog, user_home);
/* TODO: do some cleanup if the copy
* was started */
- (void) remove_tree (user_newhome);
+ (void) remove_tree (user_newhome, true);
}
fprintf (stderr,
_("%s: cannot rename directory %s to %s\n"),
return;
}
if (uflg) {
- if (fchown (fd, user_newid, (gid_t) - 1) < 0) {
+ if (fchown (fd, user_newid, (gid_t) -1) < 0) {
perror (_("failed to change mailbox owner"));
}
#ifdef WITH_AUDIT
#endif /* USE_PAM */
#endif /* ACCT_TOOLS_SETUID */
+#ifdef WITH_TCB
+ if (!shadowtcb_set_user(user_name))
+ exit(E_PW_UPDATE);
+#endif
+
/*
* Do the hard stuff - open the files, change the user entries,
* change the home directory, then close and update the files.
}
close_files ();
+#ifdef WITH_TCB
+ if ((user_newname || user_newid != -1) &&
+ !shadowtcb_move(user_newname, user_newid)) {
+ exit(E_PW_UPDATE);
+ }
+#endif
+
nscd_flush_cache ("passwd");
nscd_flush_cache ("group");
#include "shadowio.h"
/*@-exitarg@*/
#include "exitcodes.h"
+#ifdef WITH_TCB
+#include <tcb.h>
+#include "tcbfuncs.h"
+#endif
#define MSG_WARN_EDIT_OTHER_FILE _( \
"You have modified %s.\n"\
static bool createedit = false;
static int (*unlock) (void);
static bool quiet = false;
+static const char *user = NULL;
+static bool tcb_mode = false;
/* local function prototypes */
static void usage (int status);
" -p, --passwd edit passwd database\n"
" -q, --quiet quiet mode\n"
" -s, --shadow edit shadow or gshadow database\n"
+#ifdef WITH_TCB
+ " -u, --user which user's tcb shadow file to edit\n"
+#endif
"\n"), (E_SUCCESS != status) ? stderr : stdout);
exit (status);
}
#define DEFAULT_EDITOR "vi"
#endif
+#define SHADOWTCB_SCRATCHDIR ":tmp"
+
/*
*
*/
int status;
FILE *f;
char filebackup[1024], fileedit[1024];
+ char *to_rename;
snprintf (filebackup, sizeof filebackup, "%s-", file);
- snprintf (fileedit, sizeof fileedit, "%s.edit", file);
+#ifdef WITH_TCB
+ if (tcb_mode) {
+ if (mkdir(TCB_DIR "/" SHADOWTCB_SCRATCHDIR, 0700) && errno != EEXIST)
+ vipwexit (_("failed to create scratch directory"), errno, 1);
+ if (!shadowtcb_drop_priv())
+ vipwexit (_("failed to drop privileges"), errno, 1);
+ snprintf(fileedit, sizeof fileedit,
+ TCB_DIR "/" SHADOWTCB_SCRATCHDIR "/.vipw.shadow.%s", user);
+ } else {
+#endif
+ snprintf (fileedit, sizeof fileedit, "%s.edit", file);
+#ifdef WITH_TCB
+ }
+#endif
unlock = file_unlock;
filename = file;
fileeditname = fileedit;
vipwexit (_("setfscreatecon () failed"), errno, 1);
}
}
+#endif
+#ifdef WITH_TCB
+ if (tcb_mode && !shadowtcb_gain_priv())
+ vipwexit (_("failed to gain privileges"), errno, 1);
#endif
if (file_lock () == 0) {
vipwexit (_("Couldn't lock file"), errno, 5);
}
filelocked = true;
+#ifdef WITH_TCB
+ if (tcb_mode && !shadowtcb_drop_priv())
+ vipwexit (_("failed to drop privileges"), errno, 1);
+#endif
/* edited copy has same owners, perm */
if (stat (file, &st1) != 0) {
if (NULL == f) {
vipwexit (file, 1, 1);
}
+#ifdef WITH_TCB
+ if (tcb_mode && !shadowtcb_gain_priv())
+ vipwexit (_("failed to gain privileges"), errno, 1);
+#endif
if (create_backup_file (f, fileedit, &st1) != 0) {
vipwexit (_("Couldn't make backup"), errno, 1);
}
* without saving). Use pwck or grpck to do the check. --marekm
*/
createedit = false;
+#ifdef WITH_TCB
+ if (tcb_mode) {
+ if (!(f = fopen(fileedit, "r")))
+ vipwexit (_("failed to open scratch file"), errno, 1);
+ if (unlink(fileedit))
+ vipwexit (_("failed to unlink scratch file"), errno, 1);
+ if (!shadowtcb_drop_priv())
+ vipwexit (_("failed to drop privileges"), errno, 1);
+ if (stat(file, &st1))
+ vipwexit (_("failed to stat edited file"), errno, 1);
+ to_rename = malloc(strlen(file) + 2);
+ if (!to_rename)
+ vipwexit (_("failed to allocate memory"), errno, 1);
+ snprintf(to_rename, strlen(file) + 2, "%s+", file);
+ if (create_backup_file(f, to_rename, &st1)) {
+ free(to_rename);
+ vipwexit (_("failed to create backup file"), errno, 1);
+ }
+ } else {
+#endif
+ to_rename = fileedit;
+#ifdef WITH_TCB
+ }
+#endif
unlink (filebackup);
link (file, filebackup);
- if (rename (fileedit, file) == -1) {
+ if (rename (to_rename, file) == -1) {
fprintf (stderr,
_("%s: can't restore %s: %s (your changes are in %s)\n"),
- progname, file, strerror (errno), fileedit);
+ progname, file, strerror (errno), to_rename);
+ if (tcb_mode)
+ free(to_rename);
vipwexit (0, 0, 1);
}
+#ifdef WITH_TCB
+ if (tcb_mode) {
+ free(to_rename);
+ if (!shadowtcb_gain_priv())
+ vipwexit (_("failed to gain privileges"), errno, 1);
+ }
+#endif
+
if ((*file_unlock) () == 0) {
fprintf (stderr, _("%s: failed to unlock %s\n"), progname, fileeditname);
SYSLOG ((LOG_ERR, "failed to unlock %s", fileeditname));
{"passwd", no_argument, NULL, 'p'},
{"quiet", no_argument, NULL, 'q'},
{"shadow", no_argument, NULL, 's'},
+#ifdef WITH_TCB
+ {"user", required_argument, NULL, 'u'},
+#endif
{NULL, 0, NULL, '\0'}
};
- while ((c =
- getopt_long (argc, argv, "ghpqs",
- long_options, NULL)) != -1) {
+ while ((c = getopt_long (argc, argv,
+#ifdef WITH_TCB
+ "ghpqsu:",
+#else
+ "ghpqs",
+#endif
+ long_options, NULL)) != -1) {
switch (c) {
case 'g':
do_vipw = false;
case 's':
editshadow = true;
break;
+ case 'u':
+ user = optarg;
+ break;
default:
usage (E_USAGE);
}
if (do_vipw) {
if (editshadow) {
- vipwedit (SHADOW_FILE, spw_lock, spw_unlock);
+#ifdef WITH_TCB
+ if (getdef_bool("USE_TCB") && user) {
+ if (!shadowtcb_set_user(user)) {
+ fprintf (stderr,
+ _("%s: failed to find tcb directory for %s\n"),
+ progname, user);
+ return E_SHADOW_NOTFOUND;
+ }
+ tcb_mode = true;
+ }
+#endif
+ vipwedit (spw_dbname (), spw_lock, spw_unlock);
printf (MSG_WARN_EDIT_OTHER_FILE,
- SHADOW_FILE,
+ spw_dbname (),
PASSWD_FILE,
"vipw");
} else {