--- libuser-0.56.9.dfsg.1.orig/debian/libuser.links
+++ libuser-0.56.9.dfsg.1/debian/libuser.links
@@ -0,0 +1 @@
+usr/share/gtk-doc/html/libuser/libuser.devhelp usr/share/devhelp/books/libuser.devhelp
--- libuser-0.56.9.dfsg.1.orig/debian/shlibs.libuser1
+++ libuser-0.56.9.dfsg.1/debian/shlibs.libuser1
@@ -0,0 +1 @@
+libuser 0 libuser1 (>= 0.53.8-1)
--- libuser-0.56.9.dfsg.1.orig/debian/watch
+++ libuser-0.56.9.dfsg.1/debian/watch
@@ -0,0 +1,3 @@
+version=3
+opts="dversionmangle=s/\.dfsg\.\d+$//,uversionmangle=s/\-\d+$//,downloadurlmangle=s/tar.bz2/tar.bz2?format=raw/" \
+http://fedorahosted.org/libuser/attachment/wiki/LibuserDownloads /libuser/attachment/wiki/LibuserDownloads/libuser-([\d.-]*).tar.bz2 debian debian/update-orig-dfsg
\ No newline at end of file
--- libuser-0.56.9.dfsg.1.orig/debian/libuser1.install
+++ libuser-0.56.9.dfsg.1/debian/libuser1.install
@@ -0,0 +1,2 @@
+usr/lib/*.so.*
+usr/lib/libuser/*.so
\ No newline at end of file
--- libuser-0.56.9.dfsg.1.orig/debian/libuser.install
+++ libuser-0.56.9.dfsg.1/debian/libuser.install
@@ -0,0 +1,3 @@
+usr/bin/*
+usr/sbin/*
+usr/share/*
--- libuser-0.56.9.dfsg.1.orig/debian/compat
+++ libuser-0.56.9.dfsg.1/debian/compat
@@ -0,0 +1 @@
+4
--- libuser-0.56.9.dfsg.1.orig/debian/copyright
+++ libuser-0.56.9.dfsg.1/debian/copyright
@@ -0,0 +1,9 @@
+This package was debianized by Ghe Rivero <ghe@upsa.es> on
+Thu, 31 Mar 2005 09:56:46 +0200.
+
+It was downloaded from https://fedorahosted.org/libuser/wiki/LibuserDownloads
+
+Copyright & Upstream Author: Nalin Dahyabhai <nalin@redhat.com> 
+
+License: LGPL
+	/usr/share/common-licenses/LGPL
--- libuser-0.56.9.dfsg.1.orig/debian/update-orig-dfsg
+++ libuser-0.56.9.dfsg.1/debian/update-orig-dfsg
@@ -0,0 +1,14 @@
+#!/bin/bash
+ORIGDIR=`pwd`
+NAME="libuser"
+VERSION=$2
+SOURCE=$3
+NON_FREE="docs/rfc2307.txt"
+DFSG="dfsg.1"
+FILENAME=$NAME-$VERSION
+
+cd .. && bunzip2 $FILENAME.tar.bz2
+tar --file=$FILENAME.tar --delete $FILENAME/$NON_FREE
+bzip2 $FILENAME.tar
+mv $FILENAME.tar.bz2 $FILENAME.$DFSG.tar.bz2
+cd $ORIGDIR && uupdate ../$FILENAME.$DFSG.tar.bz2 -v $VERSION.$DFSG
--- libuser-0.56.9.dfsg.1.orig/debian/pyversions
+++ libuser-0.56.9.dfsg.1/debian/pyversions
@@ -0,0 +1 @@
+2.3-
--- libuser-0.56.9.dfsg.1.orig/debian/rules
+++ libuser-0.56.9.dfsg.1/debian/rules
@@ -0,0 +1,146 @@
+#!/usr/bin/make -f
+# -*- makefile -*-
+# Sample debian/rules that uses debhelper.
+# This file was originally written by Joey Hess and Craig Small.
+# As a special exception, when this file is copied by dh-make into a
+# dh-make output file, you may use that output file without restriction.
+# This special exception was added by Craig Small in version 0.37 of dh-make.
+
+# Uncomment this to turn on verbose mode.
+export DH_VERBOSE=1
+
+#include /usr/share/dpatch/dpatch.make
+
+# These are used for cross-compiling and for saving the configure script
+# from having to guess our platform (since we know it already)
+DEB_HOST_GNU_TYPE   ?= $(shell dpkg-architecture -qDEB_HOST_GNU_TYPE)
+DEB_BUILD_GNU_TYPE  ?= $(shell dpkg-architecture -qDEB_BUILD_GNU_TYPE)
+
+include /usr/share/dpatch/dpatch.make
+
+ifneq (,$(findstring noopt,$(DEB_BUILD_OPTIONS)))
+    CFLAGS += -O0
+else
+
+DEB_HOST_ARCH = $(shell dpkg-architecture -qDEB_HOST_ARCH)
+OPTI_FLAGS_LIBONLY := -fomit-frame-pointer -falign-functions=0 -falign-jumps=0 -falign-loops=0
+### PICCORO optimizer flags for i386 x86 machines
+ifeq ($(DEB_HOST_ARCH),i386)
+    OPTI_FLAGS_LIBONLY += -march=i586 -mtune=pentium -mmmx 
+else
+ifeq ($(DEB_HOST_ARCH),i486)
+    OPTI_FLAGS_LIBONLY += -march=i586 -mtune=pentium -mmmx 
+endif
+endif
+
+ifeq ($(DEB_HOST_ARCH),amd64)
+    OPTI_FLAGS_LIBONLY += -mmmx -msse
+endif
+
+endif
+
+CFLAGS = -Wall -g
+
+ifneq (,$(findstring noopt,$(DEB_BUILD_OPTIONS)))
+    CFLAGS += -O0
+else
+CFLAGS += -O2 $(OPTI_FLAGS_LIBONLY)
+CXXFLAGS= -O2 $(OPTI_FLAGS_LIBONLY)
+endif
+
+clean: unpatch
+	dh_testdir
+	dh_testroot
+	[ ! -f Makefile ] || $(MAKE) distclean
+	-rm -f config.sub config.guess
+	dh_clean
+
+install-python%: patch-stamp
+	dh_testdir
+	dh_testroot
+	dh_installdirs
+
+	[ ! -f Makefile ] || $(MAKE) distclean
+
+ifneq "$(wildcard /usr/share/misc/config.sub)" ""
+	cp -f /usr/share/misc/config.sub config.sub
+endif
+ifneq "$(wildcard /usr/share/misc/config.guess)" ""
+	cp -f /usr/share/misc/config.guess config.guess
+endif
+
+	# configure for pythonX.Y
+	CFLAGS="$(CFLAGS)" PYTHON=python$* ./configure                   \
+	       --host=$(DEB_HOST_GNU_TYPE) --build=$(DEB_BUILD_GNU_TYPE) \
+	       --prefix=/usr --mandir=\$${prefix}/share/man              \
+	       --infodir=\$${prefix}/share/info --sysconfdir=/etc        \
+	       --with-python --disable-rpath
+
+	# build for pythonX.Y
+	PYTHON=python$* $(MAKE)
+
+	# install for pythonX.Y
+	$(MAKE) install DESTDIR=$(CURDIR)/debian/tmp
+	chrpath -d $(CURDIR)/debian/tmp/usr/bin/lchfn \
+	    $(CURDIR)/debian/tmp/usr/bin/lchsh \
+	    $(CURDIR)/debian/tmp/usr/sbin/lchage \
+	    $(CURDIR)/debian/tmp/usr/sbin/lgroupadd \
+	    $(CURDIR)/debian/tmp/usr/sbin/lgroupdel \
+	    $(CURDIR)/debian/tmp/usr/sbin/lgroupmod \
+	    $(CURDIR)/debian/tmp/usr/sbin/lid \
+	    $(CURDIR)/debian/tmp/usr/sbin/lnewusers \
+	    $(CURDIR)/debian/tmp/usr/sbin/lpasswd \
+	    $(CURDIR)/debian/tmp/usr/sbin/luseradd \
+	    $(CURDIR)/debian/tmp/usr/sbin/luserdel \
+	    $(CURDIR)/debian/tmp/usr/sbin/lusermod \
+	    $(CURDIR)/debian/tmp/usr/lib/libuser.so.1.2.0 \
+	    $(CURDIR)/debian/tmp/usr/lib/libuser/libuser_files.so \
+	    $(CURDIR)/debian/tmp/usr/lib/libuser/libuser_shadow.so \
+	    $(CURDIR)/debian/tmp/usr/lib/python$*/site-packages/libusermodule.so
+	mkdir -p $(CURDIR)/debian/python-libuser/usr/lib/python$*/site-packages
+	cp $(CURDIR)/debian/tmp/usr/lib/python$*/site-packages/libusermodule.so \
+	    $(CURDIR)/debian/python-libuser/usr/lib/python$*/site-packages/libuser.so
+
+	mkdir -p $(CURDIR)/debian/tmp/usr/share/man/man8
+	for n in lchage lgroupadd lgroupdel lgroupmod lid lnewusers \
+		lpasswd luseradd luserdel lusermod; \
+	do \
+		sed "s/^\.TH $$n 1 /.TH $$n 8 /" $(CURDIR)/debian/tmp/usr/share/man/man1/$$n.1 > \
+			$(CURDIR)/debian/tmp/usr/share/man/man8/$$n.8; \
+		rm -f $(CURDIR)/debian/tmp/usr/share/man/man1/$$n.1; \
+	done
+
+install: $(patsubst %,install-%,$(shell pyversions -s))
+	dh_testdir
+	install -Dm644 debian/shlibs.libuser1 debian/libuser1/DEBIAN/shlibs
+#	install -m644 apps/libapputil.la debian/tmp/usr/lib/libuser
+
+	sed -i "/dependency_libs/ s/'.*'/''/" `find . -name '*.la'`
+
+# Build architecture-independent files here.
+binary-indep: install
+# We have nothing to do by default.
+
+# Build architecture-dependent files here.
+binary-arch: install
+	dh_testdir
+	dh_testroot
+	dh_installchangelogs ChangeLog
+	dh_installdocs -X.deps -X.dirstamp
+	dh_installexamples
+	dh_install -s --sourcedir=debian/tmp --list-missing
+	dh_installman
+	dh_pysupport
+	dh_link
+	dh_strip
+	dh_compress
+	dh_fixperms
+	dh_makeshlibs -plibuser1 
+	dh_installdeb
+	dh_shlibdeps -L libuser1 -l debian/libuser/usr/lib/
+	dh_gencontrol
+	dh_md5sums
+	dh_builddeb
+
+binary: binary-indep binary-arch
+.PHONY: build clean binary-indep binary-arch binary install patch unpatch
--- libuser-0.56.9.dfsg.1.orig/debian/changelog
+++ libuser-0.56.9.dfsg.1/debian/changelog
@@ -0,0 +1,137 @@
+libuser (1:0.56.9.dfsg.1-2) UNRELEASED; urgency=high
+
+  * Non-maintainer upload.
+  * backported updates security optimnized for venenux venaima and older releases
+  * Fix CVE-2015-3245 and CVE-2015-3246.
+  * CVE-2015-3245:
+    Incomplete blacklist vulnerability in the chfn function in libuser before
+    0.56.13-8 and 0.60 before 0.60-7, as used in the userhelper program in the
+    usermode package, allows local users to cause a denial of service
+    (/etc/passwd corruption) via a newline character in the GECOS field.
+  * CVE-2015-3246:
+    libuser before 0.56.13-8 and 0.60 before 0.60-7, as used in the userhelper
+    program in the usermode package, directly modifies /etc/passwd, which
+    allows local users to cause a denial of service (inconsistent file state)
+    by causing an error during the modification. This issue can be
+    combined with CVE-2015-3245 to gain privileges.
+  * See also https://bugs.debian.org/793465 for more information.
+
+ -- PICCORO Lenz McKAY <mckaygerhard@gmail.com>  Tue, 18 Feb 2020 12:09:28 -0400
+
+libuser (1:0.56.9.dfsg.1-1.2) unstable; urgency=low
+
+  * Non-maintainer upload.
+  * Remove references to other libraries from dependency_libs field
+    Closes: #620594
+
+ -- Luk Claes <luk@debian.org>  Thu, 02 Jun 2011 18:27:14 +0200
+
+libuser (1:0.56.9.dfsg.1-1.1) unstable; urgency=low
+
+  * Non-maintainer upload.
+  * Fix CVE-2011-0002
+    Mark the LDAP default password value as encrypted
+    Patch taken from libuser-0.56.18-3.fc14.src.rpm
+    Add 02libuser-0.56.18-default-pw.dpatch
+    Closes: 610034
+  * Fix binary-or-shlib-defines-rpath
+
+ -- Anibal Monsalve Salazar <anibal@debian.org>  Wed, 09 Feb 2011 11:22:30 +1100
+
+libuser (1:0.56.9.dfsg.1-1) unstable; urgency=low
+
+  * New upstream release
+
+ -- Ghe Rivero <ghe@debian.org>  Fri, 02 May 2008 16:01:50 +0200
+
+libuser (1:0.56.8.dfsg.1-1) unstable; urgency=low
+
+  * New upstream release
+  * New download homepage at copyright file
+  * Lintian clean: copyright-without-copyright-notice 
+
+ -- Ghe Rivero <ghe@debian.org>  Tue, 11 Mar 2008 17:40:21 +0100
+
+libuser (1:0.56.7.dfsg.1-2) unstable; urgency=low
+
+  * libuser1-dev depends on libuser1 (Closes: #464681)
+  * python-libuser section admin
+
+ -- Ghe Rivero <ghe@debian.org>  Sat,  9 Feb 2008 00:23:21 +0100
+
+libuser (1:0.56.7.dfsg.1-1) unstable; urgency=low
+
+  * Removed non-free RFC-2307 doc. (Closes: #393394)
+  * Some lintian cleans:
+    - debian-rules-ignores-make-clean-error
+    - python-package-should-be-section-python python-libuser
+
+ -- Ghe Rivero <ghe@debian.org>  Mon,  4 Feb 2008 14:40:27 +0100
+  
+libuser (1:0.56.7-2) unstable; urgency=low
+
+  * Updated watch file... again! (Closes: #462826)
+  * debian/control: updated python-libuser override section 
+
+ -- Ghe Rivero <ghe@debian.org>  Mon, 28 Jan 2008 00:31:48 +0100
+
+libuser (1:0.56.7-1) unstable; urgency=low
+
+  * New upstream release
+  * Updated watch file (Closes: #449980) and uupdate script
+  * Merged patch from Ubuntu (Closes: #426568)
+  * Bumps Standards-Version to 3.7.3
+
+ -- Ghe Rivero <ghe@debian.org>  Wed, 16 Jan 2008 10:41:07 +0100
+
+libuser (1:0.54.6-2.1.dfsg.2-0.1) unstable; urgency=medium
+
+  * Non-maintainer upload
+  * debian/control: updated maintainer email address
+  * Fixed "Source package contains non-free IETF RFC/I-D's", closes: #393394
+
+ -- Anibal Monsalve Salazar <anibal@debian.org>  Tue, 18 Sep 2007 18:13:38 +1000
+
+libuser (1:0.54.6-2.1.dfsg.1-1.2) unstable; urgency=medium
+
+  * Non-maintainer upload.
+  * Moved all manpages of commands in /usr/sbin/ from section 1 to
+    section 8 (Closes: #404020).
+  * Added autotools-dev as build-dependency.
+
+ -- Anibal Monsalve Salazar <anibal@debian.org>  Thu, 28 Dec 2006 19:18:14 +1100
+
+libuser (1:0.54.6-2.1.dfsg.1-1.1) unstable; urgency=low
+
+  * Non-maintainer upload.
+  * Update package to the last python policy (Closes: 380854):
+    + build for all python versions.
+    + use python-support.
+  * Bump Standards-Verstion to 3.7.2.
+
+ -- Pierre Habouzit <madcoder@debian.org>  Sun,  3 Sep 2006 21:58:15 +0200
+
+libuser (1:0.54.6-2.1.dfsg.1-1) unstable; urgency=low
+
+  * New upstream release
+
+ -- Ghe Rivero <ghe@upsa.es>  Mon, 31 Jul 2006 17:27:39 +0200
+
+libuser (0.54.dfsg.1-1) unstable; urgency=low
+
+  * New upstream release, Closes #331611
+
+ -- Ghe Rivero <ghe@upsa.es>  Fri, 30 Sep 2005 16:22:04 +0200
+
+libuser (0.53.8.dfsg.1-1) unstable; urgency=low
+
+  * Removed non-free RFC-2307 document from source tarball.
+
+ -- Ghe Rivero <ghe@upsa.es>  Mon,  1 Aug 2005 00:59:30 +0200
+
+libuser (0.53.8-1) unstable; urgency=low
+
+  * Initial Release, closes: #302312
+
+ -- Ghe Rivero <ghe@upsa.es>  Mon, 27 Jun 2005 13:31:01 +0200
+
--- libuser-0.56.9.dfsg.1.orig/debian/libuser.dirs
+++ libuser-0.56.9.dfsg.1/debian/libuser.dirs
@@ -0,0 +1 @@
+usr/share/devhelp/books
--- libuser-0.56.9.dfsg.1.orig/debian/libuser1-dev.examples
+++ libuser-0.56.9.dfsg.1/debian/libuser1-dev.examples
@@ -0,0 +1 @@
+samples/*.c
--- libuser-0.56.9.dfsg.1.orig/debian/libuser1-dev.install
+++ libuser-0.56.9.dfsg.1/debian/libuser1-dev.install
@@ -0,0 +1,5 @@
+usr/include/*
+usr/lib/*.la
+usr/lib/pkgconfig/*
+usr/lib/libuser/*.la
+usr/lib/*.so
--- libuser-0.56.9.dfsg.1.orig/debian/libuser.docs
+++ libuser-0.56.9.dfsg.1/debian/libuser.docs
@@ -0,0 +1 @@
+tests/
--- libuser-0.56.9.dfsg.1.orig/debian/control
+++ libuser-0.56.9.dfsg.1/debian/control
@@ -0,0 +1,51 @@
+Source: libuser
+Section: admin
+Priority: optional
+Maintainer: Ghe Rivero <ghe@debian.org>
+Build-Depends: debhelper (>= 4.0.0), python-all-dev, pkg-config,
+ libglib2.0-dev, linuxdoc-tools, groff, libpam0g-dev, libpopt-dev,
+ dpatch, autotools-dev, python-support (>= 0.4), chrpath
+Standards-Version: 3.7.3
+Homepage: https://fedorahosted.org/libuser/
+
+Package: libuser
+Architecture: any
+Depends: ${shlibs:Depends}
+Description: user and group account administration library
+ The libuser library implements a standardized interface for manipulating
+ and administering user and group accounts.  The library uses pluggable
+ back-ends to interface to its data sources.
+ .
+ Sample applications modeled after those included with the shadow password
+ suite are included.
+
+Package: libuser1-dev
+Architecture: any
+Depends: ${shlibs:Depends}, libuser1 (= ${binary:Version})
+Section: libdevel
+Description: user and group account administration library (development files)
+ The libuser library implements a standardized interface for manipulating
+ and administering user and group accounts.  The library uses pluggable
+ back-ends to interface to its data sources.
+
+Package: libuser1
+Architecture: any
+Depends: ${shlibs:Depends}
+Section: libs
+Description: user and group account administration library (shared libraries)
+ The libuser library implements a standardized interface for manipulating
+ and administering user and group accounts.  The library uses pluggable
+ back-ends to interface to its data sources.
+
+Package: python-libuser
+Architecture: any
+Depends: ${shlibs:Depends}, ${python:Depends}
+Provides: ${python:Provides}
+Replaces: python2.3-libuser (<< 1:0.54.6-2.1.dfsg.1-1.1)
+Conflicts: python2.3-libuser (<< 1:0.54.6-2.1.dfsg.1-1.1)
+Section: admin
+Description: user and group account administration library (development files)
+ The libuser library implements a standardized interface for manipulating
+ and administering user and group accounts.  The library uses pluggable
+ back-ends to interface to its data sources.
+
--- libuser-0.56.9.dfsg.1.orig/debian/patches/02libuser-0.56.18-default-pw.dpatch
+++ libuser-0.56.9.dfsg.1/debian/patches/02libuser-0.56.18-default-pw.dpatch
@@ -0,0 +1,373 @@
+#! /bin/sh /usr/share/dpatch/dpatch-run
+## 02libuser-0.56.18-default-pw.dpatch by Miloslav Trmac <mitr@redhat.com>
+##
+## All lines beginning with `## DP:' are a description of the patch.
+## DP: Correctly mark the LDAP default password value as encrypted (CVE-2011-0002) 
+
+@DPATCH@
+--- a/Makefile.am	2008-04-10 07:14:41.000000000 +1000
++++ b/Makefile.am	2011-02-08 12:21:36.000000000 +1100
+@@ -16,7 +16,7 @@ PYTHON_CPPFLAGS = -I/usr/include/python$
+ SUBDIRS = po docs
+ TESTS = tests/config_test.sh tests/files_test tests/pwhash_test tests/utils_test
+ if LDAP
+-TESTS += tests/ldap_test
++TESTS += tests/default_pw_test tests/ldap_test
+ endif
+ 
+ EXTRA_DIST = \
+@@ -27,6 +27,7 @@ EXTRA_DIST = \
+ 	tests/config_import.conf.in tests/config_import2.conf.in \
+ 	tests/config_login.defs tests/config_login2.defs \
+ 	tests/config_override.conf.in tests/config_test.sh \
++	tests/default_pw_test \
+ 	tests/files.conf.in tests/files_test tests/files_test.py \
+ 	tests/ldap.conf.in tests/ldaprc tests/ldap_skel.ldif tests/ldap_test \
+ 	tests/ldap_test.py \
+--- a/modules/ldap.c	2008-04-10 07:14:41.000000000 +1000
++++ b/modules/ldap.c	2011-02-08 12:31:59.000000000 +1100
+@@ -981,6 +981,7 @@ get_ent_adds(const char *dn, struct lu_e
+ 		mod_count = 0;
+ 		for (a = attrs; a != NULL; a = a->next) {
+ 			const char *attribute;
++			gboolean is_userpassword;
+ 
+ 			attribute = a->data;
+ 			if (strcasecmp(attribute, DISTINGUISHED_NAME) == 0)
+@@ -999,9 +1000,26 @@ get_ent_adds(const char *dn, struct lu_e
+ 			mod->mod_values
+ 				= g_malloc0((vals->n_values + 1)
+ 					    * sizeof(*mod->mod_values));
++ 			/* Ugly hack: Detect userPassword values set by
++ 			   default (by this module and others), and replace them
++ 			   by LU_CRYPTED "!!" - the default values would be
++ 			   interpreted as plaintext passwords. */
++ 			is_userpassword
++ 				= (g_ascii_strcasecmp(attribute, "userPassword")
++ 				   == 0);
+ 			for (i = 0; i < vals->n_values; i++) {
+ 				value = g_value_array_get_nth(vals, i);
+ 				mod->mod_values[i] = lu_value_strdup(value);
++ 				if (is_userpassword
++ 				    && (strcmp(mod->mod_values[i],
++ 					       LU_COMMON_DEFAULT_PASSWORD) == 0
++ 					|| strcmp(mod->mod_values[i], "!!") == 0
++ 					|| strcmp(mod->mod_values[i], "x")
++ 					== 0)) {
++ 					g_free(mod->mod_values[i]);
++ 					mod->mod_values[i]
++ 						= g_strdup(LU_CRYPTED "!!");
++ 				}
+ 			}
+ 			mods[mod_count++] = mod;
+ 		}
+@@ -2185,6 +2203,10 @@ lu_ldap_user_default(struct lu_module *m
+ 		     const char *user, gboolean is_system,
+ 		     struct lu_ent *ent, struct lu_error **error)
+ {
++	/* Note that this will set LU_USERPASSWORD to
++	   LU_COMMON_DEFAULT_PASSWORD, which is a valid plaintext password in
++	   LDAP.  get_ent_adds () makes sure this value is replaced by an
++	   invalid encrypted hash. */
+ 	return lu_common_user_default(module, user, is_system, ent, error) &&
+ 	       lu_common_suser_default(module, user, is_system, ent, error);
+ }
+@@ -2194,6 +2216,8 @@ lu_ldap_group_default(struct lu_module *
+ 		      const char *group, gboolean is_system,
+ 		      struct lu_ent *ent, struct lu_error **error)
+ {
++	/* This sets LU_SHADOWPASSWORD, which is ignored by our backend.
++	   LU_GROUPPASSWORD is not set. */
+ 	return lu_common_group_default(module, group, is_system, ent, error) &&
+ 	       lu_common_sgroup_default(module, group, is_system, ent, error);
+ }
+--- a/tests/default_pw.conf.in	2010-06-29 15:10:08.510287292 +1000
++++ b/tests/default_pw.conf.in	2011-02-08 12:21:36.000000000 +1100
+@@ -0,0 +1,48 @@
++[defaults]
++# non-portable
++moduledir = @TOP_BUILDDIR@/modules/.libs
++skeleton = /etc/skel
++mailspooldir = /var/mail
++modules = @MODULES@
++create_modules = @MODULES@
++crypt_style = md5
++
++[userdefaults]
++LU_USERNAME = %n
++LU_UIDNUMBER = 500
++LU_GIDNUMBER = %u
++# LU_USERPASSWORD = !!
++# LU_GECOS = %n
++# LU_HOMEDIRECTORY = /home/%n
++# LU_LOGINSHELL = /bin/bash
++
++# LU_SHADOWNAME = %n
++# LU_SHADOWPASSWORD = !!
++# LU_SHADOWLASTCHANGE = %d
++# LU_SHADOWMIN = 0
++# LU_SHADOWMAX = 99999
++# LU_SHADOWWARNING = 7
++# LU_SHADOWINACTIVE = -1
++# LU_SHADOWEXPIRE = -1
++# LU_SHADOWFLAG = -1
++
++[groupdefaults]
++LU_GROUPNAME = %n
++LU_GIDNUMBER = 500
++# LU_GROUPPASSWORD = !!
++# LU_MEMBERUID =
++# LU_ADMINISTRATORUID =
++
++[ldap]
++server = 127.0.0.1:3890
++basedn = dc=libuser
++bindtype = simple
++binddn = cn=Manager,dc=libuser
++
++[files]
++directory = @WORKDIR@/files
++nonroot = yes
++
++[shadow]
++directory = @WORKDIR@/files
++nonroot = yes
+--- a/tests/default_pw_test	2010-06-29 15:10:08.510287292 +1000
++++ b/tests/default_pw_test	2011-02-08 12:21:36.000000000 +1100
+@@ -0,0 +1,175 @@
++#! /bin/sh
++# Automated default password value regression tester
++#
++# Copyright (c) 2004, 2010 Red Hat, Inc. All rights reserved.
++#
++# This is free software; you can redistribute it and/or modify it under
++# the terms of the GNU Library General Public License as published by
++# the Free Software Foundation; either version 2 of the License, or
++# (at your option) any later version.
++#
++# This program is distributed in the hope that it will be useful, but
++# WITHOUT ANY WARRANTY; without even the implied warranty of
++# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
++# General Public License for more details.
++#
++# You should have received a copy of the GNU Library General Public
++# License along with this program; if not, write to the Free Software
++# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
++#
++# Author: Miloslav Trmač <mitr@redhat.com>
++
++srcdir=$srcdir/tests
++
++workdir=$(pwd)/test_default_pw
++
++trap 'status=$?; rm -rf "$workdir"; exit $status' 0
++trap '(exit 1); exit 1' 1 2 13 15
++
++rm -rf "$workdir"
++mkdir "$workdir"
++
++# Create a SSL key
++/usr/bin/openssl req -newkey rsa:512 -keyout "$workdir"/key1 -nodes \
++    -x509 -days 2 -out "$workdir"/key3 2>/dev/null <<EOF
++.
++.
++.
++.
++.
++127.0.0.1
++.
++EOF
++echo > "$workdir"/key2
++cat "$workdir"/key{1,2,3} > "$workdir"/key.pem
++rm "$workdir"/key{1,2,3}
++
++sed "s|@WORKDIR@|$workdir|g" < "$srcdir"/slapd.conf.in > "$workdir"/slapd.conf
++LIBUSER_CONF=$workdir/libuser.conf
++export LIBUSER_CONF
++# Ugly non-portable hacks
++LD_LIBRARY_PATH=$(pwd)/lib/.libs
++export LD_LIBRARY_PATH
++PYTHONPATH=$(pwd)/python/.libs
++export PYTHONPATH
++
++exit_status=0
++fail() # message
++{
++    echo "Modules $modules: $1" >&2
++    exit_status=1
++}
++
++get_file_password() # file under $workdir/files, entry name
++{
++    echo "Checking $1 $2 ..." >&2
++    awk -F : "\$1 == \"$2\" { print \$2; }" "$workdir/files/$1"
++}
++
++get_ldap_password() # entry filter
++{
++    echo "Checking $1 ..." >&2
++    ldapsearch -LLL -h 127.0.0.1 -p 3890 -x -b 'dc=libuser' "$1" userPassword \
++	| sed -n 's/userPassword:: //p'
++}
++
++valid_password() # encoded value
++{
++    local v=$(python -c "import crypt; print crypt.crypt('password', '$1')")
++    [ "x$v" = "x$1" ]
++}
++
++# Try all concievable combinations and orders, assuming "shadow" requires
++# "files".
++for modules in \
++	files ldap \
++	'files ldap' 'files shadow' 'ldap files' 'shadow files' \
++	'files ldap shadow' 'files shadow ldap' 'ldap files shadow' \
++	'ldap shadow files' 'shadow files ldap' 'shadow ldap files'; do
++
++    # FIXME
++    echo ">>>modules: $modules" >&2
++
++    # Set up an LDAP server and database files
++    mkdir "$workdir"/db "$workdir"/files
++    touch "$workdir"/files/{passwd,shadow,group,gshadow}
++    case $modules in
++	*ldap*)
++	    # FIXME: path
++	    /usr/sbin/slapd \
++		-h 'ldap://127.0.0.1:3890/ ldaps://127.0.0.1:6360/' \
++		-f "$workdir"/slapd.conf &
++	    sleep 3 # Time for slapd to initialize
++	    slapd_pid=$(cat "$workdir"/slapd.pid)
++	    trap 'status=$?; kill $slapd_pid; rm -rf "$workdir"; exit $status' 0
++	    ldapadd -h 127.0.0.1 -p 3890 -f "$srcdir/ldap_skel.ldif" -x \
++		-D cn=Manager,dc=libuser -w password
++	    ;;
++    esac
++
++    # Set up the client
++    sed -e "s|@WORKDIR@|$workdir|g; s|@TOP_BUILDDIR@|$(pwd)|g" \
++	-e "s|@MODULES@|$modules|g" < "$srcdir"/default_pw.conf.in \
++	> "$LIBUSER_CONF"
++
++    # Point "$HOME/ldaprc" to "$srcdir"/ldaprc
++    HOME="$srcdir" python "$srcdir"/default_pw_test.py
++
++    # Test that {passwd,group} handle passwords correctly
++    case $modules in
++	*shadow*)
++	    for pair in 'passwd user_default' 'group group_default'; do
++		if [ "x$(get_file_password $pair)" != xx ]; then
++		    fail "Unexpected $pair password value"
++		fi
++	    done
++	    ;;
++	*files*)
++	    for pair in 'passwd user_default' 'group group_default'; do
++		if [ "x$(get_file_password $pair)" != 'x!!' ]; then
++		    fail "Unexpected $pair password value"
++		fi
++	    done
++	    ;;
++    esac
++
++    # Test that {shadow,gshadow} handle passwords correctly
++    case $modules in
++	*shadow*)
++	    for pair in 'shadow user_default' 'gshadow group_default'; do
++		if [ "x$(get_file_password $pair)" != 'x!!' ]; then
++		    fail "Unexpected $pair password value"
++		fi
++	    done
++	    ;;
++    esac
++
++    # Test that ldap handles password correctly
++    case $modules in
++	*ldap*)
++	    if [ "x$(get_ldap_password uid=user_default)" != xe0NSWVBUfSEh ];
++	    then
++		fail "Unexpected uid=user_default password value"
++	    fi
++	    # The LDAP module does not add a group password by default, but the
++	    # shadow module may do so.  In that case the LDAP module's override
++	    # is triggered and replaces shadow's 'x' with '{CRYPT}!!'.
++	    v=$(get_ldap_password cn=group_default)
++	    if [ "x$v" != x ] && [ "x$v" != xe0NSWVBUfSEh ]; then
++		fail "Unexpected cn=group_default password"
++	    fi
++	    ;;
++    esac
++
++    case $modules in
++	*ldap*)
++	    kill "$slapd_pid"
++	    trap 'status=$?; rm -rf "$workdir"; exit $status' 0
++	    sleep 1 # Time for slapd to terminate
++	    ;;
++    esac
++    slapd_pid=
++    rm -rf "$workdir"/db "$workdir"/files
++done
++
++(exit "$exit_status"); exit "$exit_status"
+--- a/tests/default_pw_test.py	2010-06-29 15:10:08.510287292 +1000
++++ b/tests/default_pw_test.py	2011-02-08 12:21:36.000000000 +1100
+@@ -0,0 +1,40 @@
++import crypt
++import libuser
++import unittest
++
++def prompt_callback(prompts):
++    for p in prompts:
++        if p.key == 'ldap/password':
++            p.value = 'password'
++        else:
++            p.value = p.default_value
++
++# This is ugly; ideally we would want a separate connection for each test case,
++# but libssl REALLY doesn't like being unloaded (libcrypto is not unloaded
++# and keeps pointers to unloaded libssl)
++admin = libuser.admin(prompt = prompt_callback)
++
++# Test case order matches the order of function pointers in struct lu_module
++class Tests(unittest.TestCase):
++    def setUp(self):
++        # See the comment at the libuser.admin() call above
++        self.a = admin
++
++    def testGroupAddDefault(self):
++        # Add an group with default attributes
++        e = self.a.initGroup('group_default')
++        self.a.addGroup(e)
++        del e
++
++    def testUserAddDefault(self):
++        # Add an user with default attributes
++        e = self.a.initUser('user_default')
++        self.a.addUser(e, False, False)
++        del e
++
++    def tearDown(self):
++        del self.a
++
++
++if __name__ == '__main__':
++    unittest.main()
+--- a/tests/ldap_test.py	2008-04-10 07:14:42.000000000 +1000
++++ b/tests/ldap_test.py	2011-02-08 12:21:36.000000000 +1100
+@@ -65,6 +65,7 @@ class Tests(unittest.TestCase):
+         e = self.a.lookupUserByName('user6_1')
+         self.assert_(e)
+         self.assertEqual(e[libuser.USERNAME], ['user6_1'])
++        self.assertEqual(e[libuser.USERPASSWORD], ['{CRYPT}!!'])
+ 
+     def testUserAdd2(self):
+         # A maximal case
+@@ -533,6 +534,7 @@ class Tests(unittest.TestCase):
+         e = self.a.lookupGroupByName('group21_1')
+         self.assert_(e)
+         self.assertEqual(e[libuser.GROUPNAME], ['group21_1'])
++        self.assertRaises(KeyError, lambda x: x[libuser.GROUPPASSWORD], e)
+ 
+     def testGroupAdd2(self):
+         # A maximal case
--- libuser-0.56.9.dfsg.1.orig/debian/patches/01manpage.dpatch
+++ libuser-0.56.9.dfsg.1/debian/patches/01manpage.dpatch
@@ -0,0 +1,28 @@
+#! /bin/sh /usr/share/dpatch/dpatch-run
+## 01manpage.dpatch by Ghe Rivero <ghe@upsa.es>
+##
+## All lines beginning with `## DP:' are a description of the patch.
+## DP: No description.
+
+@DPATCH@
+diff -urNad --exclude=CVS --exclude=.svn ./apps/lnewusers.1 /tmp/dpep-work.UgPLTp/libuser-0.53.8.dfsg.1/apps/lnewusers.1
+--- ./apps/lnewusers.1	2005-01-17 02:13:38.000000000 +0100
++++ /tmp/dpep-work.UgPLTp/libuser-0.53.8.dfsg.1/apps/lnewusers.1	2005-08-06 11:08:21.196167784 +0200
+@@ -54,7 +54,7 @@
+ The \f[SM]GECOS\fR field is traditionally used to store user's real name
+ and other information.
+ 
+-.IP Home Directory
++.IP "Home Directory"
+ If this field is empty,
+ a default specified by
+ .B libuser
+@@ -64,7 +64,7 @@
+ configuration does not specify a default,
+ is used.
+ 
+-.IP Login Shell
++.IP "Login Shell"
+ If this field is empty,
+ a default specified by
+ .B libuser
--- libuser-0.56.9.dfsg.1.orig/debian/patches/CVE-2015-3245-and-CVE-2015-3246.dpatch
+++ libuser-0.56.9.dfsg.1/debian/patches/CVE-2015-3245-and-CVE-2015-3246.dpatch
@@ -0,0 +1,2031 @@
+#! /bin/sh /usr/share/dpatch/dpatch-run
+## From: Markus Koschany <apo@debian.org>
+## Date: Thu, 12 May 2016 12:47:22 +0200
+## Subject: CVE-2015-3245 and CVE-2015-3246
+## Origin: https://fedorahosted.org/libuser/changeset/d73aa2a5a9ce5bdd349dff46e3e4885f2b194a95/
+## The original patch was rebased for Debian's version in Wheezy. The
+## generic_add, generic_del, generic_mod, generic_lock and generic_setpass
+## functions were completely replaced with the latest upstream code since they
+## already differed too much.
+
+--- a/lib/entity.c
++++ b/lib/entity.c
+@@ -605,3 +605,42 @@ lu_ent_get_attributes_current(struct lu_ent *ent)
+ 	g_return_val_if_fail(ent->magic == LU_ENT_MAGIC, NULL);
+ 	return lu_ent_get_attributes_int(ent->current);
+ }
++
++/* Return a string representation of the first value of ATTRIBUTE in LIST if
++   any, or NULL if ATTRIBUTE doesn't exist or on error.
++
++   The caller should call g_free() on the result. */
++static char *
++lu_ent_get_first_value_strdup_int(GArray *list, const char *attribute)
++{
++	GValueArray *vals;
++
++	vals = lu_ent_get_int(list, attribute);
++	if (vals == NULL)
++		return NULL;
++	return lu_value_strdup(g_value_array_get_nth(vals, 0));
++}
++
++
++/**
++ * lu_ent_get_first_value_strdup_current:
++ * @ent: An entity
++ * @attribute: Attribute name
++ *
++ * Returns a string representation (as if by lu_value_strdup()) of the first
++ * value associated with a current attribute in a struct #lu_ent.
++ *
++ * Returns: a string, should be freed by g_free() if the attribute is present.
++ * Returns %NULL if the attribute is not present or on error.
++ */
++char *
++lu_ent_get_first_value_strdup_current(struct lu_ent *ent, const char *attribute)
++{
++	g_return_val_if_fail(ent != NULL, NULL);
++	g_return_val_if_fail(ent->magic == LU_ENT_MAGIC, NULL);
++	g_return_val_if_fail(attribute != NULL, NULL);
++	g_return_val_if_fail(strlen(attribute) > 0, NULL);
++	return lu_ent_get_first_value_strdup_int(ent->current, attribute);
++}
++
++
+diff --git a/lib/entity.h b/lib/entity.h
+index a6e6b5e..07c5cc1 100644
+--- a/lib/entity.h
++++ b/lib/entity.h
+@@ -93,6 +93,8 @@ void lu_ent_clear_all_current(lu_ent_t *ent);
+ void lu_ent_del_current(lu_ent_t *ent, const char *attr,
+ 			const GValue *value);
+ GList *lu_ent_get_attributes_current(lu_ent_t *ent);
++char *lu_ent_get_first_value_strdup_current(struct lu_ent *ent,
++			const char *attribute);
+ 
+ /* These functions are used to set and query "pending" data attributes, which
+  * will take effect when we write this entry back out. */
+diff --git a/lib/user_private.h b/lib/user_private.h
+index 882a501..8285ad4 100644
+--- a/lib/user_private.h
++++ b/lib/user_private.h
+@@ -321,9 +321,11 @@ typedef char lu_security_context_t; /* "Something" */
+   ((void)(PATH), (void)(MODE), (void)(ERROR), TRUE)
+ #endif
+ 
+-/* Lock a file. */
++#ifndef LU_DISABLE_DEPRECATED
++/* Lock a file. Deprecated. */
+ gpointer lu_util_lock_obtain(int fd, struct lu_error **error);
+ void lu_util_lock_free(gpointer lock);
++#endif
+ 
+ /* Manipulate a colon-delimited flat text file. */
+ char *lu_util_line_get_matching1(int fd, const char *firstpart,
+diff --git a/lib/util.c b/lib/util.c
+index 5d04d59..2712b2a 100644
+--- a/lib/util.c
++++ b/lib/util.c
+@@ -639,7 +639,7 @@ lu_util_field_write(int fd, const char *first, unsigned int field,
+ 			goto err;
+ 		}
+ 		len = strlen(buf);
+-		if (write(fd, buf, len) == -1) {
++		if (write(fd, buf, len) != len) {
+ 			lu_error_new(error, lu_error_write, NULL);
+ 			ret = FALSE;
+ 			goto err;
+diff --git a/modules/files.c b/modules/files.c
+index 678f2f7..8ba1f5f 100644
+--- a/modules/files.c
++++ b/modules/files.c
+@@ -25,6 +25,7 @@
+ #include <fcntl.h>
+ #include <fnmatch.h>
+ #include <limits.h>
++#include <shadow.h>
+ #include <stdio.h>
+ #include <stdlib.h>
+ #include <string.h>
+@@ -90,82 +91,96 @@ static const char suffix_shadow[] = "/shadow";
+ static const char suffix_group[] = "/group";
+ static const char suffix_gshadow[] = "/gshadow";
+ 
+-/* Create a backup copy of "filename" named "filename-". */
+-static gboolean
+-lu_files_create_backup(const char *filename,
+-		       struct lu_error **error)
++/* Return the path of FILE_SUFFIX configured in MODULE, for g_free() */
++static char *
++module_filename(struct lu_module *module, const char *file_suffix)
++{
++	const char *dir;
++	char *key;
++
++	key = g_strconcat(module->name, "/directory", NULL);
++	dir = lu_cfg_read_single(module->lu_context, key, "/etc");
++	g_free(key);
++	return g_strconcat(dir, file_suffix, NULL);
++}
++
++/* Copy contents of INPUT_FILENAME to OUTPUT_FILENAME, exclusively creating it
++ * if EXCLUSIVE.
++ * Return the file descriptor for OUTPUT_FILENAME, open for reading and writing,
++ * or -1 on error.
++ * Note that this does no locking and assumes the directories hosting the files
++ * are not being manipulated by an attacker. */
++static int
++open_and_copy_file(const char *input_filename, const char *output_filename,
++		gboolean exclusive, struct lu_error **error)
+ {
+ 	int ifd, ofd;
+-	gpointer ilock, olock;
+-	char *backupname;
+-	struct stat ist, ost;
+-	gboolean res = FALSE;
++	struct stat st;
++	int res = -1;
++	int flags;
+ 
+-	g_assert(filename != NULL);
+-	g_assert(strlen(filename) > 0);
++	g_assert(input_filename != NULL);
++	g_assert(strlen(input_filename) > 0);
++	g_assert(output_filename != NULL);
++	g_assert(strlen(output_filename) > 0);
+ 
+-	/* Open the original file. */
+-	ifd = open(filename, O_RDONLY);
++	/* Open the input file. */
++	ifd = open(input_filename, O_RDONLY);
+ 	if (ifd == -1) {
+ 		lu_error_new(error, lu_error_open,
+-			     _("couldn't open `%s': %s"), filename,
++			     _("couldn't open `%s': %s"), input_filename,
+ 			     strerror(errno));
+ 		goto err;
+ 	}
+ 
+-	/* Lock the input file. */
+-	if ((ilock = lu_util_lock_obtain(ifd, error)) == NULL)
+-		goto err_ifd;
+-
+ 	/* Read the input file's size. */
+-	if (fstat(ifd, &ist) == -1) {
++		if (fstat(ifd, &st) == -1) {
+ 		lu_error_new(error, lu_error_stat,
+-			     _("couldn't stat `%s': %s"), filename,
+-			     strerror(errno));
+-		goto err_ilock;
++			_("couldn't stat `%s': %s"), input_filename,
++			strerror(errno));
++
++		goto err_ifd;
++
+ 	}
++	/* We only need O_WRONLY, but the caller needs RDWR if ofd will be
++	 * used as e->new_fd. */
++	flags = O_RDWR | O_CREAT;
++	if (exclusive) {
++		/* This ensures that if there is a concurrent writer which is
++		 * not doing locking for some reason, we will not truncate their
++		 * temporary file. Still, the other writer may truncate our
++		 * file, and ultimately the rename() committing the changes will
++		 * lose one or the other set of changes. */
++		(void)unlink(output_filename);
++		flags |= O_EXCL;
++	} else
++		flags |= O_TRUNC;
++	/* Start with absolutely restrictive permissions to make sure nobody
++	 * can get a file descriptor for this file until we are done resetting
++	 * ownership. */
++	ofd = open(output_filename, flags, 0);
+ 
+-	/* Generate the backup file's name and open it, creating it if it
+-	 * doesn't already exist. */
+-	backupname = g_strconcat(filename, "-", NULL);
+-	ofd = open(backupname, O_WRONLY | O_CREAT, ist.st_mode);
+ 	if (ofd == -1) {
+ 		lu_error_new(error, lu_error_open,
+-			     _("error creating `%s': %s"), backupname,
++			     _("error creating `%s': %s"), output_filename,
+ 			     strerror(errno));
+-		goto err_backupname;
+-	}
+-
+-	/* If we can't read its size, or it's not a normal file, bail. */
+-	if ((fstat(ofd, &ost) == -1) || !S_ISREG(ost.st_mode)) {
+-		struct stat st;
+-		if ((stat(backupname, &st) == -1) ||
+-		    !S_ISREG(st.st_mode) ||
+-		    (st.st_dev != ost.st_dev) ||
+-		    (st.st_ino != ost.st_ino)) {
+-			lu_error_new(error, lu_error_open,
+-				     _("backup file `%s' exists and is not a regular file"),
+-				     backupname);
+-			goto err_ofd;
+-		}
+-		lu_error_new(error, lu_error_stat,
+-			     _("couldn't stat `%s': %s"), backupname,
+-			     strerror(errno));
+-		goto err_ofd;
++			goto err_ifd;
+ 	}
+ 
+-	/* Now lock the output file. */
+-	if ((olock = lu_util_lock_obtain(ofd, error)) == NULL)
++	/* Set the permissions on the new file to match the old one. */
++	if (fchown(ofd, st.st_uid, st.st_gid) == -1 && errno != EPERM) {
++		lu_error_new(error, lu_error_generic,
++			     _("Error changing owner of `%s': %s"),
++			     output_filename, strerror(errno));
+ 		goto err_ofd;
++	}
+ 
+-	/* Set the permissions on the new file to match the old one. */
+-	if (fchown(ofd, ist.st_uid, ist.st_gid) == -1 && errno != EPERM) {
++	if (fchmod(ofd, st.st_mode) == -1) {
+ 		lu_error_new(error, lu_error_generic,
+-			     _("Error changing owner of `%s': %s"), backupname,
+-			     strerror(errno));
+-		goto err_olock;
++				_("Error changing mode of `%s': %s"),
++				output_filename, strerror(errno));
++		goto err_ofd;
+ 	}
+-	fchmod(ofd, ist.st_mode);
+ 
+ 	/* Copy the data, block by block. */
+ 	for (;;) {
+@@ -178,9 +193,9 @@ lu_files_create_backup(const char *filename,
+ 			if (errno == EINTR)
+ 				continue;
+ 			lu_error_new(error, lu_error_read,
+-				     _("Error reading `%s': %s"), filename,
+-				     strerror(errno));
+-			goto err_olock;
++				     _("Error reading `%s': %s"),
++				     input_filename, strerror(errno));
++			goto err_ofd;
+ 		}
+ 		if (left == 0)
+ 			break;
+@@ -194,54 +209,298 @@ lu_files_create_backup(const char *filename,
+ 					continue;
+ 				lu_error_new(error, lu_error_write,
+ 					     _("Error writing `%s': %s"),
+-					     backupname, strerror(errno));
+-				goto err_olock;
++					     output_filename, strerror(errno));
++				goto err_ofd;
+ 			}
+ 			p += out;
+ 			left -= out;
+ 		}
+ 	}
+ 
+-	/* Flush data to disk, and truncate at the current offset.  This is
+-	 * necessary if the file existed before we opened it. */
+-	fsync(ofd);
+-	if (ftruncate(ofd, lseek(ofd, 0, SEEK_CUR)) == -1) {
+-		lu_error_new(error, lu_error_generic,
+-			     _("Error writing `%s': %s"), backupname,
+-			     strerror(errno));
+-		goto err_olock;
+-	}
+-
+-	/* Re-read data about the output file. */
+-	if (fstat(ofd, &ost) == -1) {
+-		lu_error_new(error, lu_error_stat,
+-			     _("couldn't stat `%s': %s"), backupname,
+-			     strerror(errno));
+-		goto err_olock;
++	/* Flush data to disk. */
++	if (fsync(ofd) != 0 || lseek(ofd, 0, SEEK_SET) == -1) {
++		lu_error_new(error, lu_error_write, _("Error writing `%s': %s"),
++			     output_filename, strerror(errno));
++		goto err_ofd;
+ 	}
++	res = ofd;
++	goto err_ifd; /* Do not close ofd */
+ 
+-	/* Complain if the files are somehow not the same. */
+-	if (ist.st_size != ost.st_size) {
+-		lu_error_new(error, lu_error_generic,
+-			     _("backup file size mismatch"));
+-		goto err_olock;
+-	}
+-	res = TRUE;
+ 
+- err_olock:
+-	lu_util_lock_free(olock);
+  err_ofd:
+ 	close(ofd);
+- err_backupname:
+-	g_free(backupname);
+- err_ilock:
+-	lu_util_lock_free(ilock);
+  err_ifd:
+ 	close(ifd);
+  err:
+ 	return res;
+ }
+ 
++/* Deal with an existing LOCK_FILENAME.
++ * Return TRUE if the caller should try again. */
++static gboolean
++lock_file_handle_existing(const char *lock_filename, struct lu_error **error)
++{
++	gchar *lock_contents;
++	GError *gerror;
++	gboolean ret = FALSE;
++	uintmax_t pid;
++	char *p;
++
++	gerror = NULL;
++	if (g_file_get_contents(lock_filename, &lock_contents, NULL, &gerror)
++	    == FALSE) {
++		lu_error_new(error, lu_error_read,
++			     _("couldn't read from `%s': %s"), lock_filename,
++			     gerror->message);
++		g_error_free(gerror);
++		goto err;
++	}
++	errno = 0;
++	pid = strtoumax(lock_contents, &p, 10);
++	if (errno != 0 || *p != 0 || p == lock_contents || (pid_t)pid != pid) {
++		lu_error_new(error, lu_error_lock,
++			     _("Invalid contents of lock `%s'"), lock_filename);
++		goto err_lock_contents;
++	}
++	if (kill(pid, 0) == 0 || errno != ESRCH) {
++		lu_error_new(error, lu_error_lock,
++			     _("The lock %s is held by process %ju"),
++			     lock_filename, pid);
++		goto err_lock_contents;
++	}
++	/* This is unfixably racy, but that should matter only if a genuine
++	 * lock owner crashes. */
++	if (unlink(lock_filename) != 0) {
++		lu_error_new(error, lu_error_lock,
++		     _("Error removing stale lock `%s': %s"), lock_filename,
++		     strerror(errno));
++		goto err_lock_contents;
++	}
++	ret = TRUE;
++	/* Fall through */
++
++err_lock_contents:
++	g_free(lock_contents);
++err:
++	return ret;
++}
++
++/* Create a lock file for FILENAME. */
++static gboolean
++lock_file_create(const char *filename, struct lu_error **error)
++{
++	char *lock_filename, *tmp_filename;
++	char pid_string[sizeof (pid_t) * CHAR_BIT + 1];
++	int fd;
++	gboolean ret = FALSE;
++
++	lock_filename = g_strconcat(filename, ".lock", NULL);
++	tmp_filename = g_strdup_printf("%s.lock.XXXXXX", filename);
++
++	fd = mkstemp(tmp_filename);
++	if (fd == -1) {
++		lu_error_new(error, lu_error_open,
++			     _("error opening temporary file for `%s': %s"),
++			     lock_filename, strerror(errno));
++		goto err_tmp_filename;
++	}
++	if (snprintf(pid_string, sizeof(pid_string), "%ju", (uintmax_t)getpid())
++	    >= sizeof(pid_string))
++		g_assert_not_reached();
++	if (write(fd, pid_string, strlen(pid_string)) != strlen(pid_string)) {
++		lu_error_new(error, lu_error_write, _("Error writing `%s': %s"),
++			     tmp_filename, strerror(errno));
++		close(fd);
++		goto err_tmp_file;
++	}
++	close(fd);
++
++	if (link(tmp_filename, lock_filename) != 0) {
++		if (errno == EEXIST) {
++			if (lock_file_handle_existing(lock_filename, error)
++			    == FALSE)
++				goto err_tmp_file;
++			if (link(tmp_filename, lock_filename) == 0)
++				goto got_link;
++		}
++		lu_error_new(error, lu_error_lock,
++			     _("Cannot obtain lock `%s': %s"), lock_filename,
++			     strerror(errno));
++		goto err_tmp_file;
++	}
++got_link:
++	ret = TRUE;
++	/* Fall through */
++
++err_tmp_file:
++	(void)unlink(tmp_filename);
++err_tmp_filename:
++	g_free(tmp_filename);
++	g_free(lock_filename);
++	return ret;
++}
++
++/* Remove the lock file for FILENAME. */
++static void
++lock_file_remove(const char *filename)
++{
++	char *lock_file;
++
++	lock_file = g_strconcat(filename, ".lock", NULL);
++	(void)unlink(lock_file);
++	g_free(lock_file);
++}
++
++/* State related to a file currently open for editing. */
++struct editing {
++	char *filename;
++	lu_security_context_t fscreate;
++	char *new_filename;
++	int new_fd;
++};
++
++/* Open and lock FILE_SUFFIX in MODULE for editing.
++ * Return editing state, or NULL on error. */
++static struct editing *
++editing_open(struct lu_module *module, const char *file_suffix,
++	     struct lu_error **error)
++{
++	struct editing *e;
++	char *backup_name;
++	int fd;
++
++	e = g_malloc0(sizeof (*e));
++	e->filename = module_filename(module, file_suffix);
++	/* Make sure this all works if e->filename is a symbolic link, at least
++	 * as long as it points to the same file system. */
++
++	if (geteuid() == 0) {
++		if (lckpwdf() != 0) {
++			lu_error_new(error, lu_error_lock,
++				     _("error locking file: %s"),
++				     strerror(errno));
++			goto err_filename;
++		}
++	}
++	if (lock_file_create(e->filename, error) == FALSE)
++		goto err_lckpwdf;
++
++	if (!lu_util_fscreate_save(&e->fscreate, error))
++		goto err_locked;
++	if (!lu_util_fscreate_from_file(e->filename, error))
++		goto err_fscreate;
++
++	backup_name = g_strconcat(e->filename, "-", NULL);
++	fd = open_and_copy_file(e->filename, backup_name, FALSE, error);
++	g_free (backup_name);
++	close(fd);
++	if (fd == -1)
++		goto err_fscreate;
++
++	e->new_filename = g_strconcat(e->filename, "+", NULL);
++	e->new_fd = open_and_copy_file(e->filename, e->new_filename, TRUE,
++			       	       error);
++	if (e->new_fd == -1)
++		goto err_new_filename;
++
++	return e;
++
++err_new_filename:
++ 	g_free(e->new_filename);
++err_fscreate:
++	lu_util_fscreate_restore(e->fscreate);
++
++err_locked:
++	(void)lock_file_remove(e->filename);
++err_lckpwdf:
++	if (geteuid() == 0)
++		(void)ulckpwdf();
++
++err_filename:
++ 	g_free(e->filename);
++ 	g_free(e);
++ 	return NULL;
++}
++
++
++/* Replace DESTINATION with SOURCE, even if DESTINATION is a symbolic link. */
++static gboolean
++replace_file_or_symlink(const char *source, const char *destination,
++		        struct lu_error **error)
++{
++	struct stat st;
++	char *tmp;
++	gboolean ret = FALSE;
++
++	tmp = NULL;
++	if (lstat(destination, &st) == 0 && S_ISLNK(st.st_mode)) {
++		tmp = realpath(destination, NULL);
++		if (tmp == NULL) {
++			lu_error_new(error, lu_error_generic,
++				     _("Error resolving `%s': %s"), destination,
++				     strerror(errno));
++			goto err;
++		}
++		destination = tmp;
++	}
++	if (rename(source, destination) != 0) {
++		lu_error_new(error, lu_error_write,
++			     _("Error replacing `%s': %s"), destination,
++			     strerror(errno));
++		goto err;
++	}
++	ret = TRUE;
++	/* Fall through */
++
++err:
++	free(tmp);
++	return ret;
++}
++
++/* Finish editing E, commit edits if COMMIT.
++ * Return true only if RET_INPUT and everything went OK; suggested usage is
++ *  ret = editing_close(e, commit, ret, error); */
++static gboolean
++editing_close(struct editing *e, gboolean commit, gboolean ret_input,
++	      struct lu_error **error)
++{
++	gboolean ret = FALSE;
++	gboolean unlink_new_filename = TRUE;
++
++	g_assert(e != NULL);
++
++	if (commit && fsync(e->new_fd) != 0) {
++		lu_error_new(error, lu_error_write, _("Error writing `%s': %s"),
++			     e->new_filename, strerror(errno));
++		close(e->new_fd);
++		goto err;
++	}
++	close(e->new_fd);
++
++	if (commit) {
++		if (replace_file_or_symlink(e->new_filename, e->filename,
++					    error) == FALSE)
++			goto err;
++		unlink_new_filename = FALSE;
++	}
++	ret = ret_input;
++
++err:
++	if (unlink_new_filename)
++		(void)unlink(e->new_filename);
++	g_free(e->new_filename);
++	lu_util_fscreate_restore(e->fscreate);
++
++	(void)lock_file_remove(e->filename);
++	if (geteuid() == 0)
++		(void)ulckpwdf();
++
++	g_free(e->filename);
++	g_free(e);
++	return ret;
++}
++
++
+ /* Read a line from the file, no matter how long it is, and return it as a
+  * newly-allocated string, with the terminator intact. */
+ static char *
+@@ -423,7 +682,6 @@ generic_lookup(struct lu_module *module, const char *file_suffix,
+ 	gboolean ret;
+ 	const char *dir;
+ 	int fd = -1;
+-	gpointer lock;
+ 	char *line, *filename, *key;
+ 
+ 	g_assert(module != NULL);
+@@ -438,7 +696,7 @@ generic_lookup(struct lu_module *module, const char *file_suffix,
+ 	filename = g_strconcat(dir, file_suffix, NULL);
+ 	g_free(key);
+ 
+-	/* Open the file and lock it. */
++	/* Open the file. */
+ 	fd = open(filename, O_RDONLY);
+ 	if (fd == -1) {
+ 		lu_error_new(error, lu_error_open,
+@@ -449,15 +707,9 @@ generic_lookup(struct lu_module *module, const char *file_suffix,
+ 	}
+ 	g_free(filename);
+ 
+-	if ((lock = lu_util_lock_obtain(fd, error)) == NULL) {
+-		close(fd);
+-		return FALSE;
+-	}
+-
+ 	/* Search for the entry in this file. */
+ 	line = lu_util_line_get_matchingx(fd, name, field, error);
+ 	if (line == NULL) {
+-		lu_util_lock_free(lock);
+ 		close(fd);
+ 		return FALSE;
+ 	}
+@@ -465,7 +717,6 @@ generic_lookup(struct lu_module *module, const char *file_suffix,
+ 	/* If we found data, parse it and then free the data. */
+ 	ret = parser(line, ent);
+ 	g_free(line);
+-	lu_util_lock_free(lock);
+ 	close(fd);
+ 
+ 	return ret;
+@@ -653,10 +904,11 @@ format_field(struct lu_ent *ent, const struct format_specifier *format)
+ }
+ 
+ /* Format a line for the user/group, using the information in ent, using
+- * formats to guide the formatting. */
++   formats to guide the formatting.
++   Return a line for g_free(), or NULL on error. */
+ static char *
+ format_generic(struct lu_ent *ent, const struct format_specifier *formats,
+-	       size_t format_count)
++	       size_t format_count, struct lu_error **error)
+ {
+ 	char *ret = NULL, *tmp;
+ 	size_t i;
+@@ -667,206 +919,166 @@ format_generic(struct lu_ent *ent, const struct format_specifier *formats,
+ 		char *field;
+ 
+ 		field = format_field(ent, formats + i);
+-		tmp = g_strconcat(ret ? ret : "", i > 0 ? ":" : "", field,
+-				  NULL);
+-		g_free(field);
++		if (strchr(field, '\n') != NULL) {
++			lu_error_new(error, lu_error_invalid_attribute_value,
++				     _("%s value `%s': `\\n' not allowed"),
++				     formats[i].attribute, field);
++			g_free(field);
++			goto err;
++		}
++		if (i != format_count - 1 && strchr(field, ':') != NULL) {
++			lu_error_new(error, lu_error_invalid_attribute_value,
++				     _("%s value `%s': `:' not allowed"),
++				     formats[i].attribute, field);
++			g_free(field);
++			goto err;
++		}
++		if (i == 0)
++			tmp = field;
++		else {
++			tmp = g_strconcat(ret, ":", field, NULL);
++			g_free(field);
++		}
+ 		g_free(ret);
+ 		ret = tmp;
+ 	}
+ 	/* Add an end-of-line terminator. */
+-	tmp = g_strconcat(ret ?: "", "\n", NULL);
++	g_assert(format_count != 0 && ret != NULL);
++	tmp = g_strconcat(ret, "\n", NULL);
+ 	g_free(ret);
+ 	ret = tmp;
+ 
+ 	return ret;
+-}
+ 
+-/* Construct a line for /etc/passwd using data in the lu_ent structure. */
+-static char *
+-lu_files_format_user(struct lu_ent *ent)
+-{
+-	return format_generic(ent, format_passwd, G_N_ELEMENTS(format_passwd));
++err:
++	g_free(ret);
++	return NULL;
+ }
+ 
+-/* Construct a line for /etc/group using data in the lu_ent structure. */
+-static char *
+-lu_files_format_group(struct lu_ent *ent)
+-{
+-	return format_generic(ent, format_group, G_N_ELEMENTS(format_group));
+-}
+ 
+-/* Construct a line for /etc/shadow using data in the lu_ent structure. */
+-static char *
+-lu_shadow_format_user(struct lu_ent *ent)
++/* Does NUL-terminated CONTENTS contains an entry with the same entry name used
++   in LINE? */
++static gboolean
++entry_name_conflicts(const char *contents, const char *line)
+ {
+-	return format_generic(ent, format_shadow, G_N_ELEMENTS(format_shadow));
+-}
++	size_t prefix_len;
++	char *prefix, *fragment;
++	gboolean res;
+ 
+-/* Construct a line for /etc/gshadow using data in the lu_ent structure. */
+-static char *
+-lu_shadow_format_group(struct lu_ent *ent)
+-{
+-	return format_generic(ent, format_gshadow,
+-			      G_N_ELEMENTS(format_gshadow));
++	if (strchr(line, ':') != NULL)
++		prefix_len = strchr(line, ':') - line + 1;
++	else if (strchr(line, '\n') != NULL)
++		prefix_len = strchr(line, '\n') - line + 1;
++	else
++		prefix_len = strlen(line);
++	if (strncmp(contents, line, prefix_len) == 0)
++		return TRUE;
++
++	prefix = g_strndup(line, prefix_len);
++	fragment = g_strconcat("\n", prefix, NULL);
++	g_free(prefix);
++
++	res = strstr(contents, fragment) != NULL;
++	g_free(fragment);
++	return res;
+ }
+ 
+-typedef char *(*format_fn) (struct lu_ent * ent);
+ 
+ /* Add an entity to a given flat file, using a given formatting functin to
+  * construct the proper text data. */
+ static gboolean
+ generic_add(struct lu_module *module, const char *file_suffix,
+-	    format_fn formatter, struct lu_ent *ent,
+-	    struct lu_error **error)
++	    const struct format_specifier *formats, size_t format_count,
++	    struct lu_ent *ent, struct lu_error **error)
+ {
+-	lu_security_context_t fscreate;
+-	const char *dir;
+-	char *key, *line, *filename, *contents;
+-	char *fragment1, *fragment2;
+-	int fd;
++	struct editing *e;
++	char *line, *contents;
+ 	ssize_t r;
+-	gpointer lock;
+ 	struct stat st;
+ 	off_t offset;
+ 	gboolean ret = FALSE;
+ 
+ 	g_assert(module != NULL);
+-	g_assert(formatter != NULL);
++	g_assert(formats != NULL);
++	g_assert(format_count > 0);
+ 	g_assert(ent != NULL);
+ 
+-	/* Generate the name of a file to open. */
+-	key = g_strconcat(module->name, "/directory", NULL);
+-	dir = lu_cfg_read_single(module->lu_context, key, "/etc");
+-	filename = g_strconcat(dir, file_suffix, NULL);
+-	g_free(key);
+-
+-	if (!lu_util_fscreate_save(&fscreate, error))
+-		goto err_filename;
+-	if (!lu_util_fscreate_from_file(filename, error))
+-		goto err_fscreate;
+-
+-	/* Create a backup copy of the file we're about to modify. */
+-	if (lu_files_create_backup(filename, error) == FALSE)
+-		goto err_fscreate;
+-
+-	/* Open the file. */
+-	fd = open(filename, O_RDWR);
+-	if (fd == -1) {
+-		lu_error_new(error, lu_error_open,
+-			     _("couldn't open `%s': %s"), filename,
+-			     strerror(errno));
+-		goto err_fscreate;
+-	}
++	line = format_generic(ent, formats, format_count, error);
++	if (line == NULL)
++		goto err;
+ 
+-	/* Lock the file. */
+-	if ((lock = lu_util_lock_obtain(fd, error)) == NULL)
+-		goto err_fd;
++	e = editing_open(module, file_suffix, error);
++	if (e == NULL)
++		goto err_line;
+ 
+ 	/* Read the file's size. */
+-	if (fstat(fd, &st) == -1) {
++	if (fstat(e->new_fd, &st) == -1) {
+ 		lu_error_new(error, lu_error_stat,
+-			     _("couldn't stat `%s': %s"), filename,
++			     _("couldn't stat `%s': %s"), e->new_filename,
+ 			     strerror(errno));
+-		goto err_lock;
++		goto err_editing;
+ 	}
+ 
+-	/* Generate a new line with the right data in it, and allocate space
+-	 * for the contents of the file. */
+-	line = formatter(ent);
+-	contents = g_malloc0(st.st_size + 1);
+-
+-	/* We sanity-check here to make sure that the entity isn't already
+-	 * listed in the file by name by searching for the initial part of
+-	 * the line. */
+-	if (line && strchr(line, ':')) {
+-		fragment1 = g_strndup(line, strchr(line, ':') - line + 1);
+-	} else {
+-		if (line && strchr(line, '\n')) {
+-			fragment1 = g_strndup(line,
+-					      strchr(line, '\n') - line + 1);
+-		} else {
+-			fragment1 = g_strdup(line);
+-		}
+-	}
+-	fragment2 = g_strconcat("\n", fragment1, NULL);
+-
+ 	/* Read the entire file in.  There's some room for improvement here,
+ 	 * but at least we still have the lock, so it's not going to get
+ 	 * funky on us. */
+-	if (read(fd, contents, st.st_size) != st.st_size) {
++	contents = g_malloc0(st.st_size + 1);
++	if (read(e->new_fd, contents, st.st_size) != st.st_size) {
+ 		lu_error_new(error, lu_error_read,
+ 			     _("couldn't read from `%s': %s"),
+-			     filename, strerror(errno));
+-		goto err_fragment2;
++			     e->new_filename, strerror(errno));
++		goto err_contents;
+ 	}
+ 
+-	/* Check if the beginning of the file is the same as the beginning
+-	 * of the entry. */
+-	if (strncmp(contents, fragment1, strlen(fragment1)) == 0) {
+-		lu_error_new(error, lu_error_generic,
+-			     _("entry already present in file"));
+-		goto err_fragment2;
+-	} else
+-	/* If not, search for a newline followed by the beginning of
+-	 * the entry. */
+-	if (strstr(contents, fragment2) != NULL) {
++	/* Sanity-check to make sure that the entity isn't already listed in
++	   the file. */
++	if (entry_name_conflicts(contents, line)) {
+ 		lu_error_new(error, lu_error_generic,
+ 			     _("entry already present in file"));
+-		goto err_fragment2;
++		goto err_contents;
+ 	}
+ 	/* Hooray, we can add this entry at the end of the file. */
+-	offset = lseek(fd, 0, SEEK_END);
++	offset = lseek(e->new_fd, 0, SEEK_END);
+ 	if (offset == -1) {
+ 		lu_error_new(error, lu_error_write,
+ 			     _("couldn't write to `%s': %s"),
+-			     filename, strerror(errno));
+-		goto err_fragment2;
++			     e->new_filename, strerror(errno));
++		goto err_contents;
+ 	}
+ 	/* If the last byte in the file isn't a newline, add one, and silently
+ 	 * curse people who use text editors (which shall remain unnamed) which
+ 	 * allow saving of the file without a final line terminator. */
+ 	if ((st.st_size > 0) && (contents[st.st_size - 1] != '\n')) {
+-		if (write(fd, "\n", 1) != 1) {
++		if (write(e->new_fd, "\n", 1) != 1) {
+ 			lu_error_new(error, lu_error_write,
+ 				     _("couldn't write to `%s': %s"),
+-				     filename,
+-				     strerror(errno));
+-			goto err_fragment2;
++				     e->new_filename, strerror(errno));
++			goto err_contents;
+ 		}
+ 	}
+ 	/* Attempt to write the entire line to the end. */
+-	r = write(fd, line, strlen(line));
++	r = write(e->new_fd, line, strlen(line));
+ 	if ((size_t)r != strlen(line)) {
+ 		/* Oh, come on! */
+ 		lu_error_new(error, lu_error_write,
+-			     _("couldn't write to `%s': %s"),
+-			     filename,
++			     _("couldn't write to `%s': %s"), e->new_filename,
+ 			     strerror(errno));
+-		/* Truncate off whatever we actually managed to write and
+-		 * give up. */
+-		(void)ftruncate(fd, offset);
+-		goto err_fragment2;
++		goto err_contents;
+ 	}
+-	/* Hey, it succeeded. */
+ 	ret = TRUE;
+ 	/* Fall through */
+ 
+- err_fragment2:
+-	g_free(fragment2);
+-	g_free(fragment1);
++err_contents:
+ 	g_free(contents);
++err_editing:
++	ret = editing_close(e, ret, ret, error); /* Commit/rollback happens here. */
++err_line:
+ 	g_free(line);
+- err_lock:
+-	lu_util_lock_free(lock);
+- err_fd:
+-	close(fd);
+-err_fscreate:
+-	lu_util_fscreate_restore(fscreate);
+- err_filename:
+-	g_free(filename);
++err:
+ 	return ret;
+ }
+ 
++
+ /* Make last-minute changes to the structures before adding them. */
+ static gboolean
+ lu_files_user_add_prep(struct lu_module *module, struct lu_ent *ent,
+@@ -883,8 +1095,8 @@ static gboolean
+ lu_files_user_add(struct lu_module *module, struct lu_ent *ent,
+ 		  struct lu_error **error)
+ {
+-	return generic_add(module, suffix_passwd, lu_files_format_user, ent,
+-			   error);
++	return generic_add(module, suffix_passwd, format_passwd,
++            G_N_ELEMENTS(format_passwd), ent, error);
+ }
+ 
+ /* Make last-minute changes to the record before adding it to /etc/shadow. */
+@@ -912,8 +1124,10 @@ static gboolean
+ lu_shadow_user_add(struct lu_module *module, struct lu_ent *ent,
+ 		   struct lu_error **error)
+ {
+-	return generic_add(module, suffix_shadow, lu_shadow_format_user, ent,
+-			   error);
++
++	return generic_add(module, suffix_shadow, format_shadow,
++			   G_N_ELEMENTS(format_shadow), ent, error);
++
+ }
+ 
+ /* Make last-minute changes before adding the group to the group file. */
+@@ -932,8 +1146,8 @@ static gboolean
+ lu_files_group_add(struct lu_module *module, struct lu_ent *ent,
+ 		   struct lu_error **error)
+ {
+-	return generic_add(module, suffix_group, lu_files_format_group, ent,
+-			   error);
++	return generic_add(module, suffix_group, format_group,
++			   G_N_ELEMENTS(format_group), ent, error);
+ }
+ 
+ /* Make last-minute changes before adding the shadowed group. */
+@@ -961,8 +1175,8 @@ static gboolean
+ lu_shadow_group_add(struct lu_module *module, struct lu_ent *ent,
+ 		    struct lu_error **error)
+ {
+-	return generic_add(module, suffix_gshadow, lu_shadow_format_group, ent,
+-			   error);
++	return generic_add(module, suffix_gshadow, format_gshadow,
++			   G_N_ELEMENTS(format_gshadow), ent, error);
+ }
+ 
+ /* Modify a particular record in the given file, field by field, using the
+@@ -972,14 +1186,13 @@ generic_mod(struct lu_module *module, const char *file_suffix,
+ 	    const struct format_specifier *formats, size_t format_count,
+ 	    struct lu_ent *ent, struct lu_error **error)
+ {
+-	lu_security_context_t fscreate;
+-	char *filename, *key;
+-	int fd = -1;
+-	gpointer lock;
+-	size_t i;
+-	const char *dir, *name_attribute;
+-	GValueArray *names;
++	struct editing *e;
++	char *new_line, *contents, *line, *rest;
++	char *current_name, *fragment;
++	const char *name_attribute;
+ 	gboolean ret = FALSE;
++	struct stat st;
++	size_t len;
+ 
+ 	g_assert(module != NULL);
+ 	g_assert(formats != NULL);
+@@ -995,88 +1208,98 @@ generic_mod(struct lu_module *module, const char *file_suffix,
+ 	else
+ 		g_assert_not_reached();
+ 
+-	names = lu_ent_get_current(ent, name_attribute);
+-	if (names == NULL) {
++	current_name = lu_ent_get_first_value_strdup_current(ent,
++							     name_attribute);
++	if (current_name == NULL) {
+ 		lu_error_new(error, lu_error_generic,
+ 			     _("entity object has no %s attribute"),
+ 			     name_attribute);
+ 		return FALSE;
+ 	}
+ 
+-	/* Generate the name of the file to open. */
+-	key = g_strconcat(module->name, "/directory", NULL);
+-	dir = lu_cfg_read_single(module->lu_context, key, "/etc");
+-	filename = g_strconcat(dir, file_suffix, NULL);
+-	g_free(key);
++	new_line = format_generic(ent, formats, format_count, error);
++	if (new_line == NULL)
++		goto err_current_name;
+ 
+-	if (!lu_util_fscreate_save(&fscreate, error))
+-		goto err_filename;
+-	if (!lu_util_fscreate_from_file(filename, error))
+-		goto err_fscreate;
+-	/* Create a backup file. */
+-	if (lu_files_create_backup(filename, error) == FALSE)
+-		goto err_fscreate;
++	e = editing_open(module, file_suffix, error);
++	if (e == NULL)
++		goto err_new_line;
+ 
+-	/* Open the file to be modified. */
+-	fd = open(filename, O_RDWR);
+-	if (fd == -1) {
+-		lu_error_new(error, lu_error_open,
+-			     _("couldn't open `%s': %s"), filename,
+-			     strerror(errno));
+-		goto err_fscreate;
++	if (fstat(e->new_fd, &st) == -1) {
++		lu_error_new(error, lu_error_stat, _("couldn't stat `%s': %s"),
++			     e->new_filename, strerror(errno));
++		goto err_editing;
+ 	}
+ 
+-	/* Lock the file. */
+-	if ((lock = lu_util_lock_obtain(fd, error)) == NULL)
+-		goto err_fd;
+-
+-	/* We iterate over all of the fields individually. */
+-	for (i = 0; i < format_count; i++) {
+-		GValue *value;
+-		char *field;
+-		gboolean ret2;
+-
+-		field = format_field(ent, formats + i);
+-
+-		/* Get the current name for this entity. */
+-		value = g_value_array_get_nth(names, 0);
++	contents = g_malloc(st.st_size + 1 + strlen(new_line));
++	if (read(e->new_fd, contents, st.st_size) != st.st_size) {
++		lu_error_new(error, lu_error_read,
++			     _("couldn't read from `%s': %s"), e->new_filename,
++			     strerror(errno));
++		goto err_contents;
++	}
++	contents[st.st_size] = '\0';
+ 
+-		/* Write the new value. */
+-		ret2 = lu_util_field_write(fd, g_value_get_string(value),
+-					   i + 1, field, error);
+-		g_free(field);
++	fragment = g_strconcat("\n", current_name, ":", (const gchar *)NULL);
++	len = strlen(current_name);
++	if (strncmp(contents, current_name, len) == 0 && contents[len] == ':')
++		line = contents;
++	else {
++		line = strstr(contents, fragment);
++		if (line != NULL)
++			line++;
++	}
++	g_free(fragment);
+ 
+-		/* If we had a write error, we fail now. */
+-		if (ret2 == FALSE)
+-			goto err_lock;
++	if ((strncmp(new_line, current_name, len) != 0 || new_line[len] != ':')
++	    && entry_name_conflicts(contents, new_line)) {
++		lu_error_new(error, lu_error_generic,
++			     _("entry with conflicting name already present "
++			       "in file"));
++		goto err_contents;
++	}
+ 
+-		/* We may have just renamed the account (we're safe assuming
+-		 * the new name is correct here because if we renamed it, we
+-		 * changed the name field first), so switch to using the
+-		 * account's new name. */
+-		names = lu_ent_get(ent, name_attribute);
+-		if (names == NULL) {
+-			lu_error_new(error, lu_error_generic,
+-				     _("entity object has no %s attribute"),
+-				     name_attribute);
+-			goto err_lock;
+-		}
++	if (line == NULL) {
++		lu_error_new(error, lu_error_search, NULL);
++		goto err_contents;
+ 	}
+ 
++	rest = strchr(line, '\n');
++	if (rest != NULL)
++		rest++;
++	else
++		rest = strchr(line, '\0');
++	memmove(line + strlen(new_line), rest,
++		contents + st.st_size + 1 - rest);
++	memcpy(line, new_line, strlen(new_line));
++	if (lseek(e->new_fd, line - contents, SEEK_SET) == -1) {
++		lu_error_new(error, lu_error_write, NULL);
++		goto err_contents;
++	}
++	len = strlen(line);
++	if ((size_t)write(e->new_fd, line, len) != len) {
++		lu_error_new(error, lu_error_write, NULL);
++		goto err_contents;
++	}
++	if (ftruncate(e->new_fd, (line - contents) + len) != 0) {
++		lu_error_new(error, lu_error_write, NULL);
++		goto err_contents;
++	}
+ 	ret = TRUE;
+ 	/* Fall through */
+ 
+- err_lock:
+-	lu_util_lock_free(lock);
+- err_fd:
+-	close(fd);
+-err_fscreate:
+-	lu_util_fscreate_restore(fscreate);
+- err_filename:
+-	g_free(filename);
++err_contents:
++	g_free(contents);
++err_editing:
++	ret = editing_close(e, ret, ret, error); /* Commit/rollback happens here. */
++err_new_line:
++	g_free(new_line);
++err_current_name:
++	g_free(current_name);
+ 	return ret;
+ }
+ 
++
+ /* Modify an entry in the passwd file. */
+ static gboolean
+ lu_files_user_mod(struct lu_module *module, struct lu_ent *ent,
+@@ -1118,24 +1341,20 @@ static gboolean
+ generic_del(struct lu_module *module, const char *file_suffix,
+ 	    struct lu_ent *ent, struct lu_error **error)
+ {
+-	lu_security_context_t fscreate;
+-	GValueArray *name = NULL;
+-	GValue *value;
+-	char *contents, *filename, *key;
+-	char *fragment1, *fragment2;
+-	const char *dir;
++	struct editing *e;
++	char *name;
++	char *contents;
++	char *fragment2;
+ 	struct stat st;
+ 	size_t len;
+-	int fd;
+-        gboolean ret = FALSE;
++        gboolean commit = FALSE, ret = FALSE;
+ 	gboolean found;
+-	gpointer lock;
+ 
+ 	/* Get the entity's current name. */
+ 	if (ent->type == lu_user)
+-		name = lu_ent_get_current(ent, LU_USERNAME);
++		name = lu_ent_get_first_value_strdup_current(ent, LU_USERNAME);
+ 	else if (ent->type == lu_group)
+-		name = lu_ent_get_current(ent, LU_GROUPNAME);
++		name = lu_ent_get_first_value_strdup_current(ent, LU_GROUPNAME);
+ 	else
+ 		g_assert_not_reached();
+ 	g_assert(name != NULL);
+@@ -1143,66 +1362,40 @@ generic_del(struct lu_module *module, const char *file_suffix,
+ 	g_assert(module != NULL);
+ 	g_assert(ent != NULL);
+ 
+-	/* Generate the name of the file we're going to modify. */
+-	key = g_strconcat(module->name, "/directory", NULL);
+-	dir = lu_cfg_read_single(module->lu_context, key, "/etc");
+-	filename = g_strconcat(dir, file_suffix, NULL);
+-	g_free(key);
+-
+-	if (!lu_util_fscreate_save(&fscreate, error))
+-		goto err_filename;
+-	if (!lu_util_fscreate_from_file(filename, error))
+-		goto err_fscreate;
+-	/* Create a backup of that file. */
+-	if (lu_files_create_backup(filename, error) == FALSE)
+-		goto err_fscreate;
+-
+-	/* Open the file to be modified. */
+-	fd = open(filename, O_RDWR);
+-	if (fd == -1) {
+-		lu_error_new(error, lu_error_open,
+-			     _("couldn't open `%s': %s"), filename,
+-			     strerror(errno));
+-		goto err_fscreate;
+-	}
+-
+-	/* Lock the file. */
+-	if ((lock = lu_util_lock_obtain(fd, error)) == NULL)
+-		goto err_fd;
++	e = editing_open(module, file_suffix, error);
++	if (e == NULL)
++		goto err_name;
+ 
+ 	/* Determine the file's size. */
+-	if (fstat(fd, &st) == -1) {
++	if (fstat(e->new_fd, &st) == -1) {
+ 		lu_error_new(error, lu_error_stat,
+-			     _("couldn't stat `%s': %s"), filename,
++			     _("couldn't stat `%s': %s"), e->new_filename,
+ 			     strerror(errno));
+-		goto err_lock;
++		goto err_editing;
+ 	}
+ 
+ 	/* Allocate space to hold the file and read it all in. */
+ 	contents = g_malloc(st.st_size + 1);
+-	if (read(fd, contents, st.st_size) != st.st_size) {
++	if (read(e->new_fd, contents, st.st_size) != st.st_size) {
+ 		lu_error_new(error, lu_error_read,
+-			     _("couldn't read from `%s': %s"), filename,
++			     _("couldn't read from `%s': %s"), e->new_filename,
+ 			     strerror(errno));
+ 		goto err_contents;
+ 	}
+ 	contents[st.st_size] = '\0';
+ 
+-	/* Generate string versions of what the beginning of a line might
+-	 * look like. */
+-	value = g_value_array_get_nth(name, 0);
+-	fragment1 = lu_value_strdup(value);
+-	fragment2 = g_strconcat("\n", fragment1, ":", (const gchar *)NULL);
++	/* Generate a pattern for a beginning of a non-first line */
++	fragment2 = g_strconcat("\n", name, ":", (const gchar *)NULL);
+ 
+ 	/* Remove all occurrences of this entry from the file. */
+-	len = strlen(fragment1);
++	len = strlen(name);
+ 	do {
+ 		char *tmp;
+ 
+ 		found = FALSE;
+ 		/* If the data is on the first line of the file, we remove the
+ 		 * first line. */
+-		if (strncmp(contents, fragment1, len) == 0
++		if (strncmp(contents, name, len) == 0
+ 			&& contents[len] == ':') {
+ 			char *p;
+ 
+@@ -1226,7 +1419,6 @@ generic_del(struct lu_module *module, const char *file_suffix,
+ 		}
+ 	} while(found);
+ 
+-	g_free(fragment1);
+ 	g_free(fragment2);
+ 
+ 	/* If the resulting memory chunk is the same size as the file, then
+@@ -1239,41 +1431,39 @@ generic_del(struct lu_module *module, const char *file_suffix,
+ 
+ 	/* Otherwise we need to write the new data to the file.  Jump back to
+ 	 * the beginning of the file. */
+-	if (lseek(fd, 0, SEEK_SET) == -1) {
++	if (lseek(e->new_fd, 0, SEEK_SET) == -1) {
+ 		lu_error_new(error, lu_error_write,
+-			     _("couldn't write to `%s': %s"), filename,
++			     _("couldn't write to `%s': %s"), e->new_filename,
+ 			     strerror(errno));
+ 		goto err_contents;
+ 	}
+ 
+ 	/* Write the new contents out. */
+-	if ((size_t)write(fd, contents, len) != len) {
++	if ((size_t)write(e->new_fd, contents, len) != len) {
+ 		lu_error_new(error, lu_error_write,
+-			     _("couldn't write to `%s': %s"), filename,
++			     _("couldn't write to `%s': %s"), e->new_filename,
+ 			     strerror(errno));
+ 		goto err_contents;
+ 	}
+ 
+ 	/* Truncate the file to the new (certainly shorter) length. */
+-	if (ftruncate(fd, len) == -1) {
++	if (ftruncate(e->new_fd, len) == -1) {
+ 		lu_error_new(error, lu_error_generic,
+-			     _("couldn't write to `%s': %s"), filename,
++			     _("couldn't write to `%s': %s"), e->new_filename,
+ 			     strerror(errno));
+ 		goto err_contents;
+ 	}
++	commit = TRUE;
+ 	ret = TRUE;
+ 	/* Fall through */
+ 
+  err_contents:
+ 	g_free(contents);
+- err_lock:
+-	lu_util_lock_free(lock);
+- err_fd:
+-	close(fd);
+-err_fscreate:
+-	lu_util_fscreate_restore(fscreate);
+- err_filename:
+-	g_free(filename);
++err_editing:
++	/* Commit/rollback happens here. */
++	ret = editing_close(e, commit, ret, error);
++err_name:
++	g_free(name);
+ 	return ret;
+ }
+ 
+@@ -1353,62 +1543,29 @@ static gboolean
+ generic_lock(struct lu_module *module, const char *file_suffix, int field,
+ 	     struct lu_ent *ent, enum lock_op op, struct lu_error **error)
+ {
+-	lu_security_context_t fscreate;
+-	GValueArray *name = NULL;
+-	GValue *val;
+-	char *filename, *key;
+-	const char *dir;
+-	char *value, *new_value, *namestring;
+-	int fd;
+-	gpointer lock;
+-	gboolean ret = FALSE;
++	struct editing *e;
++	char *value, *new_value, *name;
++	gboolean commit = FALSE, ret = FALSE;
+ 
+ 	/* Get the name which keys the entries of interest in the file. */
+ 	g_assert((ent->type == lu_user) || (ent->type == lu_group));
+ 	if (ent->type == lu_user)
+-		name = lu_ent_get_current(ent, LU_USERNAME);
++		name = lu_ent_get_first_value_strdup_current(ent, LU_USERNAME);
+ 	if (ent->type == lu_group)
+-		name = lu_ent_get_current(ent, LU_GROUPNAME);
++		name = lu_ent_get_first_value_strdup_current(ent, LU_GROUPNAME);
+ 	g_assert(name != NULL);
+ 
+ 	g_assert(module != NULL);
+ 	g_assert(ent != NULL);
+ 
+-	/* Generate the name of the file we're going to modify. */
+-	key = g_strconcat(module->name, "/directory", NULL);
+-	dir = lu_cfg_read_single(module->lu_context, key, "/etc");
+-	filename = g_strconcat(dir, file_suffix, NULL);
+-	g_free(key);
+-
+-	if (!lu_util_fscreate_save(&fscreate, error))
+-		goto err_filename;
+-	if (!lu_util_fscreate_from_file(filename, error))
+-		goto err_fscreate;
+-	/* Create a backup of the file. */
+-	if (lu_files_create_backup(filename, error) == FALSE)
+-		goto err_fscreate;
+-
+-	/* Open the file. */
+-	fd = open(filename, O_RDWR);
+-	if (fd == -1) {
+-		lu_error_new(error, lu_error_open,
+-			     _("couldn't open `%s': %s"), filename,
+-			     strerror(errno));
+-		goto err_fscreate;
+-	}
+-
+-	/* Lock the file. */
+-	if ((lock = lu_util_lock_obtain(fd, error)) == NULL)
+-		goto err_fd;
+-
+-	/* Generate a string representation of the name. */
+-	val = g_value_array_get_nth(name, 0);
+-	namestring = lu_value_strdup(val);
++	e = editing_open(module, file_suffix, error);
++	if (e == NULL)
++		goto err_name;
+ 
+ 	/* Read the old value from the file. */
+-	value = lu_util_field_read(fd, namestring, field, error);
++	value = lu_util_field_read(e->new_fd, name, field, error);
+ 	if (value == NULL)
+-		goto err_namestring;
++		goto err_editing;
+ 
+ 	/* Check that we actually care about this.  If there's a non-empty,
+ 	 * not locked string in there, but it's too short to be a hash, then
+@@ -1416,28 +1573,28 @@ generic_lock(struct lu_module *module, const char *file_suffix, int field,
+ 	if (LU_CRYPT_INVALID(value)) {
+ 		g_free(value);
+ 		ret = TRUE;
+-		goto err_namestring;
++		goto err_editing;
+ 	}
+ 
+ 	/* Generate a new value for the file. */
+ 	new_value = lock_process(value, op, ent, error);
+ 	g_free(value);
+ 	if (new_value == NULL)
+-		goto err_namestring;
++		goto err_editing;
+ 
+ 	/* Make the change. */
+-	ret = lu_util_field_write(fd, namestring, field, new_value, error);
++	if (lu_util_field_write(e->new_fd, name, field, new_value, error)
++	    == FALSE)
++		goto err_editing;
++	commit = TRUE;
++	ret = TRUE;
+ 	/* Fall through */
+ 
+- err_namestring:
+-	g_free(namestring);
+-	lu_util_lock_free(lock);
+- err_fd:
+-	close(fd);
+-err_fscreate:
+-	lu_util_fscreate_restore(fscreate);
+- err_filename:
+-	g_free(filename);
++err_editing:
++	/* Commit/rollback happens here. */
++	ret = editing_close(e, commit, ret, error);
++err_name:
++	g_free(name);
+ 	return ret;
+ }
+ 
+@@ -1446,31 +1603,23 @@ static gboolean
+ generic_is_locked(struct lu_module *module, const char *file_suffix,
+ 		  int field, struct lu_ent *ent, struct lu_error **error)
+ {
+-	GValueArray *name = NULL;
+-	GValue *val;
+-	char *filename, *key;
+-	const char *dir;
+-	char *value, *namestring;
++	char *filename;
++	char *value, *name;
+ 	int fd;
+-	gpointer lock;
+-	gboolean ret;
++	gboolean ret = FALSE;
+ 
+ 	/* Get the name of this account. */
+ 	g_assert((ent->type == lu_user) || (ent->type == lu_group));
+ 	if (ent->type == lu_user)
+-		name = lu_ent_get_current(ent, LU_USERNAME);
++		name = lu_ent_get_first_value_strdup_current(ent, LU_USERNAME);
+ 	if (ent->type == lu_group)
+-		name = lu_ent_get_current(ent, LU_GROUPNAME);
++		name = lu_ent_get_first_value_strdup_current(ent, LU_GROUPNAME);
+ 	g_assert(name != NULL);
+ 
+ 	g_assert(module != NULL);
+ 	g_assert(ent != NULL);
+ 
+-	/* Construct the name of the file to read. */
+-	key = g_strconcat(module->name, "/directory", NULL);
+-	dir = lu_cfg_read_single(module->lu_context, key, "/etc");
+-	filename = g_strconcat(dir, file_suffix, NULL);
+-	g_free(key);
++	filename = module_filename(module, file_suffix);
+ 
+ 	/* Open the file. */
+ 	fd = open(filename, O_RDONLY);
+@@ -1478,38 +1627,24 @@ generic_is_locked(struct lu_module *module, const char *file_suffix,
+ 		lu_error_new(error, lu_error_open,
+ 			     _("couldn't open `%s': %s"), filename,
+ 			     strerror(errno));
+-		g_free(filename);
+-		return FALSE;
+-	}
+-
+-	/* Lock the file. */
+-	if ((lock = lu_util_lock_obtain(fd, error)) == NULL) {
+-		close(fd);
+-		g_free(filename);
+-		return FALSE;
++		goto err_filename;
+ 	}
+ 
+-	/* Construct the actual name of the account holder(s). */
+-	val = g_value_array_get_nth(name, 0);
+-	namestring = lu_value_strdup(val);
+-
+ 	/* Read the value. */
+-	value = lu_util_field_read(fd, namestring, field, error);
+-	g_free (namestring);
+-	if (value == NULL) {
+-		lu_util_lock_free(lock);
+-		close(fd);
+-		g_free(filename);
+-		return FALSE;
+-	}
++	value = lu_util_field_read(fd, name, field, error);
++	if (value == NULL)
++		goto err_fd;
+ 
+ 	/* It all comes down to this. */
+ 	ret = value[0] == '!';
+ 	g_free(value);
++	/* Fall through */
+ 
+-	lu_util_lock_free(lock);
++err_fd:
+ 	close(fd);
++err_filename:
+ 	g_free(filename);
++	g_free(name);
+ 	return ret;
+ }
+ 
+@@ -1658,72 +1793,39 @@ generic_setpass(struct lu_module *module, const char *file_suffix, int field,
+ 		struct lu_ent *ent, const char *password, gboolean is_shadow,
+ 		struct lu_error **error)
+ {
+-	lu_security_context_t fscreate;
+-	GValueArray *name = NULL;
+-	GValue *val;
+-	char *filename, *key, *value, *namestring;
+-	const char *dir;
+-	int fd;
+-	gpointer lock;
++	struct editing *e;
++	char *value, *name;
+ 	gboolean ret = FALSE;
+ 
+ 	/* Get the name of this account. */
+ 	g_assert((ent->type == lu_user) || (ent->type == lu_group));
+ 	if (ent->type == lu_user)
+-		name = lu_ent_get_current(ent, LU_USERNAME);
++		name = lu_ent_get_first_value_strdup_current(ent, LU_USERNAME);
+ 	else if (ent->type == lu_group)
+-		name = lu_ent_get_current(ent, LU_GROUPNAME);
++		name = lu_ent_get_first_value_strdup_current(ent, LU_GROUPNAME);
+ 	g_assert(name != NULL);
+ 
+ 	g_assert(module != NULL);
+ 	g_assert(ent != NULL);
+ 
+-	/* Construct the name of the file to modify. */
+-	key = g_strconcat(module->name, "/directory", NULL);
+-	dir = lu_cfg_read_single(module->lu_context, key, "/etc");
+-	filename = g_strconcat(dir, file_suffix, NULL);
+-	g_free(key);
+-
+-	if (!lu_util_fscreate_save(&fscreate, error))
+-		goto err_filename;
+-	if (!lu_util_fscreate_from_file(filename, error))
+-		goto err_fscreate;
+-
+-	/* Create a backup of the file. */
+-	if (lu_files_create_backup(filename, error) == FALSE)
+-		goto err_filename;
+-
+-	/* Open the file. */
+-	fd = open(filename, O_RDWR);
+-	if (fd == -1) {
+-		lu_error_new(error, lu_error_open,
+-			     _("couldn't open `%s': %s"), filename,
+-			     strerror(errno));
+-		goto err_fscreate;
+-	}
+-
+-	/* Lock the file. */
+-	if ((lock = lu_util_lock_obtain(fd, error)) == NULL)
+-		goto err_fd;
+-
+-	/* Get the name of the account. */
+-	val = g_value_array_get_nth(name, 0);
+-	namestring = lu_value_strdup(val);
++	e = editing_open(module, file_suffix, error);
++	if (e == NULL)
++		goto err_name;
+ 
+ 	/* Read the current contents of the field. */
+-	value = lu_util_field_read(fd, namestring, field, error);
++	value = lu_util_field_read(e->new_fd, name, field, error);
+ 	if (value == NULL)
+-		goto err_namestring;
++		goto err_editing;
+ 
+ 	/* pam_unix uses shadow passwords only if pw_passwd is "x"
+ 	   (or ##${username}).  Make sure to preserve the shadow marker
+ 	   unmodified (most importantly, don't replace it by an encrypted
+-	   password) -- but only a shadow entry exists. */
++	   password) -- but only if a shadow entry exists. */
+ 	if (!is_shadow && ent_has_shadow(ent)
+ 	    && lu_ent_get_current(ent, LU_SHADOWPASSWORD) != NULL
+ 	    && (strcmp(value, "x") == 0
+ 		|| (strncmp(value, "##", 2) == 0
+-		    && strcmp(value + 2, namestring) == 0))) {
++		    && strcmp(value + 2, name) == 0))) {
+ 		ret = TRUE;
+ 		goto err_value;
+ 	}
+@@ -1738,6 +1840,12 @@ generic_setpass(struct lu_module *module, const char *file_suffix, int field,
+ 	else if (g_ascii_strncasecmp(password, LU_CRYPTED, strlen(LU_CRYPTED))
+ 		 == 0) {
+ 		password = password + strlen(LU_CRYPTED);
++		if (strpbrk(password, ":\n") != NULL) {
++			lu_error_new(error, lu_error_invalid_attribute_value,
++				     _("`:' and `\\n' not allowed in encrypted "
++				       "password"));
++			goto err_value;
++		}
+ 	} else {
+ 		char *salt;
+ 
+@@ -1752,23 +1860,19 @@ generic_setpass(struct lu_module *module, const char *file_suffix, int field,
+ 	}
+ 
+ 	/* Now write our changes to the file. */
+-	ret = lu_util_field_write(fd, namestring, field, password, error);
++	ret = lu_util_field_write(e->new_fd, name, field, password, error);
+ 	/* Fall through */
+ 
+- err_value:
++err_value:
+ 	g_free(value);
+- err_namestring:
+-	g_free(namestring);
+-	lu_util_lock_free(lock);
+- err_fd:
+-	close(fd);
+-err_fscreate:
+-	lu_util_fscreate_restore(fscreate);
+- err_filename:
+-	g_free(filename);
++err_editing:
++	ret = editing_close(e, ret, ret, error); /* Commit/rollback happens here. */
++err_name:
++	g_free(name);
+ 	return ret;
+ }
+ 
++
+ /* Set a user's password in the passwd file. */
+ static gboolean
+ lu_files_user_setpass(struct lu_module *module, struct lu_ent *ent,
+@@ -1841,7 +1945,6 @@ lu_files_enumerate(struct lu_module *module, const char *file_suffix,
+ 		   const char *pattern, struct lu_error **error)
+ {
+ 	int fd;
+-	gpointer lock;
+ 	GValueArray *ret;
+ 	GValue value;
+ 	char *buf;
+@@ -1868,20 +1971,12 @@ lu_files_enumerate(struct lu_module *module, const char *file_suffix,
+ 		return NULL;
+ 	}
+ 
+-	/* Lock the file. */
+-	if ((lock = lu_util_lock_obtain(fd, error)) == NULL) {
+-		close(fd);
+-		g_free(filename);
+-		return NULL;
+-	}
+-
+ 	/* Wrap the file for stdio operations. */
+ 	fp = fdopen(fd, "r");
+ 	if (fp == NULL) {
+ 		lu_error_new(error, lu_error_open,
+ 			     _("couldn't open `%s': %s"), filename,
+ 			     strerror(errno));
+-		lu_util_lock_free(lock);
+ 		close(fd);
+ 		g_free(filename);
+ 		return NULL;
+@@ -1917,7 +2012,6 @@ lu_files_enumerate(struct lu_module *module, const char *file_suffix,
+ 
+ 	/* Clean up. */
+ 	g_value_unset(&value);
+-	lu_util_lock_free(lock);
+ 	fclose(fp);
+ 	g_free(filename);
+ 
+@@ -1946,7 +2040,6 @@ lu_files_users_enumerate_by_group(struct lu_module *module,
+ 				  struct lu_error **error)
+ {
+ 	int fd;
+-	gpointer lock;
+ 	GValueArray *ret;
+ 	GValue value;
+ 	char *buf, grp[CHUNK_SIZE];
+@@ -1975,21 +2068,12 @@ lu_files_users_enumerate_by_group(struct lu_module *module,
+ 		return NULL;
+ 	}
+ 
+-	/* Lock the passwd file. */
+-	if ((lock = lu_util_lock_obtain(fd, error)) == NULL) {
+-		close(fd);
+-		g_free(pwdfilename);
+-		g_free(grpfilename);
+-		return NULL;
+-	}
+-
+ 	/* Wrap the descriptor in a stdio FILE. */
+ 	fp = fdopen(fd, "r");
+ 	if (fp == NULL) {
+ 		lu_error_new(error, lu_error_open,
+ 			     _("couldn't open `%s': %s"), pwdfilename,
+ 			     strerror(errno));
+-		lu_util_lock_free(lock);
+ 		close(fd);
+ 		g_free(pwdfilename);
+ 		g_free(grpfilename);
+@@ -2048,7 +2132,6 @@ lu_files_users_enumerate_by_group(struct lu_module *module,
+ 	}
+ 	/* Close the file. */
+ 	g_value_unset(&value);
+-	lu_util_lock_free(lock);
+ 	fclose(fp);
+ 
+ 	/* Open the group file. */
+@@ -2063,22 +2146,12 @@ lu_files_users_enumerate_by_group(struct lu_module *module,
+ 		return NULL;
+ 	}
+ 
+-	/* Lock the group file. */
+-	if ((lock = lu_util_lock_obtain(fd, error)) == NULL) {
+-		close(fd);
+-		g_free(pwdfilename);
+-		g_free(grpfilename);
+-		g_value_array_free(ret);
+-		return NULL;
+-	}
+-
+ 	/* Wrap the group file in an stdio file. */
+ 	fp = fdopen(fd, "r");
+ 	if (fp == NULL) {
+ 		lu_error_new(error, lu_error_open,
+ 			     _("couldn't open `%s': %s"), grpfilename,
+ 			     strerror(errno));
+-		lu_util_lock_free(lock);
+ 		close(fd);
+ 		g_free(pwdfilename);
+ 		g_free(grpfilename);
+@@ -2133,7 +2206,6 @@ lu_files_users_enumerate_by_group(struct lu_module *module,
+ 	}
+ 
+ 	/* Clean up. */
+-	lu_util_lock_free(lock);
+ 	fclose(fp);
+ 
+ 	g_free(pwdfilename);
+@@ -2150,7 +2222,6 @@ lu_files_groups_enumerate_by_user(struct lu_module *module,
+ 				  struct lu_error **error)
+ {
+ 	int fd;
+-	gpointer lock;
+ 	GValueArray *ret;
+ 	GValue value;
+ 	char *buf;
+@@ -2180,21 +2251,12 @@ lu_files_groups_enumerate_by_user(struct lu_module *module,
+ 		return NULL;
+ 	}
+ 
+-	/* Lock it. */
+-	if ((lock = lu_util_lock_obtain(fd, error)) == NULL) {
+-		close(fd);
+-		g_free(pwdfilename);
+-		g_free(grpfilename);
+-		return NULL;
+-	}
+-
+ 	/* Open it so that we can use stdio. */
+ 	fp = fdopen(fd, "r");
+ 	if (fp == NULL) {
+ 		lu_error_new(error, lu_error_open,
+ 			     _("couldn't open `%s': %s"), pwdfilename,
+ 			     strerror(errno));
+-		lu_util_lock_free(lock);
+ 		close(fd);
+ 		g_free(pwdfilename);
+ 		g_free(grpfilename);
+@@ -2244,7 +2306,6 @@ lu_files_groups_enumerate_by_user(struct lu_module *module,
+ 		}
+ 		g_free(buf);
+ 	}
+-	lu_util_lock_free(lock);
+ 	fclose(fp);
+ 
+ 	/* Open the groups file. */
+@@ -2258,21 +2319,12 @@ lu_files_groups_enumerate_by_user(struct lu_module *module,
+ 		return NULL;
+ 	}
+ 
+-	/* Lock it. */
+-	if ((lock = lu_util_lock_obtain(fd, error)) == NULL) {
+-		close(fd);
+-		g_free(pwdfilename);
+-		g_free(grpfilename);
+-		return NULL;
+-	}
+-
+ 	/* Open it so that we can use stdio. */
+ 	fp = fdopen(fd, "r");
+ 	if (fp == NULL) {
+ 		lu_error_new(error, lu_error_open,
+ 			     _("couldn't open `%s': %s"), grpfilename,
+ 			     strerror(errno));
+-		lu_util_lock_free(lock);
+ 		close(fd);
+ 		g_free(pwdfilename);
+ 		g_free(grpfilename);
+@@ -2330,7 +2382,6 @@ lu_files_groups_enumerate_by_user(struct lu_module *module,
+ 	}
+ 	g_value_unset(&value);
+ 
+-	lu_util_lock_free(lock);
+ 	fclose(fp);
+ 	g_free(pwdfilename);
+ 	g_free(grpfilename);
+@@ -2346,7 +2397,6 @@ lu_files_enumerate_full(struct lu_module *module, const char *file_suffix,
+ 			struct lu_error **error)
+ {
+ 	int fd;
+-	gpointer lock;
+ 	GPtrArray *ret = NULL;
+ 	char *buf;
+ 	char *key, *filename;
+@@ -2371,19 +2421,12 @@ lu_files_enumerate_full(struct lu_module *module, const char *file_suffix,
+ 		goto err_filename;
+ 	}
+ 
+-	/* Lock the file. */
+-	if ((lock = lu_util_lock_obtain(fd, error)) == NULL) {
+-		close(fd);
+-		goto err_filename;
+-	}
+-
+ 	/* Wrap the file up in stdio. */
+ 	fp = fdopen(fd, "r");
+ 	if (fp == NULL) {
+ 		lu_error_new(error, lu_error_open,
+ 			     _("couldn't open `%s': %s"), filename,
+ 			     strerror(errno));
+-		lu_util_lock_free(lock);
+ 		close(fd);
+ 		goto err_filename;
+ 	}
+@@ -2418,7 +2461,6 @@ lu_files_enumerate_full(struct lu_module *module, const char *file_suffix,
+ 		g_free(key);
+ 	}
+ 
+-	lu_util_lock_free(lock);
+ 	fclose(fp);
+ 
+  err_filename:
+diff --git a/tests/files_test.py b/tests/files_test.py
+index 2b93129..6c5a554 100644
+--- a/tests/files_test.py
++++ b/tests/files_test.py
+@@ -262,6 +262,21 @@ class Tests(unittest.TestCase):
+         self.assertNotEqual(e[libuser.SHADOWFLAG], [255])
+         e[libuser.SHADOWFLAG] = 255
+         self.a.modifyUser(e, False)
++
++    def testUserAdd9(self):
++        # '\n' in field values
++        # libuser.USERPASSWORD not tested because lu_shadow_user_add_prep()
++        # always replaces it by 'x'.  libuser.UIDNUMBER not tested because
++        # ent_has_name_and_id() interprets the value as a number.
++        for field in (libuser.USERNAME, libuser.GIDNUMBER, libuser.GECOS,
++                      libuser.HOMEDIRECTORY, libuser.LOGINSHELL,
++                      libuser.SHADOWPASSWORD, libuser.SHADOWLASTCHANGE, 
++                      libuser.SHADOWMIN, libuser.SHADOWMAX, 
++                      libuser.SHADOWWARNING, libuser.SHADOWINACTIVE, 
++                      libuser.SHADOWEXPIRE, libuser.SHADOWFLAG):
++            e = self.a.initUser('user_6_9' + field)
++            e[field] = str(e[field][0]) + '\nx'
++            self.assertRaises(RuntimeError, self.a.addUser, e, False, False)
+         del e
+         e = self.a.lookupUserByName('user7_2')
+         self.assertEqual(e, None)
+@@ -316,6 +331,26 @@ class Tests(unittest.TestCase):
+         self.assertEqual(e[libuser.SHADOWMAX], [99999])
+         self.assertEqual(e[libuser.SHADOWFLAG], [-1])
+ 
++    def testUserMod8(self):
++        # '\n' in field values
++        # libuser.USERPASSWORD not tested because lu_shadow_user_add_prep()
++        # always replaces it by 'x'.  libuser.UIDNUMBER not tested because
++        # ent_has_name_and_id() interprets the value as a number.
++        for field in (libuser.USERNAME, libuser.USERPASSWORD, libuser.GIDNUMBER,
++                      libuser.GECOS, libuser.HOMEDIRECTORY, libuser.LOGINSHELL,
++                      libuser.SHADOWPASSWORD, libuser.SHADOWLASTCHANGE,
++                      libuser.SHADOWMIN, libuser.SHADOWMAX,
++                      libuser.SHADOWWARNING, libuser.SHADOWINACTIVE,
++                      libuser.SHADOWEXPIRE, libuser.SHADOWFLAG):
++            e = self.a.initUser('user7_9' + field)
++            self.a.addUser(e, False, False)
++            del e
++            e = self.a.lookupUserByName('user7_9' + field)
++            self.assertIsNotNone(e)
++            self.assertNotIn('\n', str(e[field][0]))
++            e[field] = str(e[field][0]) + '\nx'
++            self.assertRaises(RuntimeError, self.a.modifyUser, e, False)
++
+     def testUserDel(self):
+         e = self.a.initUser('user8_1')
+         self.a.addUser(e, False, False)
+@@ -512,6 +547,22 @@ class Tests(unittest.TestCase):
+         self.assertEqual(e[libuser.SHADOWPASSWORD], [''])
+         self.assert_(e[libuser.SHADOWLASTCHANGE][0] > 10000)
+ 
++        self.assertEqual(e[libuser.SHADOWPASSWORD], [crypted])
++
++    def testUserSetpass5(self):
++        # '\n' in field value
++        e = self.a.initUser('user12_5')
++        self.a.addUser(e, False, False)
++        del e
++        e = self.a.lookupUserByName('user12_5')
++        self.assertNotIn('\n', e[libuser.SHADOWPASSWORD][0])
++        self.assertRaises(SystemError, self.a.setpassUser, e, 'a\nb', True)
++        self.a.setpassUser(e, 'a\nb', False)
++        del e
++        e = self.a.lookupUserByName('user12_5')
++        self.assertEqual(e[libuser.SHADOWPASSWORD][0][:3], '$1$')
++        self.assertNotIn('\n', e[libuser.SHADOWPASSWORD][0])
++        crypted = crypt.crypt('a\nb', e[libuser.SHADOWPASSWORD][0])
+     def testUsersEnumerate1(self):
+         e = self.a.initUser('user14_1')
+         self.a.addUser(e, False, False)
+@@ -774,6 +825,20 @@ class Tests(unittest.TestCase):
+         self.assertEqual(e[libuser.GROUPPASSWORD], ['x'])
+         self.assertEqual(e[libuser.SHADOWPASSWORD], ['!!04cmES7HM6wtg'])
+ 
++
++    def testGroupAdd6(self):
++        # '\n' in field values
++        # libuser.GROUPPASSWORD not tested because lu_shadow_group_add_prep()
++        # always replaces it by 'x'.  libuser.GIDNUMBER not tested because
++        # ent_has_name_and_id() interprets the value as a number.
++        for field in (libuser.GROUPNAME, libuser.SHADOWPASSWORD,
++                      libuser.MEMBERNAME, libuser.ADMINISTRATORNAME):
++            e = self.a.initGroup('group_21_6' + field)
++            if e.has_key(field):
++                e[field] = str(e[field][0]) + '\nx'
++            else:
++                e[field] = field + '\nx'
++            self.assertRaises(RuntimeError, self.a.addGroup, e)
+     def testGroupLock2(self):
+         e = self.a.initGroup('group24_2')
+         self.a.addGroup(e)
+@@ -887,6 +952,25 @@ class Tests(unittest.TestCase):
+         del e
+         e = self.a.lookupGroupByName('group27_1')
+         self.assertEqual(e[libuser.GROUPPASSWORD], ['x'])
++    def testGroupMod7(self):
++        # '\n' in field values
++        # libuser.GIDNUMBER not tested because ent_has_name_and_id() interprets
++        # the value as a number.
++        for field in (libuser.GROUPNAME, libuser.GROUPPASSWORD,
++                      libuser.SHADOWPASSWORD, libuser.MEMBERNAME,
++                      libuser.ADMINISTRATORNAME):
++            e = self.a.initGroup('group22_8' + field)
++            self.a.addGroup(e)
++            del e
++            e = self.a.lookupGroupByName('group22_8' + field)
++            self.assertIsNotNone(e)
++            if e.has_key(field):
++                self.assertNotIn('\n', str(e[field][0]))
++                e[field] = str(e[field][0]) + '\nx'
++            else:
++                e[field] = field + '\nx'
++            self.assertRaises(RuntimeError, self.a.modifyGroup, e)
++
+         self.assertEqual(e[libuser.SHADOWPASSWORD], ['06aZrb3pzuu/6'])
+         self.a.setpassGroup(e, 'password', False)
+         self.assertEqual(e[libuser.GROUPPASSWORD], ['x'])
+@@ -1022,3 +1106,19 @@ class Tests(unittest.TestCase):
+ 
+ if __name__ == '__main__':
+     unittest.main()
++        self.assertEqual(e[libuser.SHADOWPASSWORD], [crypted])
++
++    def testGroupSetpass4(self):
++        # '\n' in field value
++        e = self.a.initGroup('group27_5')
++        self.a.addGroup(e)
++        del e
++        e = self.a.lookupGroupByName('group27_5')
++        self.assertNotIn('\n', e[libuser.SHADOWPASSWORD][0])
++        self.assertRaises(SystemError, self.a.setpassGroup, e, 'a\nb', True)
++        self.a.setpassGroup(e, 'a\nb', False)
++        del e
++        e = self.a.lookupGroupByName('group27_5')
++        self.assertEqual(e[libuser.SHADOWPASSWORD][0][:3], '$1$')
++        self.assertNotIn('\n', e[libuser.SHADOWPASSWORD][0])
++        crypted = crypt.crypt('a\nb', e[libuser.SHADOWPASSWORD][0])
--- libuser-0.56.9.dfsg.1.orig/debian/patches/00list
+++ libuser-0.56.9.dfsg.1/debian/patches/00list
@@ -0,0 +1,3 @@
+01manpage
+02libuser-0.56.18-default-pw.dpatch
+CVE-2015-3245-and-CVE-2015-3246.dpatch
