pam_mkhomedir.c 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267
  1. /* PAM Make Home Dir module
  2. This module will create a users home directory if it does not exist
  3. when the session begins. This allows users to be present in central
  4. database (such as nis, kerb or ldap) without using a distributed
  5. file system or pre-creating a large number of directories.
  6. Here is a sample /etc/pam.d/login file for Debian GNU/Linux
  7. 2.1:
  8. auth requisite pam_securetty.so
  9. auth sufficient pam_ldap.so
  10. auth required pam_unix.so
  11. auth optional pam_group.so
  12. auth optional pam_mail.so
  13. account requisite pam_time.so
  14. account sufficient pam_ldap.so
  15. account required pam_unix.so
  16. session required pam_mkhomedir.so skel=/etc/skel/ umask=0022
  17. session required pam_unix.so
  18. session optional pam_lastlog.so
  19. password required pam_unix.so
  20. Released under the GNU LGPL version 2 or later
  21. Copyright (c) Red Hat, Inc. 2009
  22. Originally written by Jason Gunthorpe <jgg@debian.org> Feb 1999
  23. Structure taken from pam_lastlogin by Andrew Morgan
  24. <morgan@parc.power.net> 1996
  25. */
  26. #include "config.h"
  27. #include <sys/types.h>
  28. #include <sys/stat.h>
  29. #include <sys/time.h>
  30. #include <sys/resource.h>
  31. #include <sys/wait.h>
  32. #include <unistd.h>
  33. #include <pwd.h>
  34. #include <errno.h>
  35. #include <stdlib.h>
  36. #include <stdio.h>
  37. #include <string.h>
  38. #include <syslog.h>
  39. #include <signal.h>
  40. #include <security/pam_modules.h>
  41. #include <security/_pam_macros.h>
  42. #include <security/pam_modutil.h>
  43. #include <security/pam_ext.h>
  44. #include "pam_cc_compat.h"
  45. #include "pam_inline.h"
  46. /* argument parsing */
  47. #define MKHOMEDIR_DEBUG 020 /* be verbose about things */
  48. #define MKHOMEDIR_QUIET 040 /* keep quiet about things */
  49. #define LOGIN_DEFS "/etc/login.defs"
  50. #define UMASK_DEFAULT "0022"
  51. struct options_t {
  52. int ctrl;
  53. const char *umask;
  54. const char *skeldir;
  55. };
  56. typedef struct options_t options_t;
  57. static void
  58. _pam_parse (const pam_handle_t *pamh, int flags, int argc, const char **argv,
  59. options_t *opt)
  60. {
  61. opt->ctrl = 0;
  62. opt->umask = NULL;
  63. opt->skeldir = "/etc/skel";
  64. /* does the application require quiet? */
  65. if ((flags & PAM_SILENT) == PAM_SILENT)
  66. opt->ctrl |= MKHOMEDIR_QUIET;
  67. /* step through arguments */
  68. for (; argc-- > 0; ++argv)
  69. {
  70. const char *str;
  71. if (!strcmp(*argv, "silent")) {
  72. opt->ctrl |= MKHOMEDIR_QUIET;
  73. } else if (!strcmp(*argv, "debug")) {
  74. opt->ctrl |= MKHOMEDIR_DEBUG;
  75. } else if ((str = pam_str_skip_prefix(*argv, "umask=")) != NULL) {
  76. opt->umask = str;
  77. } else if ((str = pam_str_skip_prefix(*argv, "skel=")) != NULL) {
  78. opt->skeldir = str;
  79. } else {
  80. pam_syslog(pamh, LOG_ERR, "unknown option: %s", *argv);
  81. }
  82. }
  83. }
  84. static char*
  85. _pam_conv_str_umask_to_homemode(const char *umask)
  86. {
  87. unsigned int m = 0;
  88. char tmp[5];
  89. m = 0777 & ~strtoul(umask, NULL, 8);
  90. (void) snprintf(tmp, sizeof(tmp), "0%o", m);
  91. return strdup(tmp);
  92. }
  93. /* Do the actual work of creating a home dir */
  94. static int
  95. create_homedir (pam_handle_t *pamh, options_t *opt,
  96. const char *user, const char *dir)
  97. {
  98. int retval, child;
  99. struct sigaction newsa, oldsa;
  100. char *login_umask = NULL;
  101. char *login_homemode = NULL;
  102. /* Mention what is happening, if the notification fails that is OK */
  103. if (!(opt->ctrl & MKHOMEDIR_QUIET))
  104. pam_info(pamh, _("Creating directory '%s'."), dir);
  105. D(("called."));
  106. /*
  107. * This code arranges that the demise of the child does not cause
  108. * the application to receive a signal it is not expecting - which
  109. * may kill the application or worse.
  110. */
  111. memset(&newsa, '\0', sizeof(newsa));
  112. newsa.sa_handler = SIG_DFL;
  113. sigaction(SIGCHLD, &newsa, &oldsa);
  114. if (opt->ctrl & MKHOMEDIR_DEBUG) {
  115. pam_syslog(pamh, LOG_DEBUG, "Executing mkhomedir_helper.");
  116. }
  117. /* fetch UMASK from /etc/login.defs if not in argv */
  118. if (opt->umask == NULL) {
  119. login_umask = pam_modutil_search_key(pamh, LOGIN_DEFS, "UMASK");
  120. login_homemode = pam_modutil_search_key(pamh, LOGIN_DEFS, "HOME_MODE");
  121. if (login_homemode == NULL) {
  122. if (login_umask != NULL) {
  123. login_homemode = _pam_conv_str_umask_to_homemode(login_umask);
  124. } else {
  125. login_homemode = _pam_conv_str_umask_to_homemode(UMASK_DEFAULT);
  126. }
  127. }
  128. } else {
  129. login_homemode = _pam_conv_str_umask_to_homemode(opt->umask);
  130. }
  131. /* fork */
  132. child = fork();
  133. if (child == 0) {
  134. static char *envp[] = { NULL };
  135. const char *args[] = { NULL, NULL, NULL, NULL, NULL, NULL };
  136. if (pam_modutil_sanitize_helper_fds(pamh, PAM_MODUTIL_PIPE_FD,
  137. PAM_MODUTIL_PIPE_FD,
  138. PAM_MODUTIL_PIPE_FD) < 0)
  139. _exit(PAM_SYSTEM_ERR);
  140. /* exec the mkhomedir helper */
  141. args[0] = MKHOMEDIR_HELPER;
  142. args[1] = user;
  143. args[2] = opt->umask ? opt->umask : UMASK_DEFAULT;
  144. args[3] = opt->skeldir;
  145. args[4] = login_homemode;
  146. DIAG_PUSH_IGNORE_CAST_QUAL;
  147. execve(MKHOMEDIR_HELPER, (char **)args, envp);
  148. DIAG_POP_IGNORE_CAST_QUAL;
  149. /* should not get here: exit with error */
  150. D(("helper binary is not available"));
  151. _exit(PAM_SYSTEM_ERR);
  152. } else if (child > 0) {
  153. int rc;
  154. while ((rc=waitpid(child, &retval, 0)) < 0 && errno == EINTR);
  155. if (rc < 0) {
  156. pam_syslog(pamh, LOG_ERR, "waitpid failed: %m");
  157. retval = PAM_SYSTEM_ERR;
  158. } else if (!WIFEXITED(retval)) {
  159. pam_syslog(pamh, LOG_ERR, "mkhomedir_helper abnormal exit: %d", retval);
  160. retval = PAM_SYSTEM_ERR;
  161. } else {
  162. retval = WEXITSTATUS(retval);
  163. }
  164. } else {
  165. D(("fork failed"));
  166. pam_syslog(pamh, LOG_ERR, "fork failed: %m");
  167. retval = PAM_SYSTEM_ERR;
  168. }
  169. sigaction(SIGCHLD, &oldsa, NULL); /* restore old signal handler */
  170. if (opt->ctrl & MKHOMEDIR_DEBUG) {
  171. pam_syslog(pamh, LOG_DEBUG, "mkhomedir_helper returned %d", retval);
  172. }
  173. if (retval != PAM_SUCCESS && !(opt->ctrl & MKHOMEDIR_QUIET)) {
  174. pam_error(pamh, _("Unable to create and initialize directory '%s'."),
  175. dir);
  176. }
  177. free(login_umask);
  178. free(login_homemode);
  179. D(("returning %d", retval));
  180. return retval;
  181. }
  182. /* --- authentication management functions (only) --- */
  183. int
  184. pam_sm_open_session (pam_handle_t *pamh, int flags, int argc,
  185. const char **argv)
  186. {
  187. int retval;
  188. options_t opt;
  189. const void *user;
  190. const struct passwd *pwd;
  191. struct stat St;
  192. /* Parse the flag values */
  193. _pam_parse(pamh, flags, argc, argv, &opt);
  194. /* Determine the user name so we can get the home directory */
  195. retval = pam_get_item(pamh, PAM_USER, &user);
  196. if (retval != PAM_SUCCESS || user == NULL || *(const char *)user == '\0')
  197. {
  198. pam_syslog(pamh, LOG_NOTICE, "Cannot obtain the user name.");
  199. return PAM_USER_UNKNOWN;
  200. }
  201. /* Get the password entry */
  202. pwd = pam_modutil_getpwnam (pamh, user);
  203. if (pwd == NULL)
  204. {
  205. pam_syslog(pamh, LOG_NOTICE, "User unknown.");
  206. D(("couldn't identify user %s", user));
  207. return PAM_USER_UNKNOWN;
  208. }
  209. /* Stat the home directory, if something exists then we assume it is
  210. correct and return a success*/
  211. if (stat(pwd->pw_dir, &St) == 0) {
  212. if (opt.ctrl & MKHOMEDIR_DEBUG) {
  213. pam_syslog(pamh, LOG_DEBUG, "Home directory %s already exists.",
  214. pwd->pw_dir);
  215. }
  216. return PAM_SUCCESS;
  217. }
  218. return create_homedir(pamh, &opt, user, pwd->pw_dir);
  219. }
  220. /* Ignore */
  221. int pam_sm_close_session (pam_handle_t * pamh UNUSED, int flags UNUSED,
  222. int argc UNUSED, const char **argv UNUSED)
  223. {
  224. return PAM_SUCCESS;
  225. }