lckpwdf.-c 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142
  1. /*
  2. * This is a hack, but until libc and glibc both include this function
  3. * by default (libc only includes it if nys is not being used, at the
  4. * moment, and glibc doesn't appear to have it at all) we need to have
  5. * it here, too. :-(
  6. *
  7. * This should not become an official part of PAM.
  8. *
  9. * BEGIN_HACK
  10. */
  11. /*
  12. * lckpwdf.c -- prevent simultaneous updates of password files
  13. *
  14. * Before modifying any of the password files, call lckpwdf(). It may block
  15. * for up to 15 seconds trying to get the lock. Return value is 0 on success
  16. * or -1 on failure. When you are done, call ulckpwdf() to release the lock.
  17. * The lock is also released automatically when the process exits. Only one
  18. * process at a time may hold the lock.
  19. *
  20. * These functions are supposed to be conformant with AT&T SVID Issue 3.
  21. *
  22. * Written by Marek Michalkiewicz <marekm@i17linuxb.ists.pwr.wroc.pl>,
  23. * public domain.
  24. */
  25. #include <fcntl.h>
  26. #include <signal.h>
  27. #ifdef WITH_SELINUX
  28. #include <selinux/selinux.h>
  29. #endif
  30. #define LOCKFILE "/etc/.pwd.lock"
  31. #define TIMEOUT 15
  32. static int lockfd = -1;
  33. static int set_close_on_exec(int fd)
  34. {
  35. int flags = fcntl(fd, F_GETFD, 0);
  36. if (flags == -1)
  37. return -1;
  38. flags |= FD_CLOEXEC;
  39. return fcntl(fd, F_SETFD, flags);
  40. }
  41. static int do_lock(int fd)
  42. {
  43. struct flock fl;
  44. memset(&fl, 0, sizeof fl);
  45. fl.l_type = F_WRLCK;
  46. fl.l_whence = SEEK_SET;
  47. return fcntl(fd, F_SETLKW, &fl);
  48. }
  49. static void alarm_catch(int sig)
  50. {
  51. /* does nothing, but fcntl F_SETLKW will fail with EINTR */
  52. }
  53. static int lckpwdf(void)
  54. {
  55. struct sigaction act, oldact;
  56. sigset_t set, oldset;
  57. if (lockfd != -1)
  58. return -1;
  59. #ifdef WITH_SELINUX
  60. if(is_selinux_enabled()>0)
  61. {
  62. lockfd = open(LOCKFILE, O_WRONLY);
  63. if(lockfd == -1 && errno == ENOENT)
  64. {
  65. char *create_context_raw;
  66. int rc;
  67. if(getfilecon_raw("/etc/passwd", &create_context_raw))
  68. return -1;
  69. rc = setfscreatecon_raw(create_context_raw);
  70. freecon(create_context_raw);
  71. if(rc)
  72. return -1;
  73. lockfd = open(LOCKFILE, O_CREAT | O_WRONLY, 0600);
  74. if(setfscreatecon_raw(NULL))
  75. return -1;
  76. }
  77. }
  78. else
  79. #endif
  80. lockfd = open(LOCKFILE, O_CREAT | O_WRONLY, 0600);
  81. if (lockfd == -1)
  82. return -1;
  83. if (set_close_on_exec(lockfd) == -1)
  84. goto cleanup_fd;
  85. memset(&act, 0, sizeof act);
  86. act.sa_handler = alarm_catch;
  87. act.sa_flags = 0;
  88. sigfillset(&act.sa_mask);
  89. if (sigaction(SIGALRM, &act, &oldact) == -1)
  90. goto cleanup_fd;
  91. sigemptyset(&set);
  92. sigaddset(&set, SIGALRM);
  93. if (sigprocmask(SIG_UNBLOCK, &set, &oldset) == -1)
  94. goto cleanup_sig;
  95. alarm(TIMEOUT);
  96. if (do_lock(lockfd) == -1)
  97. goto cleanup_alarm;
  98. alarm(0);
  99. sigprocmask(SIG_SETMASK, &oldset, NULL);
  100. sigaction(SIGALRM, &oldact, NULL);
  101. return 0;
  102. cleanup_alarm:
  103. alarm(0);
  104. sigprocmask(SIG_SETMASK, &oldset, NULL);
  105. cleanup_sig:
  106. sigaction(SIGALRM, &oldact, NULL);
  107. cleanup_fd:
  108. close(lockfd);
  109. lockfd = -1;
  110. return -1;
  111. }
  112. static int ulckpwdf(void)
  113. {
  114. unlink(LOCKFILE);
  115. if (lockfd == -1)
  116. return -1;
  117. if (close(lockfd) == -1) {
  118. lockfd = -1;
  119. return -1;
  120. }
  121. lockfd = -1;
  122. return 0;
  123. }
  124. /* END_HACK */