File libuser-CVE-2015-3246.patch of Package libuser-python27

diff -up libuser-0.56.13/lib/user_private.h.CVE-2015-3246 libuser-0.56.13/lib/user_private.h
--- libuser-0.56.13/lib/user_private.h.CVE-2015-3246	2009-12-11 11:05:48.000000000 +0100
+++ libuser-0.56.13/lib/user_private.h	2015-07-08 14:02:50.641242200 +0200
@@ -321,9 +321,11 @@ typedef char lu_security_context_t; /* "
   ((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 -up libuser-0.56.13/lib/util.c.CVE-2015-3246 libuser-0.56.13/lib/util.c
--- libuser-0.56.13/lib/util.c.CVE-2015-3246	2015-07-08 14:02:50.640242231 +0200
+++ libuser-0.56.13/lib/util.c	2015-07-08 14:02:50.642242170 +0200
@@ -643,7 +643,7 @@ lu_util_field_write(int fd, const char *
 			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 -up libuser-0.56.13/modules/files.c.CVE-2015-3246 libuser-0.56.13/modules/files.c
--- libuser-0.56.13/modules/files.c.CVE-2015-3246	2009-12-11 11:05:48.000000000 +0100
+++ libuser-0.56.13/modules/files.c	2015-07-08 14:05:12.708909044 +0200
@@ -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,93 @@ static const char suffix_shadow[] = "/sh
 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,
+			     _("couldn't stat `%s': %s"), input_filename,
 			     strerror(errno));
-		goto err_ilock;
+		goto err_ifd;
 	}
 
-	/* 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);
+	/* 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);
 	if (ofd == -1) {
 		lu_error_new(error, lu_error_open,
-			     _("error creating `%s': %s"), backupname,
-			     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,
+			     _("error creating `%s': %s"), output_filename,
 			     strerror(errno));
-		goto err_ofd;
+		goto err_ifd;
 	}
 
-	/* Now lock the output file. */
-	if ((olock = lu_util_lock_obtain(ofd, error)) == NULL)
-		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 (fchown(ofd, st.st_uid, st.st_gid) == -1 && errno != EPERM) {
 		lu_error_new(error, lu_error_generic,
-			     _("Error changing owner of `%s': %s"), backupname,
-			     strerror(errno));
-		goto err_olock;
+			     _("Error changing owner of `%s': %s"),
+			     output_filename, strerror(errno));
+		goto err_ofd;
 	}
-	fchmod(ofd, ist.st_mode);
+	if (fchmod(ofd, st.st_mode) == -1) {
+ 		lu_error_new(error, lu_error_generic,
+			     _("Error changing mode of `%s': %s"),
+			     output_filename, strerror(errno));
+		goto err_ofd;
+ 	}
 
 	/* Copy the data, block by block. */
 	for (;;) {
@@ -178,9 +190,9 @@ lu_files_create_backup(const char *filen
 			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 +206,297 @@ lu_files_create_backup(const char *filen
 					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;
-	}
-
-	/* 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;
+	/* 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 = TRUE;
+	res = ofd;
+	goto err_ifd; /* Do not close ofd */
 
- 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 +678,6 @@ generic_lookup(struct lu_module *module,
 	gboolean ret;
 	const char *dir;
 	int fd = -1;
-	gpointer lock;
 	char *line, *filename, *key;
 
 	g_assert(module != NULL);
@@ -438,7 +692,7 @@ generic_lookup(struct lu_module *module,
 	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 +703,9 @@ generic_lookup(struct lu_module *module,
 	}
 	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 +713,6 @@ generic_lookup(struct lu_module *module,
 	/* 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;
@@ -668,6 +915,13 @@ format_generic(struct lu_ent *ent, const
 		char *field;
 
 		field = format_field(ent, formats + i);
+		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"),
@@ -731,12 +985,9 @@ generic_add(struct lu_module *module, co
 	    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;
-	int fd;
+	struct editing *e;
+	char *line, *contents;
 	ssize_t r;
-	gpointer lock;
 	struct stat st;
 	off_t offset;
 	gboolean ret = FALSE;
@@ -746,54 +997,30 @@ generic_add(struct lu_module *module, co
 	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);
-
 	line = format_generic(ent, formats, format_count, error);
 	if (line == NULL)
-		goto err_filename;
-
-	if (!lu_util_fscreate_save(&fscreate, error))
-		goto err_line;
-	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;
-	}
+		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;
 	}
 
 	/* 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. */
 	contents = g_malloc0(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, strerror(errno));
+			     e->new_filename, strerror(errno));
 		goto err_contents;
 	}
 
@@ -805,54 +1032,43 @@ generic_add(struct lu_module *module, co
 		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));
+			     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));
+				     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_contents;
 	}
