(root)/
glibc-2.38/
shadow/
lckpwdf.c
       1  /* Handle locking of password file.
       2     Copyright (C) 1996-2023 Free Software Foundation, Inc.
       3     This file is part of the GNU C Library.
       4  
       5     The GNU C Library is free software; you can redistribute it and/or
       6     modify it under the terms of the GNU Lesser General Public
       7     License as published by the Free Software Foundation; either
       8     version 2.1 of the License, or (at your option) any later version.
       9  
      10     The GNU C Library is distributed in the hope that it will be useful,
      11     but WITHOUT ANY WARRANTY; without even the implied warranty of
      12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      13     Lesser General Public License for more details.
      14  
      15     You should have received a copy of the GNU Lesser General Public
      16     License along with the GNU C Library; if not, see
      17     <https://www.gnu.org/licenses/>.  */
      18  
      19  #include <fcntl.h>
      20  #include <libc-lock.h>
      21  #include <shadow.h>
      22  #include <signal.h>
      23  #include <string.h>
      24  #include <unistd.h>
      25  #include <sys/file.h>
      26  #include <sigsetops.h>
      27  
      28  #include <kernel-features.h>
      29  
      30  
      31  /* Name of the lock file.  */
      32  #define PWD_LOCKFILE "/etc/.pwd.lock"
      33  
      34  /* How long to wait for getting the lock before returning with an
      35     error.  */
      36  #define TIMEOUT 15 /* sec */
      37  
      38  
      39  /* File descriptor for lock file.  */
      40  static int lock_fd = -1;
      41  
      42  /* Prevent problems in multithreaded program by using mutex.  */
      43  __libc_lock_define_initialized (static, lock)
      44  
      45  
      46  /* Prototypes for local functions.  */
      47  static void noop_handler (int __sig);
      48  
      49  
      50  /* We cannot simply return in error cases.  We have to close the file
      51     and perhaps restore the signal handler.  */
      52  #define RETURN_CLOSE_FD(code)						      \
      53    do {									      \
      54      if ((code) < 0 && lock_fd >= 0)					      \
      55        {									      \
      56  	__close (lock_fd);						      \
      57  	lock_fd = -1;							      \
      58        }									      \
      59      __libc_lock_unlock (lock);						      \
      60      return (code);							      \
      61    } while (0)
      62  
      63  #define RETURN_RESTORE_HANDLER(code)					      \
      64    do {									      \
      65      /* Restore old action handler for alarm.  We don't need to know	      \
      66         about the current one.  */					      \
      67      __sigaction (SIGALRM, &saved_act, NULL);				      \
      68      RETURN_CLOSE_FD (code);						      \
      69    } while (0)
      70  
      71  #define RETURN_CLEAR_ALARM(code)					      \
      72    do {									      \
      73      /* Clear alarm.  */							      \
      74      alarm (0);								      \
      75      /* Restore old set of handled signals.  We don't need to know	      \
      76         about the current one.*/						      \
      77      __sigprocmask (SIG_SETMASK, &saved_set, NULL);			      \
      78      RETURN_RESTORE_HANDLER (code);					      \
      79    } while (0)
      80  
      81  
      82  int
      83  __lckpwdf (void)
      84  {
      85    sigset_t saved_set;			/* Saved set of caught signals.  */
      86    struct sigaction saved_act;		/* Saved signal action.  */
      87    sigset_t new_set;			/* New set of caught signals.  */
      88    struct sigaction new_act;		/* New signal action.  */
      89    struct flock fl;			/* Information struct for locking.  */
      90    int result;
      91  
      92    if (lock_fd != -1)
      93      /* Still locked by own process.  */
      94      return -1;
      95  
      96    /* Prevent problems caused by multiple threads.  */
      97    __libc_lock_lock (lock);
      98  
      99    int oflags = O_WRONLY | O_CREAT | O_CLOEXEC;
     100    lock_fd = __open (PWD_LOCKFILE, oflags, 0600);
     101    if (lock_fd == -1)
     102      /* Cannot create lock file.  */
     103      RETURN_CLOSE_FD (-1);
     104  
     105    /* Now we have to get exclusive write access.  Since multiple
     106       process could try this we won't stop when it first fails.
     107       Instead we set a timeout for the system call.  Once the timer
     108       expires it is likely that there are some problems which cannot be
     109       resolved by waiting.
     110  
     111       It is important that we don't change the signal state.  We must
     112       restore the old signal behaviour.  */
     113    memset (&new_act, '\0', sizeof (struct sigaction));
     114    new_act.sa_handler = noop_handler;
     115    __sigfillset (&new_act.sa_mask);
     116    new_act.sa_flags = 0ul;
     117  
     118    /* Install new action handler for alarm and save old.  */
     119    if (__sigaction (SIGALRM, &new_act, &saved_act) < 0)
     120      /* Cannot install signal handler.  */
     121      RETURN_CLOSE_FD (-1);
     122  
     123    /* Now make sure the alarm signal is not blocked.  */
     124    __sigemptyset (&new_set);
     125    __sigaddset (&new_set, SIGALRM);
     126    if (__sigprocmask (SIG_UNBLOCK, &new_set, &saved_set) < 0)
     127      RETURN_RESTORE_HANDLER (-1);
     128  
     129    /* Start timer.  If we cannot get the lock in the specified time we
     130       get a signal.  */
     131    alarm (TIMEOUT);
     132  
     133    /* Try to get the lock.  */
     134    memset (&fl, '\0', sizeof (struct flock));
     135    fl.l_type = F_WRLCK;
     136    fl.l_whence = SEEK_SET;
     137    result = __fcntl (lock_fd, F_SETLKW, &fl);
     138  
     139    RETURN_CLEAR_ALARM (result);
     140  }
     141  weak_alias (__lckpwdf, lckpwdf)
     142  
     143  
     144  int
     145  __ulckpwdf (void)
     146  {
     147    int result;
     148  
     149    if (lock_fd == -1)
     150      /* There is no lock set.  */
     151      result = -1;
     152    else
     153      {
     154        /* Prevent problems caused by multiple threads.  */
     155        __libc_lock_lock (lock);
     156  
     157        result = __close (lock_fd);
     158  
     159        /* Mark descriptor as unused.  */
     160        lock_fd = -1;
     161  
     162        /* Clear mutex.  */
     163        __libc_lock_unlock (lock);
     164      }
     165  
     166    return result;
     167  }
     168  weak_alias (__ulckpwdf, ulckpwdf)
     169  
     170  
     171  static void
     172  noop_handler (int sig)
     173  {
     174    /* We simply return which makes the `fcntl' call return with an error.  */
     175  }