cfe9be import libuser-0.60-7.el7_1

Authored and Committed by centosrcm 9 years ago
    import libuser-0.60-7.el7_1
    
        
SOURCES/libuser-CVE-2015-3246.patch ADDED
@@ -0,0 +1,1530 @@
1
+ 2015-06-26 Miloslav Trmač <mitr@redhat.com>
2
+
3
+ * modules/files.c (open_and_copy_file): Replace and heavily modify ...
4
+ (lu_files_create_backup): ... this.
5
+ (lock_file_handle_existing, lock_file_create, lock_file_remove)
6
+ (struct editing, editing_open, replace_file_or_symlink)
7
+ (editing_close): New functions.
8
+ (generic_lookup, generic_is_locked, lu_files_enumerate)
9
+ (lu_files_users_enumerate_by_group, lu_files_groups_enumerate_by_user)
10
+ (lu_files_enumerate_full): Remove locking on read-only operations.
11
+ (generic_add, generic_mod, generic_del, generic_lock)
12
+ (generic_setpass): Use struct editing instead of dealing with locking,
13
+ backups, SELinux individually.
14
+
15
+ * lib/user_private.h (lu_util_lock_obtain, lu_util_lock_free): Mark
16
+ as deprecated.
17
+
18
+ * lib/util.c (lu_util_field_write): Fail on an incomplete write().
19
+
20
+ 2015-06-25 Miloslav Trmač <mitr@redhat.com>
21
+
22
+ * modules/files.c (format_generic, generic_setpass): Refuse to write
23
+ field values which contain \n.
24
+ * tests/files_test.py (Tests.testUserAdd9, Tests.testUserMod8)
25
+ (tests.testUserSetpass5, tests.testGroupAdd6, tests.testGroupMod7)
26
+ (tests.testGroupSetpass4): New tests.
27
+
28
+ diff -up libuser-0.60/lib/user_private.h.CVE-2015-3246 libuser-0.60/lib/user_private.h
29
+ --- libuser-0.60/lib/user_private.h.CVE-2015-3246 2013-10-12 23:56:07.000000000 +0200
30
+ +++ libuser-0.60/lib/user_private.h 2015-07-08 15:15:14.060544103 +0200
31
+ @@ -330,9 +330,11 @@ typedef char lu_security_context_t; /* "
32
+ ((void)(PATH), (void)(MODE), (void)(ERROR), TRUE)
33
+ #endif
34
+
35
+ -/* Lock a file. */
36
+ +#ifndef LU_DISABLE_DEPRECATED
37
+ +/* Lock a file. Deprecated. */
38
+ gpointer lu_util_lock_obtain(int fd, struct lu_error **error);
39
+ void lu_util_lock_free(gpointer lock);
40
+ +#endif
41
+
42
+ /* Manipulate a colon-delimited flat text file. */
43
+ char *lu_util_line_get_matching1(int fd, const char *firstpart,
44
+ diff -up libuser-0.60/lib/util.c.CVE-2015-3246 libuser-0.60/lib/util.c
45
+ --- libuser-0.60/lib/util.c.CVE-2015-3246 2013-10-12 23:56:07.000000000 +0200
46
+ +++ libuser-0.60/lib/util.c 2015-07-08 15:15:14.060544103 +0200
47
+ @@ -632,7 +632,7 @@ lu_util_field_write(int fd, const char *
48
+ goto err;
49
+ }
50
+ len = strlen(buf);
51
+ - if (write(fd, buf, len) == -1) {
52
+ + if (write(fd, buf, len) != len) {
53
+ lu_error_new(error, lu_error_write, NULL);
54
+ ret = FALSE;
55
+ goto err;
56
+ diff -up libuser-0.60/modules/files.c.CVE-2015-3246 libuser-0.60/modules/files.c
57
+ --- libuser-0.60/modules/files.c.CVE-2015-3246 2013-10-12 23:56:07.000000000 +0200
58
+ +++ libuser-0.60/modules/files.c 2015-07-08 15:16:41.014981429 +0200
59
+ @@ -25,6 +25,7 @@
60
+ #include <fcntl.h>
61
+ #include <fnmatch.h>
62
+ #include <limits.h>
63
+ +#include <shadow.h>
64
+ #include <stdio.h>
65
+ #include <stdlib.h>
66
+ #include <string.h>
67
+ @@ -101,82 +102,79 @@ module_filename(struct lu_module *module
68
+ return g_strconcat(dir, file_suffix, NULL);
69
+ }
70
+
71
+ -/* Create a backup copy of "filename" named "filename-". */
72
+ -static gboolean
73
+ -lu_files_create_backup(const char *filename,
74
+ - struct lu_error **error)
75
+ +/* Copy contents of INPUT_FILENAME to OUTPUT_FILENAME, exclusively creating it
76
+ + * if EXCLUSIVE.
77
+ + * Return the file descriptor for OUTPUT_FILENAME, open for reading and writing,
78
+ + * or -1 on error.
79
+ + * Note that this does no locking and assumes the directories hosting the files
80
+ + * are not being manipulated by an attacker. */
81
+ +static int
82
+ +open_and_copy_file(const char *input_filename, const char *output_filename,
83
+ + gboolean exclusive, struct lu_error **error)
84
+ {
85
+ int ifd, ofd;
86
+ - gpointer ilock, olock;
87
+ - char *backupname;
88
+ - struct stat ist, ost;
89
+ - off_t offset;
90
+ - gboolean res = FALSE;
91
+ + struct stat st;
92
+ + int res = -1;
93
+ + int flags;
94
+
95
+ - g_assert(filename != NULL);
96
+ - g_assert(strlen(filename) > 0);
97
+ + g_assert(input_filename != NULL);
98
+ + g_assert(strlen(input_filename) > 0);
99
+ + g_assert(output_filename != NULL);
100
+ + g_assert(strlen(output_filename) > 0);
101
+
102
+ - /* Open the original file. */
103
+ - ifd = open(filename, O_RDONLY);
104
+ + /* Open the input file. */
105
+ + ifd = open(input_filename, O_RDONLY);
106
+ if (ifd == -1) {
107
+ lu_error_new(error, lu_error_open,
108
+ - _("couldn't open `%s': %s"), filename,
109
+ + _("couldn't open `%s': %s"), input_filename,
110
+ strerror(errno));
111
+ goto err;
112
+ }
113
+
114
+ - /* Lock the input file. */
115
+ - if ((ilock = lu_util_lock_obtain(ifd, error)) == NULL)
116
+ - goto err_ifd;
117
+ -
118
+ /* Read the input file's size. */
119
+ - if (fstat(ifd, &ist) == -1) {
120
+ + if (fstat(ifd, &st) == -1) {
121
+ lu_error_new(error, lu_error_stat,
122
+ - _("couldn't stat `%s': %s"), filename,
123
+ + _("couldn't stat `%s': %s"), input_filename,
124
+ strerror(errno));
125
+ - goto err_ilock;
126
+ + goto err_ifd;
127
+ }
128
+
129
+ - /* Generate the backup file's name and open it, creating it if it
130
+ - * doesn't already exist. */
131
+ - backupname = g_strconcat(filename, "-", NULL);
132
+ - ofd = open(backupname, O_WRONLY | O_CREAT, ist.st_mode);
133
+ + /* We only need O_WRONLY, but the caller needs RDWR if ofd will be
134
+ + * used as e->new_fd. */
135
+ + flags = O_RDWR | O_CREAT;
136
+ + if (exclusive) {
137
+ + /* This ensures that if there is a concurrent writer which is
138
+ + * not doing locking for some reason, we will not truncate their
139
+ + * temporary file. Still, the other writer may truncate our
140
+ + * file, and ultimately the rename() committing the changes will
141
+ + * lose one or the other set of changes. */
142
+ + (void)unlink(output_filename);
143
+ + flags |= O_EXCL;
144
+ + } else
145
+ + flags |= O_TRUNC;
146
+ + /* Start with absolutely restrictive permissions to make sure nobody
147
+ + * can get a file descriptor for this file until we are done resetting
148
+ + * ownership. */
149
+ + ofd = open(output_filename, flags, 0);
150
+ if (ofd == -1) {
151
+ lu_error_new(error, lu_error_open,
152
+ - _("error creating `%s': %s"), backupname,
153
+ + _("error creating `%s': %s"), output_filename,
154
+ strerror(errno));
155
+ - goto err_backupname;
156
+ - }
157
+ -
158
+ - /* If we can't read its type, or it's not a normal file, bail. */
159
+ - if (fstat(ofd, &ost) == -1) {
160
+ - lu_error_new(error, lu_error_stat, _("couldn't stat `%s': %s"),
161
+ - backupname, strerror(errno));
162
+ - goto err_ofd;
163
+ - }
164
+ - if (!S_ISREG(ost.st_mode)) {
165
+ - lu_error_new(error, lu_error_open,
166
+ - _("backup file `%s' exists and is not a regular file"),
167
+ - backupname);
168
+ - goto err_ofd;
169
+ + goto err_ifd;
170
+ }
171
+
172
+ - /* Now lock the output file. */
173
+ - if ((olock = lu_util_lock_obtain(ofd, error)) == NULL)
174
+ - goto err_ofd;
175
+ -
176
+ /* Set the permissions on the new file to match the old one. */
177
+ - if (fchown(ofd, ist.st_uid, ist.st_gid) == -1 && errno != EPERM) {
178
+ + if (fchown(ofd, st.st_uid, st.st_gid) == -1 && errno != EPERM) {
179
+ lu_error_new(error, lu_error_generic,
180
+ - _("Error changing owner of `%s': %s"), backupname,
181
+ - strerror(errno));
182
+ - goto err_olock;
183
+ + _("Error changing owner of `%s': %s"),
184
+ + output_filename, strerror(errno));
185
+ + goto err_ofd;
186
+ }
187
+ - if (fchmod(ofd, ist.st_mode) == -1) {
188
+ + if (fchmod(ofd, st.st_mode) == -1) {
189
+ lu_error_new(error, lu_error_generic,
190
+ - _("Error changing mode of `%s': %s"), backupname,
191
+ - strerror(errno));
192
+ - goto err_olock;
193
+ + _("Error changing mode of `%s': %s"),
194
+ + output_filename, strerror(errno));
195
+ + goto err_ofd;
196
+ }
197
+
198
+ /* Copy the data, block by block. */
199
+ @@ -190,9 +188,9 @@ lu_files_create_backup(const char *filen
200
+ if (errno == EINTR)
201
+ continue;
202
+ lu_error_new(error, lu_error_read,
203
+ - _("Error reading `%s': %s"), filename,
204
+ - strerror(errno));
205
+ - goto err_olock;
206
+ + _("Error reading `%s': %s"),
207
+ + input_filename, strerror(errno));
208
+ + goto err_ofd;
209
+ }
210
+ if (left == 0)
211
+ break;
212
+ @@ -206,55 +204,297 @@ lu_files_create_backup(const char *filen
213
+ continue;
214
+ lu_error_new(error, lu_error_write,
215
+ _("Error writing `%s': %s"),
216
+ - backupname, strerror(errno));
217
+ - goto err_olock;
218
+ + output_filename, strerror(errno));
219
+ + goto err_ofd;
220
+ }
221
+ p += out;
222
+ left -= out;
223
+ }
224
+ }
225
+
226
+ - /* Flush data to disk, and truncate at the current offset. This is
227
+ - * necessary if the file existed before we opened it. */
228
+ - fsync(ofd);
229
+ - offset = lseek(ofd, 0, SEEK_CUR);
230
+ - if (offset == -1 || ftruncate(ofd, offset) == -1) {
231
+ - lu_error_new(error, lu_error_generic,
232
+ - _("Error writing `%s': %s"), backupname,
233
+ - strerror(errno));
234
+ - goto err_olock;
235
+ - }
236
+ -
237
+ - /* Re-read data about the output file. */
238
+ - if (fstat(ofd, &ost) == -1) {
239
+ - lu_error_new(error, lu_error_stat,
240
+ - _("couldn't stat `%s': %s"), backupname,
241
+ - strerror(errno));
242
+ - goto err_olock;
243
+ - }
244
+ -
245
+ - /* Complain if the files are somehow not the same. */
246
+ - if (ist.st_size != ost.st_size) {
247
+ - lu_error_new(error, lu_error_generic,
248
+ - _("backup file size mismatch"));
249
+ - goto err_olock;
250
+ + /* Flush data to disk. */
251
+ + if (fsync(ofd) != 0 || lseek(ofd, 0, SEEK_SET) == -1) {
252
+ + lu_error_new(error, lu_error_write, _("Error writing `%s': %s"),
253
+ + output_filename, strerror(errno));
254
+ + goto err_ofd;
255
+ }
256
+ - res = TRUE;
257
+ + res = ofd;
258
+ + goto err_ifd; /* Do not close ofd */
259
+
260
+ - err_olock:
261
+ - lu_util_lock_free(olock);
262
+ err_ofd:
263
+ close(ofd);
264
+ - err_backupname:
265
+ - g_free(backupname);
266
+ - err_ilock:
267
+ - lu_util_lock_free(ilock);
268
+ err_ifd:
269
+ close(ifd);
270
+ err:
271
+ return res;
272
+ }
273
+
274
+ +/* Deal with an existing LOCK_FILENAME.
275
+ + * Return TRUE if the caller should try again. */
276
+ +static gboolean
277
+ +lock_file_handle_existing(const char *lock_filename, struct lu_error **error)
278
+ +{
279
+ + gchar *lock_contents;
280
+ + GError *gerror;
281
+ + gboolean ret = FALSE;
282
+ + uintmax_t pid;
283
+ + char *p;
284
+ +
285
+ + gerror = NULL;
286
+ + if (g_file_get_contents(lock_filename, &lock_contents, NULL, &gerror)
287
+ + == FALSE) {
288
+ + lu_error_new(error, lu_error_read,
289
+ + _("couldn't read from `%s': %s"), lock_filename,
290
+ + gerror->message);
291
+ + g_error_free(gerror);
292
+ + goto err;
293
+ + }
294
+ + errno = 0;
295
+ + pid = strtoumax(lock_contents, &p, 10);
296
+ + if (errno != 0 || *p != 0 || p == lock_contents || (pid_t)pid != pid) {
297
+ + lu_error_new(error, lu_error_lock,
298
+ + _("Invalid contents of lock `%s'"), lock_filename);
299
+ + goto err_lock_contents;
300
+ + }
301
+ + if (kill(pid, 0) == 0 || errno != ESRCH) {
302
+ + lu_error_new(error, lu_error_lock,
303
+ + _("The lock %s is held by process %ju"),
304
+ + lock_filename, pid);
305
+ + goto err_lock_contents;
306
+ + }
307
+ + /* This is unfixably racy, but that should matter only if a genuine
308
+ + * lock owner crashes. */
309
+ + if (unlink(lock_filename) != 0) {
310
+ + lu_error_new(error, lu_error_lock,
311
+ + _("Error removing stale lock `%s': %s"), lock_filename,
312
+ + strerror(errno));
313
+ + goto err_lock_contents;
314
+ + }
315
+ + ret = TRUE;
316
+ + /* Fall through */
317
+ +
318
+ +err_lock_contents:
319
+ + g_free(lock_contents);
320
+ +err:
321
+ + return ret;
322
+ +}
323
+ +
324
+ +/* Create a lock file for FILENAME. */
325
+ +static gboolean
326
+ +lock_file_create(const char *filename, struct lu_error **error)
327
+ +{
328
+ + char *lock_filename, *tmp_filename;
329
+ + char pid_string[sizeof (pid_t) * CHAR_BIT + 1];
330
+ + int fd;
331
+ + gboolean ret = FALSE;
332
+ +
333
+ + lock_filename = g_strconcat(filename, ".lock", NULL);
334
+ + tmp_filename = g_strdup_printf("%s.lock.XXXXXX", filename);
335
+ +
336
+ + fd = mkstemp(tmp_filename);
337
+ + if (fd == -1) {
338
+ + lu_error_new(error, lu_error_open,
339
+ + _("error opening temporary file for `%s': %s"),
340
+ + lock_filename, strerror(errno));
341
+ + goto err_tmp_filename;
342
+ + }
343
+ + if (snprintf(pid_string, sizeof(pid_string), "%ju", (uintmax_t)getpid())
344
+ + >= sizeof(pid_string))
345
+ + g_assert_not_reached();
346
+ + if (write(fd, pid_string, strlen(pid_string)) != strlen(pid_string)) {
347
+ + lu_error_new(error, lu_error_write, _("Error writing `%s': %s"),
348
+ + tmp_filename, strerror(errno));
349
+ + close(fd);
350
+ + goto err_tmp_file;
351
+ + }
352
+ + close(fd);
353
+ +
354
+ + if (link(tmp_filename, lock_filename) != 0) {
355
+ + if (errno == EEXIST) {
356
+ + if (lock_file_handle_existing(lock_filename, error)
357
+ + == FALSE)
358
+ + goto err_tmp_file;
359
+ + if (link(tmp_filename, lock_filename) == 0)
360
+ + goto got_link;
361
+ + }
362
+ + lu_error_new(error, lu_error_lock,
363
+ + _("Cannot obtain lock `%s': %s"), lock_filename,
364
+ + strerror(errno));
365
+ + goto err_tmp_file;
366
+ + }
367
+ +got_link:
368
+ + ret = TRUE;
369
+ + /* Fall through */
370
+ +
371
+ +err_tmp_file:
372
+ + (void)unlink(tmp_filename);
373
+ +err_tmp_filename:
374
+ + g_free(tmp_filename);
375
+ + g_free(lock_filename);
376
+ + return ret;
377
+ +}
378
+ +
379
+ +/* Remove the lock file for FILENAME. */
380
+ +static void
381
+ +lock_file_remove(const char *filename)
382
+ +{
383
+ + char *lock_file;
384
+ +
385
+ + lock_file = g_strconcat(filename, ".lock", NULL);
386
+ + (void)unlink(lock_file);
387
+ + g_free(lock_file);
388
+ +}
389
+ +
390
+ +/* State related to a file currently open for editing. */
391
+ +struct editing {
392
+ + char *filename;
393
+ + lu_security_context_t fscreate;
394
+ + char *new_filename;
395
+ + int new_fd;
396
+ +};
397
+ +
398
+ +/* Open and lock FILE_SUFFIX in MODULE for editing.
399
+ + * Return editing state, or NULL on error. */
400
+ +static struct editing *
401
+ +editing_open(struct lu_module *module, const char *file_suffix,
402
+ + struct lu_error **error)
403
+ +{
404
+ + struct editing *e;
405
+ + char *backup_name;
406
+ + int fd;
407
+ +
408
+ + e = g_malloc0(sizeof (*e));
409
+ + e->filename = module_filename(module, file_suffix);
410
+ + /* Make sure this all works if e->filename is a symbolic link, at least
411
+ + * as long as it points to the same file system. */
412
+ +
413
+ + if (geteuid() == 0) {
414
+ + if (lckpwdf() != 0) {
415
+ + lu_error_new(error, lu_error_lock,
416
+ + _("error locking file: %s"),
417
+ + strerror(errno));
418
+ + goto err_filename;
419
+ + }
420
+ + }
421
+ + if (lock_file_create(e->filename, error) == FALSE)
422
+ + goto err_lckpwdf;
423
+ +
424
+ + if (!lu_util_fscreate_save(&e->fscreate, error))
425
+ + goto err_locked;
426
+ + if (!lu_util_fscreate_from_file(e->filename, error))
427
+ + goto err_fscreate;
428
+ +
429
+ + backup_name = g_strconcat(e->filename, "-", NULL);
430
+ + fd = open_and_copy_file(e->filename, backup_name, FALSE, error);
431
+ + g_free (backup_name);
432
+ + close(fd);
433
+ + if (fd == -1)
434
+ + goto err_fscreate;
435
+ +
436
+ + e->new_filename = g_strconcat(e->filename, "+", NULL);
437
+ + e->new_fd = open_and_copy_file(e->filename, e->new_filename, TRUE,
438
+ + error);
439
+ + if (e->new_fd == -1)
440
+ + goto err_new_filename;
441
+ +
442
+ + return e;
443
+ +
444
+ +err_new_filename:
445
+ + g_free(e->new_filename);
446
+ +err_fscreate:
447
+ + lu_util_fscreate_restore(e->fscreate);
448
+ +
449
+ +err_locked:
450
+ + (void)lock_file_remove(e->filename);
451
+ +err_lckpwdf:
452
+ + if (geteuid() == 0)
453
+ + (void)ulckpwdf();
454
+ +
455
+ +err_filename:
456
+ + g_free(e->filename);
457
+ + g_free(e);
458
+ + return NULL;
459
+ +}
460
+ +
461
+ +
462
+ +/* Replace DESTINATION with SOURCE, even if DESTINATION is a symbolic link. */
463
+ +static gboolean
464
+ +replace_file_or_symlink(const char *source, const char *destination,
465
+ + struct lu_error **error)
466
+ +{
467
+ + struct stat st;
468
+ + char *tmp;
469
+ + gboolean ret = FALSE;
470
+ +
471
+ + tmp = NULL;
472
+ + if (lstat(destination, &st) == 0 && S_ISLNK(st.st_mode)) {
473
+ + tmp = realpath(destination, NULL);
474
+ + if (tmp == NULL) {
475
+ + lu_error_new(error, lu_error_generic,
476
+ + _("Error resolving `%s': %s"), destination,
477
+ + strerror(errno));
478
+ + goto err;
479
+ + }
480
+ + destination = tmp;
481
+ + }
482
+ + if (rename(source, destination) != 0) {
483
+ + lu_error_new(error, lu_error_write,
484
+ + _("Error replacing `%s': %s"), destination,
485
+ + strerror(errno));
486
+ + goto err;
487
+ + }
488
+ + ret = TRUE;
489
+ + /* Fall through */
490
+ +
491
+ +err:
492
+ + free(tmp);
493
+ + return ret;
494
+ +}
495
+ +
496
+ +/* Finish editing E, commit edits if COMMIT.
497
+ + * Return true only if RET_INPUT and everything went OK; suggested usage is
498
+ + * ret = editing_close(e, commit, ret, error); */
499
+ +static gboolean
500
+ +editing_close(struct editing *e, gboolean commit, gboolean ret_input,
501
+ + struct lu_error **error)
502
+ +{
503
+ + gboolean ret = FALSE;
504
+ + gboolean unlink_new_filename = TRUE;
505
+ +
506
+ + g_assert(e != NULL);
507
+ +
508
+ + if (commit && fsync(e->new_fd) != 0) {
509
+ + lu_error_new(error, lu_error_write, _("Error writing `%s': %s"),
510
+ + e->new_filename, strerror(errno));
511
+ + close(e->new_fd);
512
+ + goto err;
513
+ + }
514
+ + close(e->new_fd);
515
+ +
516
+ + if (commit) {
517
+ + if (replace_file_or_symlink(e->new_filename, e->filename,
518
+ + error) == FALSE)
519
+ + goto err;
520
+ + unlink_new_filename = FALSE;
521
+ + }
522
+ + ret = ret_input;
523
+ +
524
+ +err:
525
+ + if (unlink_new_filename)
526
+ + (void)unlink(e->new_filename);
527
+ + g_free(e->new_filename);
528
+ + lu_util_fscreate_restore(e->fscreate);
529
+ +
530
+ + (void)lock_file_remove(e->filename);
531
+ + if (geteuid() == 0)
532
+ + (void)ulckpwdf();
533
+ +
534
+ + g_free(e->filename);
535
+ + g_free(e);
536
+ + return ret;
537
+ +}
538
+ +
539
+ +
540
+ /* Read a line from the file, no matter how long it is, and return it as a
541
+ * newly-allocated string, with the terminator intact. */
542
+ static char *
543
+ @@ -435,7 +675,6 @@ generic_lookup(struct lu_module *module,
544
+ {
545
+ gboolean ret;
546
+ int fd = -1;
547
+ - gpointer lock;
548
+ char *line, *filename;
549
+
550
+ g_assert(module != NULL);
551
+ @@ -446,7 +685,7 @@ generic_lookup(struct lu_module *module,
552
+
553
+ filename = module_filename(module, file_suffix);
554
+
555
+ - /* Open the file and lock it. */
556
+ + /* Open the file. */
557
+ fd = open(filename, O_RDONLY);
558
+ if (fd == -1) {
559
+ lu_error_new(error, lu_error_open,
560
+ @@ -457,15 +696,9 @@ generic_lookup(struct lu_module *module,
561
+ }
562
+ g_free(filename);
563
+
564
+ - if ((lock = lu_util_lock_obtain(fd, error)) == NULL) {
565
+ - close(fd);
566
+ - return FALSE;
567
+ - }
568
+ -
569
+ /* Search for the entry in this file. */
570
+ line = lu_util_line_get_matchingx(fd, name, field, error);
571
+ if (line == NULL) {
572
+ - lu_util_lock_free(lock);
573
+ close(fd);
574
+ return FALSE;
575
+ }
576
+ @@ -473,7 +706,6 @@ generic_lookup(struct lu_module *module,
577
+ /* If we found data, parse it and then free the data. */
578
+ ret = parser(line, ent);
579
+ g_free(line);
580
+ - lu_util_lock_free(lock);
581
+ close(fd);
582
+
583
+ return ret;
584
+ @@ -666,6 +898,13 @@ format_generic(struct lu_ent *ent, const
585
+ char *field;
586
+
587
+ field = format_field(ent, formats + i);
588
+ + if (strchr(field, '\n') != NULL) {
589
+ + lu_error_new(error, lu_error_invalid_attribute_value,
590
+ + _("%s value `%s': `\\n' not allowed"),
591
+ + formats[i].attribute, field);
592
+ + g_free(field);
593
+ + goto err;
594
+ + }
595
+ if (i != format_count - 1 && strchr(field, ':') != NULL) {
596
+ lu_error_new(error, lu_error_invalid_attribute_value,
597
+ _("%s value `%s': `:' not allowed"),
598
+ @@ -729,11 +968,9 @@ generic_add(struct lu_module *module, co
599
+ const struct format_specifier *formats, size_t format_count,
600
+ struct lu_ent *ent, struct lu_error **error)
601
+ {
602
+ - lu_security_context_t fscreate;
603
+ - char *line, *filename, *contents;
604
+ - int fd;
605
+ + struct editing *e;
606
+ + char *line, *contents;
607
+ ssize_t r;
608
+ - gpointer lock;
609
+ struct stat st;
610
+ off_t offset;
611
+ gboolean ret = FALSE;
612
+ @@ -743,50 +980,30 @@ generic_add(struct lu_module *module, co
613
+ g_assert(format_count > 0);
614
+ g_assert(ent != NULL);
615
+
616
+ - filename = module_filename(module, file_suffix);
617
+ -
618
+ line = format_generic(ent, formats, format_count, error);
619
+ if (line == NULL)
620
+ - goto err_filename;
621
+ + goto err;
622
+
623
+ - if (!lu_util_fscreate_save(&fscreate, error))
624
+ + e = editing_open(module, file_suffix, error);
625
+ + if (e == NULL)
626
+ goto err_line;
627
+ - if (!lu_util_fscreate_from_file(filename, error))
628
+ - goto err_fscreate;
629
+ -
630
+ - /* Create a backup copy of the file we're about to modify. */
631
+ - if (lu_files_create_backup(filename, error) == FALSE)
632
+ - goto err_fscreate;
633
+ -
634
+ - /* Open the file. */
635
+ - fd = open(filename, O_RDWR);
636
+ - if (fd == -1) {
637
+ - lu_error_new(error, lu_error_open,
638
+ - _("couldn't open `%s': %s"), filename,
639
+ - strerror(errno));
640
+ - goto err_fscreate;
641
+ - }
642
+ -
643
+ - /* Lock the file. */
644
+ - if ((lock = lu_util_lock_obtain(fd, error)) == NULL)
645
+ - goto err_fd;
646
+
647
+ /* Read the file's size. */
648
+ - if (fstat(fd, &st) == -1) {
649
+ + if (fstat(e->new_fd, &st) == -1) {
650
+ lu_error_new(error, lu_error_stat,
651
+ - _("couldn't stat `%s': %s"), filename,
652
+ + _("couldn't stat `%s': %s"), e->new_filename,
653
+ strerror(errno));
654
+ - goto err_lock;
655
+ + goto err_editing;
656
+ }
657
+
658
+ /* Read the entire file in. There's some room for improvement here,
659
+ * but at least we still have the lock, so it's not going to get
660
+ * funky on us. */
661
+ contents = g_malloc0(st.st_size + 1);
662
+ - if (read(fd, contents, st.st_size) != st.st_size) {
663
+ + if (read(e->new_fd, contents, st.st_size) != st.st_size) {
664
+ lu_error_new(error, lu_error_read,
665
+ _("couldn't read from `%s': %s"),
666
+ - filename, strerror(errno));
667
+ + e->new_filename, strerror(errno));
668
+ goto err_contents;
669
+ }
670
+
671
+ @@ -798,54 +1015,43 @@ generic_add(struct lu_module *module, co
672
+ goto err_contents;
673
+ }
674
+ /* Hooray, we can add this entry at the end of the file. */
675
+ - offset = lseek(fd, 0, SEEK_END);
676
+ + offset = lseek(e->new_fd, 0, SEEK_END);
677
+ if (offset == -1) {
678
+ lu_error_new(error, lu_error_write,
679
+ _("couldn't write to `%s': %s"),
680
+ - filename, strerror(errno));
681
+ + e->new_filename, strerror(errno));
682
+ goto err_contents;
683
+ }
684
+ /* If the last byte in the file isn't a newline, add one, and silently
685
+ * curse people who use text editors (which shall remain unnamed) which
686
+ * allow saving of the file without a final line terminator. */
687
+ if ((st.st_size > 0) && (contents[st.st_size - 1] != '\n')) {
688
+ - if (write(fd, "\n", 1) != 1) {
689
+ + if (write(e->new_fd, "\n", 1) != 1) {
690
+ lu_error_new(error, lu_error_write,
691
+ _("couldn't write to `%s': %s"),
692
+ - filename,
693
+ - strerror(errno));
694
+ + e->new_filename, strerror(errno));
695
+ goto err_contents;
696
+ }
697
+ }
698
+ /* Attempt to write the entire line to the end. */
699
+ - r = write(fd, line, strlen(line));
700
+ + r = write(e->new_fd, line, strlen(line));
701
+ if ((size_t)r != strlen(line)) {
702
+ /* Oh, come on! */
703
+ lu_error_new(error, lu_error_write,
704
+ - _("couldn't write to `%s': %s"),
705
+ - filename,
706
+ + _("couldn't write to `%s': %s"), e->new_filename,
707
+ strerror(errno));
708
+ - /* Truncate off whatever we actually managed to write and
709
+ - * give up. */
710
+ - (void)ftruncate(fd, offset);
711
+ goto err_contents;
712
+ }
713
+ - /* Hey, it succeeded. */
714
+ ret = TRUE;
715
+ /* Fall through */
716
+
717
+ err_contents:
718
+ g_free(contents);
719
+ -err_lock:
720
+ - lu_util_lock_free(lock);
721
+ -err_fd:
722
+ - close(fd);
723
+ -err_fscreate:
724
+ - lu_util_fscreate_restore(fscreate);
725
+ +err_editing:
726
+ + ret = editing_close(e, ret, ret, error); /* Commit/rollback happens here. */
727
+ err_line:
728
+ g_free(line);
729
+ -err_filename:
730
+ - g_free(filename);
731
+ +err:
732
+ return ret;
733
+ }
734
+
735
+ @@ -938,11 +1144,9 @@ generic_mod(struct lu_module *module, co
736
+ const struct format_specifier *formats, size_t format_count,
737
+ struct lu_ent *ent, struct lu_error **error)
738
+ {
739
+ - lu_security_context_t fscreate;
740
+ - char *filename, *new_line, *contents, *line, *rest;
741
+ + struct editing *e;
742
+ + char *new_line, *contents, *line, *rest;
743
+ char *current_name, *fragment;
744
+ - int fd;
745
+ - gpointer lock;
746
+ const char *name_attribute;
747
+ gboolean ret = FALSE;
748
+ struct stat st;
749
+ @@ -971,43 +1175,24 @@ generic_mod(struct lu_module *module, co
750
+ return FALSE;
751
+ }
752
+
753
+ - filename = module_filename(module, file_suffix);
754
+ -
755
+ new_line = format_generic(ent, formats, format_count, error);
756
+ if (new_line == NULL)
757
+ - goto err_filename;
758
+ + goto err_current_name;
759
+
760
+ - if (!lu_util_fscreate_save(&fscreate, error))
761
+ + e = editing_open(module, file_suffix, error);
762
+ + if (e == NULL)
763
+ goto err_new_line;
764
+ - if (!lu_util_fscreate_from_file(filename, error))
765
+ - goto err_fscreate;
766
+ - /* Create a backup file. */
767
+ - if (lu_files_create_backup(filename, error) == FALSE)
768
+ - goto err_fscreate;
769
+
770
+ - /* Open the file to be modified. */
771
+ - fd = open(filename, O_RDWR);
772
+ - if (fd == -1) {
773
+ - lu_error_new(error, lu_error_open,
774
+ - _("couldn't open `%s': %s"), filename,
775
+ - strerror(errno));
776
+ - goto err_fscreate;
777
+ - }
778
+ -
779
+ - /* Lock the file. */
780
+ - if ((lock = lu_util_lock_obtain(fd, error)) == NULL)
781
+ - goto err_fd;
782
+ -
783
+ - if (fstat(fd, &st) == -1) {
784
+ + if (fstat(e->new_fd, &st) == -1) {
785
+ lu_error_new(error, lu_error_stat, _("couldn't stat `%s': %s"),
786
+ - filename, strerror(errno));
787
+ - goto err_lock;
788
+ + e->new_filename, strerror(errno));
789
+ + goto err_editing;
790
+ }
791
+
792
+ contents = g_malloc(st.st_size + 1 + strlen(new_line));
793
+ - if (read(fd, contents, st.st_size) != st.st_size) {
794
+ + if (read(e->new_fd, contents, st.st_size) != st.st_size) {
795
+ lu_error_new(error, lu_error_read,
796
+ - _("couldn't read from `%s': %s"), filename,
797
+ + _("couldn't read from `%s': %s"), e->new_filename,
798
+ strerror(errno));
799
+ goto err_contents;
800
+ }
801
+ @@ -1045,16 +1230,16 @@ generic_mod(struct lu_module *module, co
802
+ memmove(line + strlen(new_line), rest,
803
+ contents + st.st_size + 1 - rest);
804
+ memcpy(line, new_line, strlen(new_line));
805
+ - if (lseek(fd, line - contents, SEEK_SET) == -1) {
806
+ + if (lseek(e->new_fd, line - contents, SEEK_SET) == -1) {
807
+ lu_error_new(error, lu_error_write, NULL);
808
+ goto err_contents;
809
+ }
810
+ len = strlen(line);
811
+ - if ((size_t)write(fd, line, len) != len) {
812
+ + if ((size_t)write(e->new_fd, line, len) != len) {
813
+ lu_error_new(error, lu_error_write, NULL);
814
+ goto err_contents;
815
+ }
816
+ - if (ftruncate(fd, (line - contents) + len) != 0) {
817
+ + if (ftruncate(e->new_fd, (line - contents) + len) != 0) {
818
+ lu_error_new(error, lu_error_write, NULL);
819
+ goto err_contents;
820
+ }
821
+ @@ -1063,16 +1248,11 @@ generic_mod(struct lu_module *module, co
822
+
823
+ err_contents:
824
+ g_free(contents);
825
+ -err_lock:
826
+ - lu_util_lock_free(lock);
827
+ -err_fd:
828
+ - close(fd);
829
+ -err_fscreate:
830
+ - lu_util_fscreate_restore(fscreate);
831
+ +err_editing:
832
+ + ret = editing_close(e, ret, ret, error); /* Commit/rollback happens here. */
833
+ err_new_line:
834
+ g_free(new_line);
835
+ -err_filename:
836
+ - g_free(filename);
837
+ +err_current_name:
838
+ g_free(current_name);
839
+ return ret;
840
+ }
841
+ @@ -1118,16 +1298,14 @@ static gboolean
842
+ generic_del(struct lu_module *module, const char *file_suffix,
843
+ struct lu_ent *ent, struct lu_error **error)
844
+ {
845
+ - lu_security_context_t fscreate;
846
+ + struct editing *e;
847
+ char *name;
848
+ - char *contents, *filename;
849
+ + char *contents;
850
+ char *fragment2;
851
+ struct stat st;
852
+ size_t len;
853
+ - int fd;
854
+ - gboolean ret = FALSE;
855
+ + gboolean commit = FALSE, ret = FALSE;
856
+ gboolean found;
857
+ - gpointer lock;
858
+
859
+ /* Get the entity's current name. */
860
+ if (ent->type == lu_user)
861
+ @@ -1141,42 +1319,23 @@ generic_del(struct lu_module *module, co
862
+ g_assert(module != NULL);
863
+ g_assert(ent != NULL);
864
+
865
+ - filename = module_filename(module, file_suffix);
866
+ -
867
+ - if (!lu_util_fscreate_save(&fscreate, error))
868
+ - goto err_filename;
869
+ - if (!lu_util_fscreate_from_file(filename, error))
870
+ - goto err_fscreate;
871
+ - /* Create a backup of that file. */
872
+ - if (lu_files_create_backup(filename, error) == FALSE)
873
+ - goto err_fscreate;
874
+ -
875
+ - /* Open the file to be modified. */
876
+ - fd = open(filename, O_RDWR);
877
+ - if (fd == -1) {
878
+ - lu_error_new(error, lu_error_open,
879
+ - _("couldn't open `%s': %s"), filename,
880
+ - strerror(errno));
881
+ - goto err_fscreate;
882
+ - }
883
+ -
884
+ - /* Lock the file. */
885
+ - if ((lock = lu_util_lock_obtain(fd, error)) == NULL)
886
+ - goto err_fd;
887
+ + e = editing_open(module, file_suffix, error);
888
+ + if (e == NULL)
889
+ + goto err_name;
890
+
891
+ /* Determine the file's size. */
892
+ - if (fstat(fd, &st) == -1) {
893
+ + if (fstat(e->new_fd, &st) == -1) {
894
+ lu_error_new(error, lu_error_stat,
895
+ - _("couldn't stat `%s': %s"), filename,
896
+ + _("couldn't stat `%s': %s"), e->new_filename,
897
+ strerror(errno));
898
+ - goto err_lock;
899
+ + goto err_editing;
900
+ }
901
+
902
+ /* Allocate space to hold the file and read it all in. */
903
+ contents = g_malloc(st.st_size + 1);
904
+ - if (read(fd, contents, st.st_size) != st.st_size) {
905
+ + if (read(e->new_fd, contents, st.st_size) != st.st_size) {
906
+ lu_error_new(error, lu_error_read,
907
+ - _("couldn't read from `%s': %s"), filename,
908
+ + _("couldn't read from `%s': %s"), e->new_filename,
909
+ strerror(errno));
910
+ goto err_contents;
911
+ }
912
+ @@ -1229,41 +1388,38 @@ generic_del(struct lu_module *module, co
913
+
914
+ /* Otherwise we need to write the new data to the file. Jump back to
915
+ * the beginning of the file. */
916
+ - if (lseek(fd, 0, SEEK_SET) == -1) {
917
+ + if (lseek(e->new_fd, 0, SEEK_SET) == -1) {
918
+ lu_error_new(error, lu_error_write,
919
+ - _("couldn't write to `%s': %s"), filename,
920
+ + _("couldn't write to `%s': %s"), e->new_filename,
921
+ strerror(errno));
922
+ goto err_contents;
923
+ }
924
+
925
+ /* Write the new contents out. */
926
+ - if ((size_t)write(fd, contents, len) != len) {
927
+ + if ((size_t)write(e->new_fd, contents, len) != len) {
928
+ lu_error_new(error, lu_error_write,
929
+ - _("couldn't write to `%s': %s"), filename,
930
+ + _("couldn't write to `%s': %s"), e->new_filename,
931
+ strerror(errno));
932
+ goto err_contents;
933
+ }
934
+
935
+ /* Truncate the file to the new (certainly shorter) length. */
936
+ - if (ftruncate(fd, len) == -1) {
937
+ + if (ftruncate(e->new_fd, len) == -1) {
938
+ lu_error_new(error, lu_error_generic,
939
+ - _("couldn't write to `%s': %s"), filename,
940
+ + _("couldn't write to `%s': %s"), e->new_filename,
941
+ strerror(errno));
942
+ goto err_contents;
943
+ }
944
+ + commit = TRUE;
945
+ ret = TRUE;
946
+ /* Fall through */
947
+
948
+ err_contents:
949
+ g_free(contents);
950
+ - err_lock:
951
+ - lu_util_lock_free(lock);
952
+ - err_fd:
953
+ - close(fd);
954
+ -err_fscreate:
955
+ - lu_util_fscreate_restore(fscreate);
956
+ - err_filename:
957
+ - g_free(filename);
958
+ +err_editing:
959
+ + /* Commit/rollback happens here. */
960
+ + ret = editing_close(e, commit, ret, error);
961
+ +err_name:
962
+ g_free(name);
963
+ return ret;
964
+ }
965
+ @@ -1344,12 +1500,9 @@ static gboolean
966
+ generic_lock(struct lu_module *module, const char *file_suffix, int field,
967
+ struct lu_ent *ent, enum lock_op op, struct lu_error **error)
968
+ {
969
+ - lu_security_context_t fscreate;
970
+ - char *filename;
971
+ + struct editing *e;
972
+ char *value, *new_value, *name;
973
+ - int fd;
974
+ - gpointer lock;
975
+ - gboolean ret = FALSE;
976
+ + gboolean commit = FALSE, ret = FALSE;
977
+
978
+ /* Get the name which keys the entries of interest in the file. */
979
+ g_assert((ent->type == lu_user) || (ent->type == lu_group));
980
+ @@ -1362,33 +1515,14 @@ generic_lock(struct lu_module *module, c
981
+ g_assert(module != NULL);
982
+ g_assert(ent != NULL);
983
+
984
+ - filename = module_filename(module, file_suffix);
985
+ -
986
+ - if (!lu_util_fscreate_save(&fscreate, error))
987
+ - goto err_filename;
988
+ - if (!lu_util_fscreate_from_file(filename, error))
989
+ - goto err_fscreate;
990
+ - /* Create a backup of the file. */
991
+ - if (lu_files_create_backup(filename, error) == FALSE)
992
+ - goto err_fscreate;
993
+ -
994
+ - /* Open the file. */
995
+ - fd = open(filename, O_RDWR);
996
+ - if (fd == -1) {
997
+ - lu_error_new(error, lu_error_open,
998
+ - _("couldn't open `%s': %s"), filename,
999
+ - strerror(errno));
1000
+ - goto err_fscreate;
1001
+ - }
1002
+ -
1003
+ - /* Lock the file. */
1004
+ - if ((lock = lu_util_lock_obtain(fd, error)) == NULL)
1005
+ - goto err_fd;
1006
+ + e = editing_open(module, file_suffix, error);
1007
+ + if (e == NULL)
1008
+ + goto err_name;
1009
+
1010
+ /* Read the old value from the file. */
1011
+ - value = lu_util_field_read(fd, name, field, error);
1012
+ + value = lu_util_field_read(e->new_fd, name, field, error);
1013
+ if (value == NULL)
1014
+ - goto err_lock;
1015
+ + goto err_editing;
1016
+
1017
+ /* Check that we actually care about this. If there's a non-empty,
1018
+ * not locked string in there, but it's too short to be a hash, then
1019
+ @@ -1396,27 +1530,27 @@ generic_lock(struct lu_module *module, c
1020
+ if (LU_CRYPT_INVALID(value)) {
1021
+ g_free(value);
1022
+ ret = TRUE;
1023
+ - goto err_lock;
1024
+ + goto err_editing;
1025
+ }
1026
+
1027
+ /* Generate a new value for the file. */
1028
+ new_value = lock_process(value, op, ent, error);
1029
+ g_free(value);
1030
+ if (new_value == NULL)
1031
+ - goto err_lock;
1032
+ + goto err_editing;
1033
+
1034
+ /* Make the change. */
1035
+ - ret = lu_util_field_write(fd, name, field, new_value, error);
1036
+ + if (lu_util_field_write(e->new_fd, name, field, new_value, error)
1037
+ + == FALSE)
1038
+ + goto err_editing;
1039
+ + commit = TRUE;
1040
+ + ret = TRUE;
1041
+ /* Fall through */
1042
+
1043
+ -err_lock:
1044
+ - lu_util_lock_free(lock);
1045
+ - err_fd:
1046
+ - close(fd);
1047
+ -err_fscreate:
1048
+ - lu_util_fscreate_restore(fscreate);
1049
+ - err_filename:
1050
+ - g_free(filename);
1051
+ +err_editing:
1052
+ + /* Commit/rollback happens here. */
1053
+ + ret = editing_close(e, commit, ret, error);
1054
+ +err_name:
1055
+ g_free(name);
1056
+ return ret;
1057
+ }
1058
+ @@ -1429,7 +1563,6 @@ generic_is_locked(struct lu_module *modu
1059
+ char *filename;
1060
+ char *value, *name;
1061
+ int fd;
1062
+ - gpointer lock;
1063
+ gboolean ret = FALSE;
1064
+
1065
+ /* Get the name of this account. */
1066
+ @@ -1454,22 +1587,16 @@ generic_is_locked(struct lu_module *modu
1067
+ goto err_filename;
1068
+ }
1069
+
1070
+ - /* Lock the file. */
1071
+ - if ((lock = lu_util_lock_obtain(fd, error)) == NULL)
1072
+ - goto err_fd;
1073
+ -
1074
+ /* Read the value. */
1075
+ value = lu_util_field_read(fd, name, field, error);
1076
+ if (value == NULL)
1077
+ - goto err_lock;
1078
+ + goto err_fd;
1079
+
1080
+ /* It all comes down to this. */
1081
+ ret = value[0] == '!';
1082
+ g_free(value);
1083
+ /* Fall through */
1084
+
1085
+ -err_lock:
1086
+ - lu_util_lock_free(lock);
1087
+ err_fd:
1088
+ close(fd);
1089
+ err_filename:
1090
+ @@ -1624,10 +1751,8 @@ generic_setpass(struct lu_module *module
1091
+ struct lu_ent *ent, const char *password, gboolean is_shadow,
1092
+ struct lu_error **error)
1093
+ {
1094
+ - lu_security_context_t fscreate;
1095
+ - char *filename, *value, *name;
1096
+ - int fd;
1097
+ - gpointer lock;
1098
+ + struct editing *e;
1099
+ + char *value, *name;
1100
+ gboolean ret = FALSE;
1101
+
1102
+ /* Get the name of this account. */
1103
+ @@ -1641,34 +1766,14 @@ generic_setpass(struct lu_module *module
1104
+ g_assert(module != NULL);
1105
+ g_assert(ent != NULL);
1106
+
1107
+ - filename = module_filename(module, file_suffix);
1108
+ -
1109
+ - if (!lu_util_fscreate_save(&fscreate, error))
1110
+ - goto err_filename;
1111
+ - if (!lu_util_fscreate_from_file(filename, error))
1112
+ - goto err_fscreate;
1113
+ -
1114
+ - /* Create a backup of the file. */
1115
+ - if (lu_files_create_backup(filename, error) == FALSE)
1116
+ - goto err_filename;
1117
+ -
1118
+ - /* Open the file. */
1119
+ - fd = open(filename, O_RDWR);
1120
+ - if (fd == -1) {
1121
+ - lu_error_new(error, lu_error_open,
1122
+ - _("couldn't open `%s': %s"), filename,
1123
+ - strerror(errno));
1124
+ - goto err_fscreate;
1125
+ - }
1126
+ -
1127
+ - /* Lock the file. */
1128
+ - if ((lock = lu_util_lock_obtain(fd, error)) == NULL)
1129
+ - goto err_fd;
1130
+ + e = editing_open(module, file_suffix, error);
1131
+ + if (e == NULL)
1132
+ + goto err_name;
1133
+
1134
+ /* Read the current contents of the field. */
1135
+ - value = lu_util_field_read(fd, name, field, error);
1136
+ + value = lu_util_field_read(e->new_fd, name, field, error);
1137
+ if (value == NULL)
1138
+ - goto err_lock;
1139
+ + goto err_editing;
1140
+
1141
+ /* pam_unix uses shadow passwords only if pw_passwd is "x"
1142
+ (or ##${username}). Make sure to preserve the shadow marker
1143
+ @@ -1693,9 +1798,9 @@ generic_setpass(struct lu_module *module
1144
+ else if (g_ascii_strncasecmp(password, LU_CRYPTED, strlen(LU_CRYPTED))
1145
+ == 0) {
1146
+ password = password + strlen(LU_CRYPTED);
1147
+ - if (strchr(password, ':') != NULL) {
1148
+ + if (strpbrk(password, ":\n") != NULL) {
1149
+ lu_error_new(error, lu_error_invalid_attribute_value,
1150
+ - _("`:' not allowed in encrypted "
1151
+ + _("`:' and `\\n' not allowed in encrypted "
1152
+ "password"));
1153
+ goto err_value;
1154
+ }
1155
+ @@ -1713,19 +1818,14 @@ generic_setpass(struct lu_module *module
1156
+ }
1157
+
1158
+ /* Now write our changes to the file. */
1159
+ - ret = lu_util_field_write(fd, name, field, password, error);
1160
+ + ret = lu_util_field_write(e->new_fd, name, field, password, error);
1161
+ /* Fall through */
1162
+
1163
+ - err_value:
1164
+ +err_value:
1165
+ g_free(value);
1166
+ -err_lock:
1167
+ - lu_util_lock_free(lock);
1168
+ - err_fd:
1169
+ - close(fd);
1170
+ -err_fscreate:
1171
+ - lu_util_fscreate_restore(fscreate);
1172
+ - err_filename:
1173
+ - g_free(filename);
1174
+ +err_editing:
1175
+ + ret = editing_close(e, ret, ret, error); /* Commit/rollback happens here. */
1176
+ +err_name:
1177
+ g_free(name);
1178
+ return ret;
1179
+ }
1180
+ @@ -1802,7 +1902,6 @@ lu_files_enumerate(struct lu_module *mod
1181
+ const char *pattern, struct lu_error **error)
1182
+ {
1183
+ int fd;
1184
+ - gpointer lock;
1185
+ GValueArray *ret;
1186
+ GValue value;
1187
+ char *buf;
1188
+ @@ -1824,20 +1923,12 @@ lu_files_enumerate(struct lu_module *mod
1189
+ return NULL;
1190
+ }
1191
+
1192
+ - /* Lock the file. */
1193
+ - if ((lock = lu_util_lock_obtain(fd, error)) == NULL) {
1194
+ - close(fd);
1195
+ - g_free(filename);
1196
+ - return NULL;
1197
+ - }
1198
+ -
1199
+ /* Wrap the file for stdio operations. */
1200
+ fp = fdopen(fd, "r");
1201
+ if (fp == NULL) {
1202
+ lu_error_new(error, lu_error_open,
1203
+ _("couldn't open `%s': %s"), filename,
1204
+ strerror(errno));
1205
+ - lu_util_lock_free(lock);
1206
+ close(fd);
1207
+ g_free(filename);
1208
+ return NULL;
1209
+ @@ -1873,7 +1964,6 @@ lu_files_enumerate(struct lu_module *mod
1210
+
1211
+ /* Clean up. */
1212
+ g_value_unset(&value);
1213
+ - lu_util_lock_free(lock);
1214
+ fclose(fp);
1215
+ g_free(filename);
1216
+
1217
+ @@ -1902,7 +1992,6 @@ lu_files_users_enumerate_by_group(struct
1218
+ struct lu_error **error)
1219
+ {
1220
+ int fd;
1221
+ - gpointer lock;
1222
+ GValueArray *ret;
1223
+ GValue value;
1224
+ char *buf, grp[CHUNK_SIZE];
1225
+ @@ -1927,21 +2016,12 @@ lu_files_users_enumerate_by_group(struct
1226
+ return NULL;
1227
+ }
1228
+
1229
+ - /* Lock the passwd file. */
1230
+ - if ((lock = lu_util_lock_obtain(fd, error)) == NULL) {
1231
+ - close(fd);
1232
+ - g_free(pwdfilename);
1233
+ - g_free(grpfilename);
1234
+ - return NULL;
1235
+ - }
1236
+ -
1237
+ /* Wrap the descriptor in a stdio FILE. */
1238
+ fp = fdopen(fd, "r");
1239
+ if (fp == NULL) {
1240
+ lu_error_new(error, lu_error_open,
1241
+ _("couldn't open `%s': %s"), pwdfilename,
1242
+ strerror(errno));
1243
+ - lu_util_lock_free(lock);
1244
+ close(fd);
1245
+ g_free(pwdfilename);
1246
+ g_free(grpfilename);
1247
+ @@ -2000,7 +2080,6 @@ lu_files_users_enumerate_by_group(struct
1248
+ }
1249
+ /* Close the file. */
1250
+ g_value_unset(&value);
1251
+ - lu_util_lock_free(lock);
1252
+ fclose(fp);
1253
+
1254
+ /* Open the group file. */
1255
+ @@ -2015,22 +2094,12 @@ lu_files_users_enumerate_by_group(struct
1256
+ return NULL;
1257
+ }
1258
+
1259
+ - /* Lock the group file. */
1260
+ - if ((lock = lu_util_lock_obtain(fd, error)) == NULL) {
1261
+ - close(fd);
1262
+ - g_free(pwdfilename);
1263
+ - g_free(grpfilename);
1264
+ - g_value_array_free(ret);
1265
+ - return NULL;
1266
+ - }
1267
+ -
1268
+ /* Wrap the group file in an stdio file. */
1269
+ fp = fdopen(fd, "r");
1270
+ if (fp == NULL) {
1271
+ lu_error_new(error, lu_error_open,
1272
+ _("couldn't open `%s': %s"), grpfilename,
1273
+ strerror(errno));
1274
+ - lu_util_lock_free(lock);
1275
+ close(fd);
1276
+ g_free(pwdfilename);
1277
+ g_free(grpfilename);
1278
+ @@ -2085,7 +2154,6 @@ lu_files_users_enumerate_by_group(struct
1279
+ }
1280
+
1281
+ /* Clean up. */
1282
+ - lu_util_lock_free(lock);
1283
+ fclose(fp);
1284
+
1285
+ g_free(pwdfilename);
1286
+ @@ -2102,7 +2170,6 @@ lu_files_groups_enumerate_by_user(struct
1287
+ struct lu_error **error)
1288
+ {
1289
+ int fd;
1290
+ - gpointer lock;
1291
+ GValueArray *ret;
1292
+ GValue value;
1293
+ char *buf;
1294
+ @@ -2126,19 +2193,12 @@ lu_files_groups_enumerate_by_user(struct
1295
+ goto err_pwdfilename;
1296
+ }
1297
+
1298
+ - /* Lock it. */
1299
+ - if ((lock = lu_util_lock_obtain(fd, error)) == NULL) {
1300
+ - close(fd);
1301
+ - goto err_pwdfilename;
1302
+ - }
1303
+ -
1304
+ /* Open it so that we can use stdio. */
1305
+ fp = fdopen(fd, "r");
1306
+ if (fp == NULL) {
1307
+ lu_error_new(error, lu_error_open,
1308
+ _("couldn't open `%s': %s"), pwdfilename,
1309
+ strerror(errno));
1310
+ - lu_util_lock_free(lock);
1311
+ close(fd);
1312
+ goto err_pwdfilename;
1313
+ }
1314
+ @@ -2186,7 +2246,6 @@ lu_files_groups_enumerate_by_user(struct
1315
+ }
1316
+ g_free(buf);
1317
+ }
1318
+ - lu_util_lock_free(lock);
1319
+ fclose(fp);
1320
+
1321
+ /* Open the groups file. */
1322
+ @@ -2198,19 +2257,12 @@ lu_files_groups_enumerate_by_user(struct
1323
+ goto err_key;
1324
+ }
1325
+
1326
+ - /* Lock it. */
1327
+ - if ((lock = lu_util_lock_obtain(fd, error)) == NULL) {
1328
+ - close(fd);
1329
+ - goto err_key;
1330
+ - }
1331
+ -
1332
+ /* Open it so that we can use stdio. */
1333
+ fp = fdopen(fd, "r");
1334
+ if (fp == NULL) {
1335
+ lu_error_new(error, lu_error_open,
1336
+ _("couldn't open `%s': %s"), grpfilename,
1337
+ strerror(errno));
1338
+ - lu_util_lock_free(lock);
1339
+ close(fd);
1340
+ goto err_key;
1341
+ }
1342
+ @@ -2267,7 +2319,6 @@ lu_files_groups_enumerate_by_user(struct
1343
+ g_free(key);
1344
+ g_value_unset(&value);
1345
+
1346
+ - lu_util_lock_free(lock);
1347
+ fclose(fp);
1348
+ g_free(pwdfilename);
1349
+ g_free(grpfilename);
1350
+ @@ -2291,7 +2342,6 @@ lu_files_enumerate_full(struct lu_module
1351
+ struct lu_error **error)
1352
+ {
1353
+ int fd;
1354
+ - gpointer lock;
1355
+ GPtrArray *ret = NULL;
1356
+ char *buf;
1357
+ char *key, *filename;
1358
+ @@ -2311,19 +2361,12 @@ lu_files_enumerate_full(struct lu_module
1359
+ goto err_filename;
1360
+ }
1361
+
1362
+ - /* Lock the file. */
1363
+ - if ((lock = lu_util_lock_obtain(fd, error)) == NULL) {
1364
+ - close(fd);
1365
+ - goto err_filename;
1366
+ - }
1367
+ -
1368
+ /* Wrap the file up in stdio. */
1369
+ fp = fdopen(fd, "r");
1370
+ if (fp == NULL) {
1371
+ lu_error_new(error, lu_error_open,
1372
+ _("couldn't open `%s': %s"), filename,
1373
+ strerror(errno));
1374
+ - lu_util_lock_free(lock);
1375
+ close(fd);
1376
+ goto err_filename;
1377
+ }
1378
+ @@ -2358,7 +2401,6 @@ lu_files_enumerate_full(struct lu_module
1379
+ g_free(key);
1380
+ }
1381
+
1382
+ - lu_util_lock_free(lock);
1383
+ fclose(fp);
1384
+
1385
+ err_filename:
1386
+ diff -up libuser-0.60/tests/files_test.py.CVE-2015-3246 libuser-0.60/tests/files_test.py
1387
+ --- libuser-0.60/tests/files_test.py.CVE-2015-3246 2013-10-12 23:56:08.000000000 +0200
1388
+ +++ libuser-0.60/tests/files_test.py 2015-07-08 15:15:14.061544074 +0200
1389
+ @@ -262,6 +262,21 @@ class Tests(unittest.TestCase):
1390
+ e = self.a.initUser('user6_8')
1391
+ self.assertRaises(RuntimeError, self.a.addUser, e, False, False)
1392
+
1393
+ + def testUserAdd9(self):
1394
+ + # '\n' in field values
1395
+ + # libuser.USERPASSWORD not tested because lu_shadow_user_add_prep()
1396
+ + # always replaces it by 'x'. libuser.UIDNUMBER not tested because
1397
+ + # ent_has_name_and_id() interprets the value as a number.
1398
+ + for field in (libuser.USERNAME, libuser.GIDNUMBER, libuser.GECOS,
1399
+ + libuser.HOMEDIRECTORY, libuser.LOGINSHELL,
1400
+ + libuser.SHADOWPASSWORD, libuser.SHADOWLASTCHANGE,
1401
+ + libuser.SHADOWMIN, libuser.SHADOWMAX,
1402
+ + libuser.SHADOWWARNING, libuser.SHADOWINACTIVE,
1403
+ + libuser.SHADOWEXPIRE, libuser.SHADOWFLAG):
1404
+ + e = self.a.initUser('user_6_9' + field)
1405
+ + e[field] = str(e[field][0]) + '\nx'
1406
+ + self.assertRaises(RuntimeError, self.a.addUser, e, False, False)
1407
+ +
1408
+ def testUserMod1(self):
1409
+ # A minimal case
1410
+ e = self.a.initUser('user7_1')
1411
+ @@ -421,6 +436,26 @@ class Tests(unittest.TestCase):
1412
+ e[libuser.USERNAME] = 'user7_7'
1413
+ self.assertRaises(RuntimeError, self.a.modifyUser, e, False)
1414
+
1415
+ + def testUserMod8(self):
1416
+ + # '\n' in field values
1417
+ + # libuser.USERPASSWORD not tested because lu_shadow_user_add_prep()
1418
+ + # always replaces it by 'x'. libuser.UIDNUMBER not tested because
1419
+ + # ent_has_name_and_id() interprets the value as a number.
1420
+ + for field in (libuser.USERNAME, libuser.USERPASSWORD, libuser.GIDNUMBER,
1421
+ + libuser.GECOS, libuser.HOMEDIRECTORY, libuser.LOGINSHELL,
1422
+ + libuser.SHADOWPASSWORD, libuser.SHADOWLASTCHANGE,
1423
+ + libuser.SHADOWMIN, libuser.SHADOWMAX,
1424
+ + libuser.SHADOWWARNING, libuser.SHADOWINACTIVE,
1425
+ + libuser.SHADOWEXPIRE, libuser.SHADOWFLAG):
1426
+ + e = self.a.initUser('user7_9' + field)
1427
+ + self.a.addUser(e, False, False)
1428
+ + del e
1429
+ + e = self.a.lookupUserByName('user7_9' + field)
1430
+ + self.assertIsNotNone(e)
1431
+ + self.assertNotIn('\n', str(e[field][0]))
1432
+ + e[field] = str(e[field][0]) + '\nx'
1433
+ + self.assertRaises(RuntimeError, self.a.modifyUser, e, False)
1434
+ +
1435
+ def testUserDel(self):
1436
+ e = self.a.initUser('user8_1')
1437
+ self.a.addUser(e, False, False)
1438
+ @@ -619,6 +654,22 @@ class Tests(unittest.TestCase):
1439
+ crypted = crypt.crypt('a:b', e[libuser.SHADOWPASSWORD][0])
1440
+ self.assertEqual(e[libuser.SHADOWPASSWORD], [crypted])
1441
+
1442
+ + def testUserSetpass5(self):
1443
+ + # '\n' in field value
1444
+ + e = self.a.initUser('user12_5')
1445
+ + self.a.addUser(e, False, False)
1446
+ + del e
1447
+ + e = self.a.lookupUserByName('user12_5')
1448
+ + self.assertNotIn('\n', e[libuser.SHADOWPASSWORD][0])
1449
+ + self.assertRaises(SystemError, self.a.setpassUser, e, 'a\nb', True)
1450
+ + self.a.setpassUser(e, 'a\nb', False)
1451
+ + del e
1452
+ + e = self.a.lookupUserByName('user12_5')
1453
+ + self.assertEqual(e[libuser.SHADOWPASSWORD][0][:3], '$1$')
1454
+ + self.assertNotIn('\n', e[libuser.SHADOWPASSWORD][0])
1455
+ + crypted = crypt.crypt('a\nb', e[libuser.SHADOWPASSWORD][0])
1456
+ + self.assertEqual(e[libuser.SHADOWPASSWORD], [crypted])
1457
+ +
1458
+ def testUserRemovepass(self):
1459
+ e = self.a.initUser('user13_1')
1460
+ e[libuser.SHADOWPASSWORD] = '03dgZm5nZvqOc'
1461
+ @@ -884,6 +935,20 @@ class Tests(unittest.TestCase):
1462
+ e = self.a.initGroup('group21_5')
1463
+ self.assertRaises(RuntimeError, self.a.addGroup, e)
1464
+
1465
+ + def testGroupAdd6(self):
1466
+ + # '\n' in field values
1467
+ + # libuser.GROUPPASSWORD not tested because lu_shadow_group_add_prep()
1468
+ + # always replaces it by 'x'. libuser.GIDNUMBER not tested because
1469
+ + # ent_has_name_and_id() interprets the value as a number.
1470
+ + for field in (libuser.GROUPNAME, libuser.SHADOWPASSWORD,
1471
+ + libuser.MEMBERNAME, libuser.ADMINISTRATORNAME):
1472
+ + e = self.a.initGroup('group_21_6' + field)
1473
+ + if e.has_key(field):
1474
+ + e[field] = str(e[field][0]) + '\nx'
1475
+ + else:
1476
+ + e[field] = field + '\nx'
1477
+ + self.assertRaises(RuntimeError, self.a.addGroup, e)
1478
+ +
1479
+ def testGroupMod1(self):
1480
+ # A minimal case
1481
+ e = self.a.initGroup('group22_1')
1482
+ @@ -997,6 +1062,25 @@ class Tests(unittest.TestCase):
1483
+ e[libuser.GROUPNAME] = 'group22_6'
1484
+ self.assertRaises(RuntimeError, self.a.modifyGroup, e)
1485
+
1486
+ + def testGroupMod7(self):
1487
+ + # '\n' in field values
1488
+ + # libuser.GIDNUMBER not tested because ent_has_name_and_id() interprets
1489
+ + # the value as a number.
1490
+ + for field in (libuser.GROUPNAME, libuser.GROUPPASSWORD,
1491
+ + libuser.SHADOWPASSWORD, libuser.MEMBERNAME,
1492
+ + libuser.ADMINISTRATORNAME):
1493
+ + e = self.a.initGroup('group22_8' + field)
1494
+ + self.a.addGroup(e)
1495
+ + del e
1496
+ + e = self.a.lookupGroupByName('group22_8' + field)
1497
+ + self.assertIsNotNone(e)
1498
+ + if e.has_key(field):
1499
+ + self.assertNotIn('\n', str(e[field][0]))
1500
+ + e[field] = str(e[field][0]) + '\nx'
1501
+ + else:
1502
+ + e[field] = field + '\nx'
1503
+ + self.assertRaises(RuntimeError, self.a.modifyGroup, e)
1504
+ +
1505
+ def testGroupDel(self):
1506
+ e = self.a.initGroup('group23_1')
1507
+ self.a.addGroup(e)
1508
+ @@ -1190,6 +1274,22 @@ class Tests(unittest.TestCase):
1509
+ crypted = crypt.crypt('a:b', e[libuser.SHADOWPASSWORD][0])
1510
+ self.assertEqual(e[libuser.SHADOWPASSWORD], [crypted])
1511
+
1512
+ + def testGroupSetpass4(self):
1513
+ + # '\n' in field value
1514
+ + e = self.a.initGroup('group27_5')
1515
+ + self.a.addGroup(e)
1516
+ + del e
1517
+ + e = self.a.lookupGroupByName('group27_5')
1518
+ + self.assertNotIn('\n', e[libuser.SHADOWPASSWORD][0])
1519
+ + self.assertRaises(SystemError, self.a.setpassGroup, e, 'a\nb', True)
1520
+ + self.a.setpassGroup(e, 'a\nb', False)
1521
+ + del e
1522
+ + e = self.a.lookupGroupByName('group27_5')
1523
+ + self.assertEqual(e[libuser.SHADOWPASSWORD][0][:3], '$1$')
1524
+ + self.assertNotIn('\n', e[libuser.SHADOWPASSWORD][0])
1525
+ + crypted = crypt.crypt('a\nb', e[libuser.SHADOWPASSWORD][0])
1526
+ + self.assertEqual(e[libuser.SHADOWPASSWORD], [crypted])
1527
+ +
1528
+ def testGroupRemovepass(self):
1529
+ e = self.a.initGroup('group28_1')
1530
+ e[libuser.SHADOWPASSWORD] = '07Js7N.eEhbgs'
file modified
+12 -1
SPECS/libuser.spec CHANGED
@@ -2,11 +2,12 @@
2
2
3
3
Name: libuser
4
4
Version: 0.60
5
- Release: 5%{?dist}
5
+ Release: 7%{?dist}
6
6
Group: System Environment/Base
7
7
License: LGPLv2+
8
8
URL: https://fedorahosted.org/libuser/
9
9
Source: https://fedorahosted.org/releases/l/i/libuser/libuser-%{version}.tar.xz
10
+ Patch0: libuser-CVE-2015-3246.patch
10
11
BuildRequires: glib2-devel, linuxdoc-tools, pam-devel, popt-devel, python2-devel
11
12
BuildRequires: cyrus-sasl-devel, libselinux-devel, openldap-devel
12
13
# To make sure the configure script can find it
@@ -46,6 +47,8 @@ administering user and group accounts.
46
47
%prep
47
48
%setup -q
48
49
50
+ %patch0 -p1 -b .CVE-2015-3246
51
+
49
52
%build
50
53
%configure --with-selinux --with-ldap --with-html-dir=%{_datadir}/gtk-doc/html
51
54
make
@@ -96,6 +99,14 @@ python -c "import libuser"
96
99
%{_datadir}/gtk-doc/html/*
97
100
98
101
%changelog
102
+ * Wed Jul 8 2015 Miloslav Trmač <mitr@redhat.com> - 0.60-7
103
+ - Update CVE-2015-3246 patch based on review comments
104
+ Resolves: #1235519
105
+
106
+ * Fri Jun 26 2015 Miloslav Trmač <mitr@redhat.com> - 0.60-6
107
+ - Fix CVE-2015-3246
108
+ Resolves: #1235519
109
+
99
110
* Fri Jan 24 2014 Daniel Mach <dmach@redhat.com> - 0.60-5
100
111
- Mass rebuild 2014-01-24
101
112