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