pam_keyinit.c 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298
  1. /*
  2. * pam_keyinit: Initialise the session keyring on login through a PAM module
  3. *
  4. * Copyright (C) 2006 Red Hat, Inc. All Rights Reserved.
  5. * Written by David Howells (dhowells@redhat.com)
  6. *
  7. * This program is free software; you can redistribute it and/or
  8. * modify it under the terms of the GNU General Public License
  9. * as published by the Free Software Foundation; either version
  10. * 2 of the License, or (at your option) any later version.
  11. */
  12. #include "config.h"
  13. #include <stdarg.h>
  14. #include <string.h>
  15. #include <syslog.h>
  16. #include <pwd.h>
  17. #include <unistd.h>
  18. #include <errno.h>
  19. #include <security/pam_modules.h>
  20. #include <security/pam_modutil.h>
  21. #include <security/pam_ext.h>
  22. #include <sys/syscall.h>
  23. #define KEY_SPEC_SESSION_KEYRING -3 /* ID for session keyring */
  24. #define KEY_SPEC_USER_KEYRING -4 /* ID for UID-specific keyring */
  25. #define KEY_SPEC_USER_SESSION_KEYRING -5 /* - key ID for UID-session keyring */
  26. #define KEYCTL_GET_KEYRING_ID 0 /* ask for a keyring's ID */
  27. #define KEYCTL_JOIN_SESSION_KEYRING 1 /* start named session keyring */
  28. #define KEYCTL_REVOKE 3 /* revoke a key */
  29. #define KEYCTL_LINK 8 /* link a key into a keyring */
  30. static int my_session_keyring = 0;
  31. static int session_counter = 0;
  32. static int do_revoke = 0;
  33. static uid_t revoke_as_uid;
  34. static gid_t revoke_as_gid;
  35. static int xdebug = 0;
  36. static void debug(pam_handle_t *pamh, const char *fmt, ...)
  37. __attribute__((format(printf, 2, 3)));
  38. static void debug(pam_handle_t *pamh, const char *fmt, ...)
  39. {
  40. va_list va;
  41. if (xdebug) {
  42. va_start(va, fmt);
  43. pam_vsyslog(pamh, LOG_DEBUG, fmt, va);
  44. va_end(va);
  45. }
  46. }
  47. static void error(pam_handle_t *pamh, const char *fmt, ...)
  48. __attribute__((format(printf, 2, 3)));
  49. static void error(pam_handle_t *pamh, const char *fmt, ...)
  50. {
  51. va_list va;
  52. va_start(va, fmt);
  53. pam_vsyslog(pamh, LOG_ERR, fmt, va);
  54. va_end(va);
  55. }
  56. /*
  57. * initialise the session keyring for this process
  58. */
  59. static int init_keyrings(pam_handle_t *pamh, int force, int error_ret)
  60. {
  61. int session, usession, ret;
  62. if (!force) {
  63. /* get the IDs of the session keyring and the user session
  64. * keyring */
  65. session = syscall(__NR_keyctl,
  66. KEYCTL_GET_KEYRING_ID,
  67. KEY_SPEC_SESSION_KEYRING,
  68. 0);
  69. debug(pamh, "GET SESSION = %d", session);
  70. if (session < 0) {
  71. /* don't worry about keyrings if facility not
  72. * installed */
  73. if (errno == ENOSYS)
  74. return PAM_SUCCESS;
  75. return error_ret;
  76. }
  77. usession = syscall(__NR_keyctl,
  78. KEYCTL_GET_KEYRING_ID,
  79. KEY_SPEC_USER_SESSION_KEYRING,
  80. 0);
  81. debug(pamh, "GET SESSION = %d", usession);
  82. if (usession < 0)
  83. return error_ret;
  84. /* if the user session keyring is our keyring, then we don't
  85. * need to do anything if we're not forcing */
  86. if (session != usession)
  87. return PAM_SUCCESS;
  88. }
  89. /* create a session keyring, discarding the old one */
  90. ret = syscall(__NR_keyctl,
  91. KEYCTL_JOIN_SESSION_KEYRING,
  92. NULL);
  93. debug(pamh, "JOIN = %d", ret);
  94. if (ret < 0)
  95. return error_ret;
  96. my_session_keyring = ret;
  97. /* make a link from the session keyring to the user keyring */
  98. ret = syscall(__NR_keyctl,
  99. KEYCTL_LINK,
  100. KEY_SPEC_USER_KEYRING,
  101. KEY_SPEC_SESSION_KEYRING);
  102. return ret < 0 ? error_ret : PAM_SUCCESS;
  103. }
  104. /*
  105. * revoke the session keyring for this process
  106. */
  107. static int kill_keyrings(pam_handle_t *pamh, int error_ret)
  108. {
  109. uid_t old_uid;
  110. gid_t old_gid;
  111. int ret = PAM_SUCCESS;
  112. /* revoke the session keyring we created earlier */
  113. if (my_session_keyring > 0) {
  114. debug(pamh, "REVOKE %d", my_session_keyring);
  115. old_uid = geteuid();
  116. old_gid = getegid();
  117. debug(pamh, "UID:%d [%d] GID:%d [%d]",
  118. revoke_as_uid, old_uid, revoke_as_gid, old_gid);
  119. /* switch to the real UID and GID so that we have permission to
  120. * revoke the key */
  121. if (revoke_as_gid != old_gid && setregid(-1, revoke_as_gid) < 0) {
  122. error(pamh, "Unable to change GID to %d temporarily\n", revoke_as_gid);
  123. return error_ret;
  124. }
  125. if (revoke_as_uid != old_uid && setresuid(-1, revoke_as_uid, old_uid) < 0) {
  126. error(pamh, "Unable to change UID to %d temporarily\n", revoke_as_uid);
  127. if (getegid() != old_gid && setregid(-1, old_gid) < 0)
  128. error(pamh, "Unable to change GID back to %d\n", old_gid);
  129. return error_ret;
  130. }
  131. if (syscall(__NR_keyctl, KEYCTL_REVOKE, my_session_keyring) < 0) {
  132. ret = error_ret;
  133. }
  134. /* return to the original UID and GID (probably root) */
  135. if (revoke_as_uid != old_uid && setreuid(-1, old_uid) < 0) {
  136. error(pamh, "Unable to change UID back to %d\n", old_uid);
  137. ret = error_ret;
  138. }
  139. if (revoke_as_gid != old_gid && setregid(-1, old_gid) < 0) {
  140. error(pamh, "Unable to change GID back to %d\n", old_gid);
  141. ret = error_ret;
  142. }
  143. my_session_keyring = 0;
  144. }
  145. return ret;
  146. }
  147. static int do_keyinit(pam_handle_t *pamh, int argc, const char **argv, int error_ret)
  148. {
  149. struct passwd *pw;
  150. const char *username;
  151. int ret, loop, force = 0;
  152. uid_t old_uid, uid;
  153. gid_t old_gid, gid;
  154. for (loop = 0; loop < argc; loop++) {
  155. if (strcmp(argv[loop], "force") == 0)
  156. force = 1;
  157. else if (strcmp(argv[loop], "debug") == 0)
  158. xdebug = 1;
  159. else if (strcmp(argv[loop], "revoke") == 0)
  160. do_revoke = 1;
  161. }
  162. /* don't do anything if already created a keyring (will be called
  163. * multiple times if mentioned more than once in a pam script)
  164. */
  165. if (my_session_keyring > 0)
  166. return PAM_SUCCESS;
  167. /* look up the target UID */
  168. ret = pam_get_user(pamh, &username, "key user");
  169. if (ret != PAM_SUCCESS)
  170. return ret;
  171. pw = pam_modutil_getpwnam(pamh, username);
  172. if (!pw) {
  173. pam_syslog(pamh, LOG_NOTICE, "Unable to look up user \"%s\"\n",
  174. username);
  175. return PAM_USER_UNKNOWN;
  176. }
  177. revoke_as_uid = uid = pw->pw_uid;
  178. old_uid = getuid();
  179. revoke_as_gid = gid = pw->pw_gid;
  180. old_gid = getgid();
  181. debug(pamh, "UID:%d [%d] GID:%d [%d]", uid, old_uid, gid, old_gid);
  182. /* switch to the real UID and GID so that the keyring ends up owned by
  183. * the right user */
  184. if (gid != old_gid && setregid(gid, -1) < 0) {
  185. error(pamh, "Unable to change GID to %d temporarily\n", gid);
  186. return error_ret;
  187. }
  188. if (uid != old_uid && setreuid(uid, -1) < 0) {
  189. error(pamh, "Unable to change UID to %d temporarily\n", uid);
  190. if (setregid(old_gid, -1) < 0)
  191. error(pamh, "Unable to change GID back to %d\n", old_gid);
  192. return error_ret;
  193. }
  194. ret = init_keyrings(pamh, force, error_ret);
  195. /* return to the original UID and GID (probably root) */
  196. if (uid != old_uid && setreuid(old_uid, -1) < 0) {
  197. error(pamh, "Unable to change UID back to %d\n", old_uid);
  198. ret = error_ret;
  199. }
  200. if (gid != old_gid && setregid(old_gid, -1) < 0) {
  201. error(pamh, "Unable to change GID back to %d\n", old_gid);
  202. ret = error_ret;
  203. }
  204. return ret;
  205. }
  206. /*
  207. * Dummy
  208. */
  209. int pam_sm_authenticate(pam_handle_t *pamh UNUSED, int flags UNUSED,
  210. int argc UNUSED, const char **argv UNUSED)
  211. {
  212. return PAM_IGNORE;
  213. }
  214. /*
  215. * since setcred and open_session are called in different orders, a
  216. * session ring is invoked by the first of these functions called.
  217. */
  218. int pam_sm_setcred(pam_handle_t *pamh, int flags,
  219. int argc, const char **argv)
  220. {
  221. if (flags & PAM_ESTABLISH_CRED) {
  222. debug(pamh, "ESTABLISH_CRED");
  223. return do_keyinit(pamh, argc, argv, PAM_CRED_ERR);
  224. }
  225. if (flags & PAM_DELETE_CRED && my_session_keyring > 0 && do_revoke) {
  226. debug(pamh, "DELETE_CRED");
  227. return kill_keyrings(pamh, PAM_CRED_ERR);
  228. }
  229. return PAM_IGNORE;
  230. }
  231. int pam_sm_open_session(pam_handle_t *pamh, int flags UNUSED,
  232. int argc, const char **argv)
  233. {
  234. session_counter++;
  235. debug(pamh, "OPEN %d", session_counter);
  236. return do_keyinit(pamh, argc, argv, PAM_SESSION_ERR);
  237. }
  238. /*
  239. * close a PAM session by revoking the session keyring if requested
  240. */
  241. int pam_sm_close_session(pam_handle_t *pamh, int flags UNUSED,
  242. int argc UNUSED, const char **argv UNUSED)
  243. {
  244. debug(pamh, "CLOSE %d,%d,%d",
  245. session_counter, my_session_keyring, do_revoke);
  246. session_counter--;
  247. if (session_counter <= 0 && my_session_keyring > 0 && do_revoke)
  248. kill_keyrings(pamh, PAM_SESSION_ERR);
  249. return PAM_SUCCESS;
  250. }