]> granicus.if.org Git - shadow/blob - lib/commonio.c
* src/groupadd.c, lib/commonio.c, lib/groupio.c: Added missing
[shadow] / lib / commonio.c
1 /*
2  * Copyright (c) 1990 - 1994, Julianne Frances Haugh
3  * Copyright (c) 1996 - 2001, Marek Michałkiewicz
4  * Copyright (c) 2001 - 2006, Tomasz Kłoczko
5  * Copyright (c) 2007 - 2009, Nicolas François
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  * 3. The name of the copyright holders or contributors may not be used to
17  *    endorse or promote products derived from this software without
18  *    specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
23  * PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT
24  * HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31  */
32
33 #include <config.h>
34
35 #ident "$Id$"
36
37 #include "defines.h"
38 #include <assert.h>
39 #include <sys/stat.h>
40 #include <stdlib.h>
41 #include <limits.h>
42 #include <utime.h>
43 #include <fcntl.h>
44 #include <errno.h>
45 #include <stdio.h>
46 #include <signal.h>
47 #include "nscd.h"
48 #ifdef WITH_SELINUX
49 #include <selinux/selinux.h>
50 #endif
51 #include "prototypes.h"
52 #include "commonio.h"
53
54 /* local function prototypes */
55 static int lrename (const char *, const char *);
56 static int check_link_count (const char *file);
57 static int do_lock_file (const char *file, const char *lock);
58 static /*@null@*/ /*@dependent@*/FILE *fopen_set_perms (
59         const char *name,
60         const char *mode,
61         const struct stat *sb);
62 static int create_backup (const char *, FILE *);
63 static void free_linked_list (struct commonio_db *);
64 static void add_one_entry (
65         struct commonio_db *db,
66         /*@owned@*/struct commonio_entry *p);
67 static bool name_is_nis (const char *name);
68 static int write_all (const struct commonio_db *);
69 static /*@dependent@*/ /*@null@*/struct commonio_entry *find_entry_by_name (
70         struct commonio_db *,
71         const char *);
72 static /*@dependent@*/ /*@null@*/struct commonio_entry *next_entry_by_name (
73         struct commonio_db *,
74         /*@null@*/struct commonio_entry *pos,
75         const char *);
76
77 static int lock_count = 0;
78 static bool nscd_need_reload = false;
79
80 /*
81  * Simple rename(P) alternative that attempts to rename to symlink
82  * target.
83  */
84 int lrename (const char *old, const char *new)
85 {
86
87         char resolved_path[PATH_MAX];
88         int res;
89
90 #if defined(S_ISLNK)
91         struct stat sb;
92         if (lstat (new, &sb) == 0 && S_ISLNK (sb.st_mode)) {
93                 if (realpath (new, resolved_path) == NULL) {
94                         perror ("realpath in lrename()");
95                 } else {
96                         new = resolved_path;
97                 }
98         }
99 #endif
100         res = rename (old, new);
101         return res;
102 }
103
104 static int check_link_count (const char *file)
105 {
106         struct stat sb;
107
108         if (stat (file, &sb) != 0) {
109                 return 0;
110         }
111
112         if (sb.st_nlink != 2) {
113                 return 0;
114         }
115
116         return 1;
117 }
118
119
120 static int do_lock_file (const char *file, const char *lock)
121 {
122         int fd;
123         pid_t pid;
124         ssize_t len;
125         int retval;
126         char buf[32];
127
128         fd = open (file, O_CREAT | O_EXCL | O_WRONLY, 0600);
129         if (-1 == fd) {
130                 return 0;
131         }
132
133         pid = getpid ();
134         snprintf (buf, sizeof buf, "%lu", (unsigned long) pid);
135         len = (ssize_t) strlen (buf) + 1;
136         if (write (fd, buf, (size_t) len) != len) {
137                 (void) close (fd);
138                 unlink (file);
139                 return 0;
140         }
141         close (fd);
142
143         if (link (file, lock) == 0) {
144                 retval = check_link_count (file);
145                 unlink (file);
146                 return retval;
147         }
148
149         fd = open (lock, O_RDWR);
150         if (-1 == fd) {
151                 unlink (file);
152                 errno = EINVAL;
153                 return 0;
154         }
155         len = read (fd, buf, sizeof (buf) - 1);
156         close (fd);
157         if (len <= 0) {
158                 unlink (file);
159                 errno = EINVAL;
160                 return 0;
161         }
162         buf[len] = '\0';
163         if (get_pid (buf, &pid) == 0) {
164                 unlink (file);
165                 errno = EINVAL;
166                 return 0;
167         }
168         if (kill (pid, 0) == 0) {
169                 unlink (file);
170                 errno = EEXIST;
171                 return 0;
172         }
173         if (unlink (lock) != 0) {
174                 unlink (file);
175                 return 0;
176         }
177
178         retval = 0;
179         if ((link (file, lock) == 0) && (check_link_count (file) != 0)) {
180                 retval = 1;
181         }
182
183         unlink (file);
184         return retval;
185 }
186
187
188 static /*@null@*/ /*@dependent@*/FILE *fopen_set_perms (
189         const char *name,
190         const char *mode,
191         const struct stat *sb)
192 {
193         FILE *fp;
194         mode_t mask;
195
196         mask = umask (0777);
197         fp = fopen (name, mode);
198         (void) umask (mask);
199         if (NULL == fp) {
200                 return NULL;
201         }
202
203 #ifdef HAVE_FCHOWN
204         if (fchown (fileno (fp), sb->st_uid, sb->st_gid) != 0) {
205                 goto fail;
206         }
207 #else
208         if (chown (name, sb->st_mode) != 0) {
209                 goto fail;
210         }
211 #endif
212
213 #ifdef HAVE_FCHMOD
214         if (fchmod (fileno (fp), sb->st_mode & 0664) != 0) {
215                 goto fail;
216         }
217 #else
218         if (chmod (name, sb->st_mode & 0664) != 0) {
219                 goto fail;
220         }
221 #endif
222         return fp;
223
224       fail:
225         fclose (fp);
226         unlink (name);
227         return NULL;
228 }
229
230
231 static int create_backup (const char *backup, FILE * fp)
232 {
233         struct stat sb;
234         struct utimbuf ub;
235         FILE *bkfp;
236         int c;
237         mode_t mask;
238
239         if (fstat (fileno (fp), &sb) != 0) {
240                 return -1;
241         }
242
243         mask = umask (077);
244         bkfp = fopen (backup, "w");
245         (void) umask (mask);
246         if (NULL == bkfp) {
247                 return -1;
248         }
249
250         /* TODO: faster copy, not one-char-at-a-time.  --marekm */
251         c = 0;
252         if (fseek (fp, 0, SEEK_SET) == 0) {
253                 while ((c = getc (fp)) != EOF) {
254                         if (putc (c, bkfp) == EOF) {
255                                 break;
256                         }
257                 }
258         }
259         if ((c != EOF) || (ferror (fp) != 0) || (fflush (bkfp) != 0)) {
260                 fclose (bkfp);
261                 return -1;
262         }
263         if (   (fsync (fileno (bkfp)) != 0)
264             || (fclose (bkfp) != 0)) {
265                 return -1;
266         }
267
268         ub.actime = sb.st_atime;
269         ub.modtime = sb.st_mtime;
270         (void) utime (backup, &ub);
271         return 0;
272 }
273
274
275 static void free_linked_list (struct commonio_db *db)
276 {
277         struct commonio_entry *p;
278
279         while (NULL != db->head) {
280                 p = db->head;
281                 db->head = p->next;
282
283                 if (NULL != p->line) {
284                         free (p->line);
285                 }
286
287                 if (NULL != p->eptr) {
288                         db->ops->free (p->eptr);
289                 }
290
291                 free (p);
292         }
293         db->tail = NULL;
294 }
295
296
297 int commonio_setname (struct commonio_db *db, const char *name)
298 {
299         snprintf (db->filename, sizeof (db->filename), "%s", name);
300         return 1;
301 }
302
303
304 bool commonio_present (const struct commonio_db *db)
305 {
306         return (access (db->filename, F_OK) == 0);
307 }
308
309
310 int commonio_lock_nowait (struct commonio_db *db)
311 {
312         char file[1024];
313         char lock[1024];
314
315         if (db->locked) {
316                 return 1;
317         }
318
319         snprintf (file, sizeof file, "%s.%lu",
320                   db->filename, (unsigned long) getpid ());
321         snprintf (lock, sizeof lock, "%s.lock", db->filename);
322         if (do_lock_file (file, lock) != 0) {
323                 db->locked = true;
324                 lock_count++;
325                 return 1;
326         }
327         return 0;
328 }
329
330
331 int commonio_lock (struct commonio_db *db)
332 {
333 #ifdef HAVE_LCKPWDF
334         /*
335          * only if the system libc has a real lckpwdf() - the one from
336          * lockpw.c calls us and would cause infinite recursion!
337          */
338
339         /*
340          * Call lckpwdf() on the first lock.
341          * If it succeeds, call *_lock() only once
342          * (no retries, it should always succeed).
343          */
344         if (0 == lock_count) {
345                 if (lckpwdf () == -1) {
346                         return 0;       /* failure */
347                 }
348         }
349
350         if (commonio_lock_nowait (db) != 0) {
351                 return 1;       /* success */
352         }
353
354         ulckpwdf ();
355         return 0;               /* failure */
356 #else
357         int i;
358
359         /*
360          * lckpwdf() not used - do it the old way.
361          */
362 #ifndef LOCK_TRIES
363 #define LOCK_TRIES 15
364 #endif
365
366 #ifndef LOCK_SLEEP
367 #define LOCK_SLEEP 1
368 #endif
369         for (i = 0; i < LOCK_TRIES; i++) {
370                 if (i > 0) {
371                         sleep (LOCK_SLEEP);     /* delay between retries */
372                 }
373                 if (commonio_lock_nowait (db) != 0) {
374                         return 1;       /* success */
375                 }
376                 /* no unnecessary retries on "permission denied" errors */
377                 if (geteuid () != 0) {
378                         return 0;
379                 }
380         }
381         return 0;               /* failure */
382 #endif
383 }
384
385 static void dec_lock_count (void)
386 {
387         if (lock_count > 0) {
388                 lock_count--;
389                 if (lock_count == 0) {
390                         /* Tell nscd when lock count goes to zero,
391                            if any of the files were changed.  */
392                         if (nscd_need_reload) {
393                                 nscd_flush_cache ("passwd");
394                                 nscd_flush_cache ("group");
395                                 nscd_need_reload = false;
396                         }
397 #ifdef HAVE_LCKPWDF
398                         ulckpwdf ();
399 #endif
400                 }
401         }
402 }
403
404
405 int commonio_unlock (struct commonio_db *db)
406 {
407         char lock[1024];
408
409         if (db->isopen) {
410                 db->readonly = true;
411                 if (commonio_close (db) == 0) {
412                         if (db->locked) {
413                                 dec_lock_count ();
414                         }
415                         return 0;
416                 }
417         }
418         if (db->locked) {
419                 /*
420                  * Unlock in reverse order: remove the lock file,
421                  * then call ulckpwdf() (if used) on last unlock.
422                  */
423                 db->locked = false;
424                 snprintf (lock, sizeof lock, "%s.lock", db->filename);
425                 unlink (lock);
426                 dec_lock_count ();
427                 return 1;
428         }
429         return 0;
430 }
431
432
433 /*
434  * Add an entry at the end.
435  *
436  * defines p->next, p->prev
437  * (unfortunately, owned special are not supported)
438  */
439 static void add_one_entry (struct commonio_db *db,
440                            /*@owned@*/struct commonio_entry *p)
441 {
442         /*@-mustfreeonly@*/
443         p->next = NULL;
444         p->prev = db->tail;
445         /*@=mustfreeonly@*/
446         if (NULL == db->head) {
447                 db->head = p;
448         }
449         if (NULL != db->tail) {
450                 db->tail->next = p;
451         }
452         db->tail = p;
453 }
454
455
456 static bool name_is_nis (const char *name)
457 {
458         return (('+' == name[0]) || ('-' == name[0]));
459 }
460
461
462 /*
463  * New entries are inserted before the first NIS entry.  Order is preserved
464  * when db is written out.
465  */
466 #ifndef KEEP_NIS_AT_END
467 #define KEEP_NIS_AT_END 1
468 #endif
469
470 #if KEEP_NIS_AT_END
471 static void add_one_entry_nis (struct commonio_db *db,
472                                /*@owned@*/struct commonio_entry *newp);
473
474 /*
475  * Insert an entry between the regular entries, and the NIS entries.
476  *
477  * defines newp->next, newp->prev
478  * (unfortunately, owned special are not supported)
479  */
480 static void add_one_entry_nis (struct commonio_db *db,
481                                /*@owned@*/struct commonio_entry *newp)
482 {
483         struct commonio_entry *p;
484
485         for (p = db->head; NULL != p; p = p->next) {
486                 if (name_is_nis (p->eptr ? db->ops->getname (p->eptr)
487                                          : p->line)) {
488                         /*@-mustfreeonly@*/
489                         newp->next = p;
490                         newp->prev = p->prev;
491                         /*@=mustfreeonly@*/
492                         if (NULL != p->prev) {
493                                 p->prev->next = newp;
494                         } else {
495                                 db->head = newp;
496                         }
497                         p->prev = newp;
498                         return;
499                 }
500         }
501         add_one_entry (db, newp);
502 }
503 #endif                          /* KEEP_NIS_AT_END */
504
505 /* Initial buffer size, as well as increment if not sufficient
506    (for reading very long lines in group files).  */
507 #define BUFLEN 4096
508
509 int commonio_open (struct commonio_db *db, int mode)
510 {
511         char *buf;
512         char *cp;
513         char *line;
514         struct commonio_entry *p;
515         void *eptr = NULL;
516         int flags = mode;
517         size_t buflen;
518         int saved_errno;
519
520         mode &= ~O_CREAT;
521
522         if (   db->isopen
523             || (   (O_RDONLY != mode)
524                 && (O_RDWR != mode))) {
525                 errno = EINVAL;
526                 return 0;
527         }
528         db->readonly = (mode == O_RDONLY);
529         if (!db->readonly && !db->locked) {
530                 errno = EACCES;
531                 return 0;
532         }
533
534         db->head = db->tail = NULL;
535         db->cursor = NULL;
536         db->changed = false;
537
538         db->fp = fopen (db->filename, db->readonly ? "r" : "r+");
539
540         /*
541          * If O_CREAT was specified and the file didn't exist, it will be
542          * created by commonio_close().  We have no entries to read yet.  --marekm
543          */
544         if (NULL == db->fp) {
545                 if (((flags & O_CREAT) != 0) && (ENOENT == errno)) {
546                         db->isopen = true;
547                         return 1;
548                 }
549                 return 0;
550         }
551
552         /* Do not inherit fd in spawned processes (e.g. nscd) */
553         fcntl(fileno(db->fp), F_SETFD, FD_CLOEXEC);
554
555 #ifdef WITH_SELINUX
556         db->scontext = NULL;
557         if ((is_selinux_enabled () > 0) && (!db->readonly)) {
558                 if (fgetfilecon (fileno (db->fp), &db->scontext) < 0) {
559                         goto cleanup_errno;
560                 }
561         }
562 #endif
563
564         buflen = BUFLEN;
565         buf = (char *) malloc (buflen);
566         if (NULL == buf) {
567                 goto cleanup_ENOMEM;
568         }
569
570         while (db->ops->fgets (buf, (int) buflen, db->fp) == buf) {
571                 while (   ((cp = strrchr (buf, '\n')) == NULL)
572                        && (feof (db->fp) == 0)) {
573                         size_t len;
574
575                         buflen += BUFLEN;
576                         cp = (char *) realloc (buf, buflen);
577                         if (NULL == cp) {
578                                 goto cleanup_buf;
579                         }
580                         buf = cp;
581                         len = strlen (buf);
582                         if (db->ops->fgets (buf + len,
583                                             (int) (buflen - len),
584                                             db->fp) == NULL) {
585                                 goto cleanup_buf;
586                         }
587                 }
588                 cp = strrchr (buf, '\n');
589                 if (NULL != cp) {
590                         *cp = '\0';
591                 }
592
593                 line = strdup (buf);
594                 if (NULL == line) {
595                         goto cleanup_buf;
596                 }
597
598                 if (name_is_nis (line)) {
599                         eptr = NULL;
600                 } else {
601                         eptr = db->ops->parse (line);
602                         if (NULL != eptr) {
603                                 eptr = db->ops->dup (eptr);
604                                 if (NULL == eptr) {
605                                         goto cleanup_line;
606                                 }
607                         }
608                 }
609
610                 p = (struct commonio_entry *) malloc (sizeof *p);
611                 if (NULL == p) {
612                         goto cleanup_entry;
613                 }
614
615                 p->eptr = eptr;
616                 p->line = line;
617                 p->changed = false;
618
619                 add_one_entry (db, p);
620         }
621
622         free (buf);
623
624         if (ferror (db->fp) != 0) {
625                 goto cleanup_errno;
626         }
627
628         if ((NULL != db->ops->open_hook) && (db->ops->open_hook () == 0)) {
629                 goto cleanup_errno;
630         }
631
632         db->isopen = true;
633         return 1;
634
635       cleanup_entry:
636         if (NULL != eptr) {
637                 db->ops->free (eptr);
638         }
639       cleanup_line:
640         free (line);
641       cleanup_buf:
642         free (buf);
643       cleanup_ENOMEM:
644         errno = ENOMEM;
645       cleanup_errno:
646         saved_errno = errno;
647         free_linked_list (db);
648 #ifdef WITH_SELINUX
649         if (db->scontext != NULL) {
650                 freecon (db->scontext);
651                 db->scontext = NULL;
652         }
653 #endif
654         fclose (db->fp);
655         db->fp = NULL;
656         errno = saved_errno;
657         return 0;
658 }
659
660 /*
661  * Sort given db according to cmp function (usually compares uids)
662  */
663 int
664 commonio_sort (struct commonio_db *db, int (*cmp) (const void *, const void *))
665 {
666         struct commonio_entry **entries, *ptr;
667         size_t n = 0, i;
668
669         for (ptr = db->head; NULL != ptr; ptr = ptr->next) {
670                 n++;
671         }
672
673         if (n <= 1) {
674                 return 0;
675         }
676
677         entries = malloc (n * sizeof (struct commonio_entry *));
678         if (entries == NULL) {
679                 return -1;
680         }
681
682         n = 0;
683         for (ptr = db->head; NULL != ptr; ptr = ptr->next) {
684                 entries[n++] = ptr;
685         }
686         qsort (entries, n, sizeof (struct commonio_entry *), cmp);
687
688         db->head = entries[0];
689         db->tail = entries[--n];
690         db->head->prev = NULL;
691         db->head->next = entries[1];
692         db->tail->prev = entries[n - 1];
693         db->tail->next = NULL;
694
695         for (i = 1; i < n; i++) {
696                 entries[i]->prev = entries[i - 1];
697                 entries[i]->next = entries[i + 1];
698         }
699
700         free (entries);
701         db->changed = true;
702
703         return 0;
704 }
705
706 /*
707  * Sort entries in db according to order in another.
708  */
709 int commonio_sort_wrt (struct commonio_db *shadow, struct commonio_db *passwd)
710 {
711         struct commonio_entry *head = NULL, *pw_ptr, *spw_ptr;
712         const char *name;
713
714         if ((NULL == shadow) || (NULL == shadow->head)) {
715                 return 0;
716         }
717
718         for (pw_ptr = passwd->head; NULL != pw_ptr; pw_ptr = pw_ptr->next) {
719                 if (NULL == pw_ptr->eptr) {
720                         continue;
721                 }
722                 name = passwd->ops->getname (pw_ptr->eptr);
723                 for (spw_ptr = shadow->head;
724                      NULL != spw_ptr;
725                      spw_ptr = spw_ptr->next) {
726                         if (NULL == spw_ptr->eptr) {
727                                 continue;
728                         }
729                         if (strcmp (name, shadow->ops->getname (spw_ptr->eptr))
730                             == 0) {
731                                 break;
732                         }
733                 }
734                 if (NULL == spw_ptr) {
735                         continue;
736                 }
737                 commonio_del_entry (shadow, spw_ptr);
738                 spw_ptr->next = head;
739                 head = spw_ptr;
740         }
741
742         for (spw_ptr = head; NULL != spw_ptr; spw_ptr = head) {
743                 head = head->next;
744
745                 if (NULL != shadow->head) {
746                         shadow->head->prev = spw_ptr;
747                 }
748                 spw_ptr->next = shadow->head;
749                 shadow->head = spw_ptr;
750         }
751
752         shadow->head->prev = NULL;
753         shadow->changed = true;
754
755         return 0;
756 }
757
758 /*
759  * write_all - Write the database to its file.
760  *
761  * It returns 0 if all the entries could be written correctly.
762  */
763 static int write_all (const struct commonio_db *db)
764         /*@requires notnull db->fp@*/
765 {
766         const struct commonio_entry *p;
767         void *eptr;
768
769         for (p = db->head; NULL != p; p = p->next) {
770                 if (p->changed) {
771                         eptr = p->eptr;
772                         assert (NULL != eptr);
773                         if (db->ops->put (eptr, db->fp) != 0) {
774                                 return -1;
775                         }
776                 } else if (NULL != p->line) {
777                         if (db->ops->fputs (p->line, db->fp) == EOF) {
778                                 return -1;
779                         }
780                         if (putc ('\n', db->fp) == EOF) {
781                                 return -1;
782                         }
783                 }
784         }
785         return 0;
786 }
787
788
789 int commonio_close (struct commonio_db *db)
790         /*@requires notnull db->fp@*/
791 {
792         char buf[1024];
793         int errors = 0;
794         struct stat sb;
795
796 #ifdef WITH_SELINUX
797         /*@null@*/security_context_t old_context = NULL;
798 #endif
799
800         if (!db->isopen) {
801                 errno = EINVAL;
802                 return 0;
803         }
804         db->isopen = false;
805
806         if (!db->changed || db->readonly) {
807                 fclose (db->fp);
808                 db->fp = NULL;
809                 goto success;
810         }
811
812         if ((NULL != db->ops->close_hook) && (db->ops->close_hook () == 0)) {
813                 goto fail;
814         }
815
816         memzero (&sb, sizeof sb);
817         if (NULL != db->fp) {
818                 if (fstat (fileno (db->fp), &sb) != 0) {
819                         fclose (db->fp);
820                         db->fp = NULL;
821                         goto fail;
822                 }
823 #ifdef WITH_SELINUX
824                 if (db->scontext != NULL) {
825                         if (getfscreatecon (&old_context) < 0) {
826                                 errors++;
827                                 goto fail;
828                         }
829                         if (setfscreatecon (db->scontext) < 0) {
830                                 errors++;
831                                 goto fail;
832                         }
833                 }
834 #endif
835                 /*
836                  * Create backup file.
837                  */
838                 snprintf (buf, sizeof buf, "%s-", db->filename);
839
840                 if (create_backup (buf, db->fp) != 0) {
841                         errors++;
842                 }
843
844                 if (fclose (db->fp) != 0) {
845                         errors++;
846                 }
847
848                 if (errors != 0) {
849                         db->fp = NULL;
850                         goto fail;
851                 }
852         } else {
853                 /*
854                  * Default permissions for new [g]shadow files.
855                  * (passwd and group always exist...)
856                  */
857                 sb.st_mode = 0400;
858                 sb.st_uid = 0;
859                 sb.st_gid = 0;
860         }
861
862         snprintf (buf, sizeof buf, "%s+", db->filename);
863
864         db->fp = fopen_set_perms (buf, "w", &sb);
865         if (NULL == db->fp) {
866                 goto fail;
867         }
868
869         if (write_all (db) != 0) {
870                 errors++;
871         }
872
873         if (fflush (db->fp) != 0) {
874                 errors++;
875         }
876 #ifdef HAVE_FSYNC
877         if (fsync (fileno (db->fp)) != 0) {
878                 errors++;
879         }
880 #else
881         sync ();
882 #endif
883         if (fclose (db->fp) != 0) {
884                 errors++;
885         }
886
887         db->fp = NULL;
888
889         if (errors != 0) {
890                 unlink (buf);
891                 goto fail;
892         }
893
894         if (lrename (buf, db->filename) != 0) {
895                 goto fail;
896         }
897
898         nscd_need_reload = true;
899         goto success;
900       fail:
901         errors++;
902       success:
903
904 #ifdef WITH_SELINUX
905         if (db->scontext != NULL) {
906                 if (NULL != old_context) {
907                 if (setfscreatecon (old_context) < 0) {
908                         errors++;
909                 }
910                         freecon (old_context);
911                         old_context = NULL;
912                 }
913                 freecon (db->scontext);
914                 db->scontext = NULL;
915         }
916 #endif
917         free_linked_list (db);
918         return errors == 0;
919 }
920
921 static /*@dependent@*/ /*@null@*/struct commonio_entry *next_entry_by_name (
922         struct commonio_db *db,
923         /*@null@*/struct commonio_entry *pos,
924         const char *name)
925 {
926         struct commonio_entry *p;
927         void *ep;
928
929         if (NULL == pos) {
930                 return NULL;
931         }
932
933         for (p = pos; NULL != p; p = p->next) {
934                 ep = p->eptr;
935                 if (   (NULL != ep)
936                     && (strcmp (db->ops->getname (ep), name) == 0)) {
937                         break;
938                 }
939         }
940         return p;
941 }
942
943 static /*@dependent@*/ /*@null@*/struct commonio_entry *find_entry_by_name (
944         struct commonio_db *db,
945         const char *name)
946 {
947         return next_entry_by_name(db, db->head, name);
948 }
949
950
951 int commonio_update (struct commonio_db *db, const void *eptr)
952 {
953         struct commonio_entry *p;
954         void *nentry;
955
956         if (!db->isopen || db->readonly) {
957                 errno = EINVAL;
958                 return 0;
959         }
960         nentry = db->ops->dup (eptr);
961         if (NULL == nentry) {
962                 errno = ENOMEM;
963                 return 0;
964         }
965         p = find_entry_by_name (db, db->ops->getname (eptr));
966         if (NULL != p) {
967                 if (next_entry_by_name (db, p->next, db->ops->getname (eptr)) != NULL) {
968                         fprintf (stderr, _("Multiple entries named '%s' in %s. Please fix this with pwck or grpck.\n"), db->ops->getname (eptr), db->filename);
969                         return 0;
970                 }
971                 db->ops->free (p->eptr);
972                 p->eptr = nentry;
973                 p->changed = true;
974                 db->cursor = p;
975
976                 db->changed = true;
977                 return 1;
978         }
979         /* not found, new entry */
980         p = (struct commonio_entry *) malloc (sizeof *p);
981         if (NULL == p) {
982                 db->ops->free (nentry);
983                 errno = ENOMEM;
984                 return 0;
985         }
986
987         p->eptr = nentry;
988         p->line = NULL;
989         p->changed = true;
990
991 #if KEEP_NIS_AT_END
992         add_one_entry_nis (db, p);
993 #else
994         add_one_entry (db, p);
995 #endif
996
997         db->changed = true;
998         return 1;
999 }
1000
1001
1002 void commonio_del_entry (struct commonio_db *db, const struct commonio_entry *p)
1003 {
1004         if (p == db->cursor) {
1005                 db->cursor = p->next;
1006         }
1007
1008         if (NULL != p->prev) {
1009                 p->prev->next = p->next;
1010         } else {
1011                 db->head = p->next;
1012         }
1013
1014         if (NULL != p->next) {
1015                 p->next->prev = p->prev;
1016         } else {
1017                 db->tail = p->prev;
1018         }
1019
1020         db->changed = true;
1021 }
1022
1023 /*
1024  * commonio_remove - Remove the entry of the given name from the database.
1025  */
1026 int commonio_remove (struct commonio_db *db, const char *name)
1027 {
1028         struct commonio_entry *p;
1029
1030         if (!db->isopen || db->readonly) {
1031                 errno = EINVAL;
1032                 return 0;
1033         }
1034         p = find_entry_by_name (db, name);
1035         if (NULL == p) {
1036                 errno = ENOENT;
1037                 return 0;
1038         }
1039         if (next_entry_by_name (db, p->next, name) != NULL) {
1040                 fprintf (stderr, _("Multiple entries named '%s' in %s. Please fix this with pwck or grpck.\n"), name, db->filename);
1041                 return 0;
1042         }
1043
1044         commonio_del_entry (db, p);
1045
1046         if (NULL != p->line) {
1047                 free (p->line);
1048         }
1049
1050         if (NULL != p->eptr) {
1051                 db->ops->free (p->eptr);
1052         }
1053
1054         return 1;
1055 }
1056
1057 /*
1058  * commonio_locate - Find the first entry with the specified name in
1059  *                   the database.
1060  *
1061  *      If found, it returns the entry and set the cursor of the database to
1062  *      that entry.
1063  *
1064  *      Otherwise, it returns NULL.
1065  */
1066 /*@observer@*/ /*@null@*/const void *commonio_locate (struct commonio_db *db, const char *name)
1067 {
1068         struct commonio_entry *p;
1069
1070         if (!db->isopen) {
1071                 errno = EINVAL;
1072                 return NULL;
1073         }
1074         p = find_entry_by_name (db, name);
1075         if (NULL == p) {
1076                 errno = ENOENT;
1077                 return NULL;
1078         }
1079         db->cursor = p;
1080         return p->eptr;
1081 }
1082
1083 /*
1084  * commonio_rewind - Restore the database cursor to the first entry.
1085  *
1086  * It returns 0 on error, 1 on success.
1087  */
1088 int commonio_rewind (struct commonio_db *db)
1089 {
1090         if (!db->isopen) {
1091                 errno = EINVAL;
1092                 return 0;
1093         }
1094         db->cursor = NULL;
1095         return 1;
1096 }
1097
1098 /*
1099  * commonio_next - Return the next entry of the specified database
1100  *
1101  * It returns the next entry, or NULL if no other entries could be found.
1102  */
1103 /*@observer@*/ /*@null@*/const void *commonio_next (struct commonio_db *db)
1104 {
1105         void *eptr;
1106
1107         if (!db->isopen) {
1108                 errno = EINVAL;
1109                 return 0;
1110         }
1111         if (NULL == db->cursor) {
1112                 db->cursor = db->head;
1113         } else {
1114                 db->cursor = db->cursor->next;
1115         }
1116
1117         while (NULL != db->cursor) {
1118                 eptr = db->cursor->eptr;
1119                 if (NULL != eptr) {
1120                         return eptr;
1121                 }
1122
1123                 db->cursor = db->cursor->next;
1124         }
1125         return NULL;
1126 }
1127