-	/* Hey, it succeeded. */
 	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_editing:
+	ret = editing_close(e, ret, ret, error); /* Commit/rollback happens here. */
 err_line:
 	g_free(line);
-err_filename:
-	g_free(filename);
+err:
 	return ret;
 }
 
@@ -961,12 +1177,10 @@ generic_mod(struct lu_module *module, co
 	    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, *new_line, *contents, *line, *rest;
+	struct editing *e;
+	char *new_line, *contents, *line, *rest;
 	char *current_name, *fragment;
-	int fd;
-	gpointer lock;
-	const char *dir, *name_attribute;
+	const char *name_attribute;
 	GValueArray *names;
 	gboolean ret = FALSE;
 	struct stat st;
@@ -994,47 +1208,24 @@ generic_mod(struct lu_module *module, co
 		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_filename;
+		return FALSE;
 
-	if (!lu_util_fscreate_save(&fscreate, error))
+	e = editing_open(module, file_suffix, error);
+	if (e == NULL)
 		goto err_new_line;
-	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;
 
-	/* 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;
-
-	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, strerror(errno));
-		goto err_lock;
+			     e->new_filename, strerror(errno));
+		goto err_editing;
 	}
 
 	contents = g_malloc(st.st_size + 1 + strlen(new_line));
-	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;
 	}
@@ -1075,16 +1266,16 @@ generic_mod(struct lu_module *module, co
 	memmove(line + strlen(new_line), rest,
 		contents + st.st_size + 1 - rest);
 	memcpy(line, new_line, strlen(new_line));
-	if (lseek(fd, line - contents, SEEK_SET) == -1) {
+	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(fd, line, len) != len) {
+	if ((size_t)write(e->new_fd, line, len) != len) {
 		lu_error_new(error, lu_error_write, NULL);
 		goto err_contents;
 	}
-	if (ftruncate(fd, (line - contents) + len) != 0) {
+	if (ftruncate(e->new_fd, (line - contents) + len) != 0) {
 		lu_error_new(error, lu_error_write, NULL);
 		goto err_contents;
 	}
@@ -1093,16 +1284,10 @@ generic_mod(struct lu_module *module, co
 
 err_contents:
 	g_free(contents);
-err_lock:
-	lu_util_lock_free(lock);
-err_fd:
-	close(fd);
-err_fscreate:
-	lu_util_fscreate_restore(fscreate);
+err_editing:
+	ret = editing_close(e, ret, ret, error); /* Commit/rollback happens here. */
 err_new_line:
 	g_free(new_line);
-err_filename:
-	g_free(filename);
 	return ret;
 }
 
@@ -1147,18 +1332,15 @@ 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;
+	struct editing *e;
 	GValueArray *name = NULL;
 	GValue *value;
-	char *contents, *filename, *key;
+	char *contents;
 	char *fragment1, *fragment2;
-	const char *dir;
 	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)
@@ -1172,46 +1354,23 @@ generic_del(struct lu_module *module, co
 	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;
 
 	/* 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;
 	}
@@ -1268,41 +1427,38 @@ generic_del(struct lu_module *module, co
 
 	/* 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:
 	return ret;
 }
 
@@ -1382,15 +1538,11 @@ 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;
+	struct editing *e;
 	GValueArray *name = NULL;
 	GValue *val;
-	char *filename, *key;
-	const char *dir;
 	char *value, *new_value, *namestring;
-	int fd;
-	gpointer lock;
-	gboolean ret = FALSE;
+	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));
@@ -1403,39 +1555,16 @@ generic_lock(struct lu_module *module, c
 	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;
+	e = editing_open(module, file_suffix, error);
+	if (e == NULL)
+		goto err;
 
 	/* Generate a string representation of the name. */
 	val = g_value_array_get_nth(name, 0);
 	namestring = lu_value_strdup(val);
 
 	/* Read the old value from the file. */
-	value = lu_util_field_read(fd, namestring, field, error);
+	value = lu_util_field_read(e->new_fd, namestring, field, error);
 	if (value == NULL)
 		goto err_namestring;
 
@@ -1455,18 +1584,18 @@ generic_lock(struct lu_module *module, c
 		goto err_namestring;
 
 	/* Make the change. */
-	ret = lu_util_field_write(fd, namestring, field, new_value, error);
+	if (lu_util_field_write(e->new_fd, namestring, field, new_value, error)
+	    == FALSE)
+		goto err_namestring;
+	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);
+	/* Commit/rollback happens here. */
+	ret = editing_close(e, commit, ret, error);
+err:
 	return ret;
 }
 
@@ -1481,7 +1610,6 @@ generic_is_locked(struct lu_module *modu
 	const char *dir;
 	char *value, *namestring;
 	int fd;
-	gpointer lock;
 	gboolean ret;
 
 	/* Get the name of this account. */
@@ -1511,13 +1639,6 @@ generic_is_locked(struct lu_module *modu
 		return FALSE;
 	}
 
-	/* Lock the file. */
-	if ((lock = lu_util_lock_obtain(fd, error)) == NULL) {
-		close(fd);
-		g_free(filename);
-		return FALSE;
-	}
-
 	/* Construct the actual name of the account holder(s). */
 	val = g_value_array_get_nth(name, 0);
 	namestring = lu_value_strdup(val);
@@ -1526,7 +1647,6 @@ generic_is_locked(struct lu_module *modu
 	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;
@@ -1536,7 +1656,6 @@ generic_is_locked(struct lu_module *modu
 	ret = value[0] == '!';
 	g_free(value);
 
-	lu_util_lock_free(lock);
 	close(fd);
 	g_free(filename);
 	return ret;
@@ -1687,13 +1806,10 @@ generic_setpass(struct lu_module *module
 		struct lu_ent *ent, const char *password, gboolean is_shadow,
 		struct lu_error **error)
 {
-	lu_security_context_t fscreate;
+	struct editing *e;
 	GValueArray *name = NULL;
 	GValue *val;
-	char *filename, *key, *value, *namestring;
-	const char *dir;
-	int fd;
-	gpointer lock;
+	char *value, *namestring;
 	gboolean ret = FALSE;
 
 	/* Get the name of this account. */
@@ -1707,40 +1823,16 @@ generic_setpass(struct lu_module *module
 	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;
+	e = editing_open(module, file_suffix, error);
+	if (e == NULL)
+		goto err;
 
 	/* Get the name of the account. */
 	val = g_value_array_get_nth(name, 0);
 	namestring = lu_value_strdup(val);
 
 	/* Read the current contents of the field. */
-	value = lu_util_field_read(fd, namestring, field, error);
+	value = lu_util_field_read(e->new_fd, namestring, field, error);
 	if (value == NULL)
 		goto err_namestring;
 
@@ -1767,9 +1859,9 @@ generic_setpass(struct lu_module *module
 	else if (g_ascii_strncasecmp(password, LU_CRYPTED, strlen(LU_CRYPTED))
 		 == 0) {
 		password = password + strlen(LU_CRYPTED);
-		if (strchr(password, ':') != NULL) {
+		if (strpbrk(password, ":\n") != NULL) {
 			lu_error_new(error, lu_error_invalid_attribute_value,
-				     _("`:' not allowed in encrypted "
+				     _("`:' and `\\n' not allowed in encrypted "
 				       "password"));
 			goto err_value;
 		}
@@ -1787,20 +1879,15 @@ generic_setpass(struct lu_module *module
 	}
 
 	/* 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, namestring, field, password, error);
 	/* Fall through */
 
  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);
+	ret = editing_close(e, ret, ret, error); /* Commit/rollback happens here. */
+err:
 	return ret;
 }
 
@@ -1876,7 +1963,6 @@ lu_files_enumerate(struct lu_module *mod
 		   const char *pattern, struct lu_error **error)
 {
 	int fd;
-	gpointer lock;
 	GValueArray *ret;
 	GValue value;
 	char *buf;
@@ -1903,20 +1989,12 @@ lu_files_enumerate(struct lu_module *mod
 		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;
@@ -1952,7 +2030,6 @@ lu_files_enumerate(struct lu_module *mod
 
 	/* Clean up. */
 	g_value_unset(&value);
-	lu_util_lock_free(lock);
 	fclose(fp);
 	g_free(filename);
 
@@ -1981,7 +2058,6 @@ lu_files_users_enumerate_by_group(struct
 				  struct lu_error **error)
 {
 	int fd;
-	gpointer lock;
 	GValueArray *ret;
 	GValue value;
 	char *buf, grp[CHUNK_SIZE];
@@ -2010,21 +2086,12 @@ lu_files_users_enumerate_by_group(struct
 		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);
@@ -2083,7 +2150,6 @@ lu_files_users_enumerate_by_group(struct
 	}
 	/* Close the file. */
 	g_value_unset(&value);
-	lu_util_lock_free(lock);
 	fclose(fp);
 
 	/* Open the group file. */
@@ -2098,22 +2164,12 @@ lu_files_users_enumerate_by_group(struct
 		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);
@@ -2168,7 +2224,6 @@ lu_files_users_enumerate_by_group(struct
 	}
 
 	/* Clean up. */
-	lu_util_lock_free(lock);
 	fclose(fp);
 
 	g_free(pwdfilename);
@@ -2185,7 +2240,6 @@ lu_files_groups_enumerate_by_user(struct
 				  struct lu_error **error)
 {
 	int fd;
-	gpointer lock;
 	GValueArray *ret;
 	GValue value;
 	char *buf;
@@ -2215,21 +2269,12 @@ lu_files_groups_enumerate_by_user(struct
 		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);
@@ -2279,7 +2324,6 @@ lu_files_groups_enumerate_by_user(struct
 		}
 		g_free(buf);
 	}
-	lu_util_lock_free(lock);
 	fclose(fp);
 
 	/* Open the groups file. */
@@ -2293,21 +2337,12 @@ lu_files_groups_enumerate_by_user(struct
 		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);
@@ -2365,7 +2400,6 @@ lu_files_groups_enumerate_by_user(struct
 	}
 	g_value_unset(&value);
 
-	lu_util_lock_free(lock);
 	fclose(fp);
 	g_free(pwdfilename);
 	g_free(grpfilename);
@@ -2381,7 +2415,6 @@ lu_files_enumerate_full(struct lu_module
 			struct lu_error **error)
 {
 	int fd;
-	gpointer lock;
 	GPtrArray *ret = NULL;
 	char *buf;
 	char *key, *filename;
@@ -2406,19 +2439,12 @@ lu_files_enumerate_full(struct lu_module
 		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;
 	}
@@ -2453,7 +2479,6 @@ lu_files_enumerate_full(struct lu_module
 		g_free(key);
 	}
 
-	lu_util_lock_free(lock);
 	fclose(fp);
 
  err_filename:
diff -up libuser-0.56.13/tests/files_test.py.CVE-2015-3246 libuser-0.56.13/tests/files_test.py
--- libuser-0.56.13/tests/files_test.py.CVE-2015-3246	2015-07-08 14:02:50.632242475 +0200
+++ libuser-0.56.13/tests/files_test.py	2015-07-08 14:02:50.643242139 +0200
@@ -262,6 +262,21 @@ class Tests(unittest.TestCase):
         e = self.a.initUser('user6_8')
         self.assertRaises(RuntimeError, self.a.addUser, e, False, 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)
+
     def testUserMod1(self):
         # A minimal case
         e = self.a.initUser('user7_1')
@@ -421,6 +436,26 @@ class Tests(unittest.TestCase):
         e[libuser.USERNAME] = 'user7_7'
         self.assertRaises(RuntimeError, self.a.modifyUser, e, False)
 
+    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.assert_(e)
+            self.assert_('\n' not in 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)
@@ -614,6 +649,22 @@ class Tests(unittest.TestCase):
         crypted = crypt.crypt('a:b', e[libuser.SHADOWPASSWORD][0])
         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.assert_('\n' not in 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.assert_('\n' not in e[libuser.SHADOWPASSWORD][0])
+        crypted = crypt.crypt('a\nb', e[libuser.SHADOWPASSWORD][0])
+        self.assertEqual(e[libuser.SHADOWPASSWORD], [crypted])
+
     def testUserRemovepass(self):
         e = self.a.initUser('user13_1')
         e[libuser.SHADOWPASSWORD] = '03dgZm5nZvqOc'
@@ -837,6 +888,20 @@ class Tests(unittest.TestCase):
         e = self.a.initGroup('group21_5')
         self.assertRaises(RuntimeError, self.a.addGroup, e)
 
+    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 testGroupMod1(self):
         # A minimal case
         e = self.a.initGroup('group22_1')
@@ -950,6 +1015,25 @@ class Tests(unittest.TestCase):
         e[libuser.GROUPNAME] = 'group22_6'
         self.assertRaises(RuntimeError, self.a.modifyGroup, e)
 
+    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.assert_(e)
+            if e.has_key(field):
+                self.assert_('\n' not in str(e[field][0]))
+                e[field] = str(e[field][0]) + '\nx'
+            else:
+                e[field] = field + '\nx'
+            self.assertRaises(RuntimeError, self.a.modifyGroup, e)
+
     def testGroupDel(self):
         e = self.a.initGroup('group23_1')
         self.a.addGroup(e)
@@ -1138,6 +1222,22 @@ class Tests(unittest.TestCase):
         crypted = crypt.crypt('a:b', e[libuser.SHADOWPASSWORD][0])
         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.assert_('\n' not in 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.assert_('\n' not in e[libuser.SHADOWPASSWORD][0])
+        crypted = crypt.crypt('a\nb', e[libuser.SHADOWPASSWORD][0])
+        self.assertEqual(e[libuser.SHADOWPASSWORD], [crypted])
+
     def testGroupRemovepass(self):
         e = self.a.initGroup('group28_1')
         e[libuser.SHADOWPASSWORD] = '07Js7N.eEhbgs'
openSUSE Build Service is sponsored by