1 /* pam_keyinit.c: Initialise the session keyring on login through a PAM module
3 * Copyright (C) 2006 Red Hat, Inc. All Rights Reserved.
4 * Written by David Howells (dhowells@redhat.com)
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version
9 * 2 of the License, or (at your option) any later version.
19 #include <security/pam_modules.h>
20 #include <security/pam_modutil.h>
21 #include <security/pam_ext.h>
22 #include <sys/syscall.h>
24 #define KEY_SPEC_SESSION_KEYRING -3 /* ID for session keyring */
25 #define KEY_SPEC_USER_KEYRING -4 /* ID for UID-specific keyring */
26 #define KEY_SPEC_USER_SESSION_KEYRING -5 /* - key ID for UID-session keyring */
28 #define KEYCTL_GET_KEYRING_ID 0 /* ask for a keyring's ID */
29 #define KEYCTL_JOIN_SESSION_KEYRING 1 /* start named session keyring */
30 #define KEYCTL_REVOKE 3 /* revoke a key */
31 #define KEYCTL_LINK 8 /* link a key into a keyring */
33 static int my_session_keyring;
34 static int session_counter;
36 static int revoke_as_uid;
37 static int revoke_as_gid;
38 static int xdebug = 0;
40 static void debug(pam_handle_t *pamh, const char *fmt, ...)
41 __attribute__((format(printf, 2, 3)));
43 static void debug(pam_handle_t *pamh, const char *fmt, ...)
49 pam_vsyslog(pamh, LOG_DEBUG, fmt, va);
54 static int error(pam_handle_t *pamh, const char *fmt, ...)
55 __attribute__((format(printf, 2, 3)));
57 static int error(pam_handle_t *pamh, const char *fmt, ...)
62 pam_vsyslog(pamh, LOG_ERR, fmt, va);
65 return PAM_SESSION_ERR;
69 * initialise the session keyring for this process
71 static int init_keyrings(pam_handle_t *pamh, int force)
73 int session, usession, ret;
76 /* get the IDs of the session keyring and the user session
78 session = syscall(__NR_keyctl,
79 KEYCTL_GET_KEYRING_ID,
80 KEY_SPEC_SESSION_KEYRING,
82 debug(pamh, "GET SESSION = %d", session);
84 /* don't worry about keyrings if facility not
88 return PAM_SESSION_ERR;
91 usession = syscall(__NR_keyctl,
92 KEYCTL_GET_KEYRING_ID,
93 KEY_SPEC_USER_SESSION_KEYRING,
95 debug(pamh, "GET SESSION = %d", usession);
97 return PAM_SESSION_ERR;
99 /* if the user session keyring is our keyring, then we don't
100 * need to do anything if we're not forcing */
101 if (session != usession)
105 /* create a session keyring, discarding the old one */
106 ret = syscall(__NR_keyctl,
107 KEYCTL_JOIN_SESSION_KEYRING,
109 debug(pamh, "JOIN = %d", ret);
111 return PAM_SESSION_ERR;
113 my_session_keyring = ret;
115 /* make a link from the session keyring to the user keyring */
116 ret = syscall(__NR_keyctl,
118 KEY_SPEC_USER_KEYRING,
119 KEY_SPEC_SESSION_KEYRING);
121 return ret < 0 ? PAM_SESSION_ERR : PAM_SUCCESS;
125 * revoke the session keyring for this process
127 static void kill_keyrings(pam_handle_t *pamh)
129 int old_uid, old_gid;
131 /* revoke the session keyring we created earlier */
132 if (my_session_keyring > 0) {
133 debug(pamh, "REVOKE %d", my_session_keyring);
137 debug(pamh, "UID:%d [%d] GID:%d [%d]",
138 revoke_as_uid, old_uid, revoke_as_gid, old_gid);
140 /* switch to the real UID and GID so that we have permission to
142 if (revoke_as_gid != old_gid && setregid(-1, revoke_as_gid) < 0)
143 error(pamh, "Unable to change GID to %d temporarily\n",
146 if (revoke_as_uid != old_uid && setresuid(-1, revoke_as_uid, old_uid) < 0)
147 error(pamh, "Unable to change UID to %d temporarily\n",
154 /* return to the orignal UID and GID (probably root) */
155 if (revoke_as_uid != old_uid && setreuid(-1, old_uid) < 0)
156 error(pamh, "Unable to change UID back to %d\n", old_uid);
158 if (revoke_as_gid != old_gid && setregid(-1, old_gid) < 0)
159 error(pamh, "Unable to change GID back to %d\n", old_gid);
161 my_session_keyring = 0;
166 * open a PAM session by making sure there's a session keyring
169 int pam_sm_open_session(pam_handle_t *pamh, int flags UNUSED,
170 int argc, const char **argv)
173 const char *username;
174 int ret, old_uid, uid, old_gid, gid, loop, force = 0;
176 for (loop = 0; loop < argc; loop++) {
177 if (strcmp(argv[loop], "force") == 0)
179 else if (strcmp(argv[loop], "debug") == 0)
181 else if (strcmp(argv[loop], "revoke") == 0)
185 /* don't do anything if already created a keyring (will be called
186 * multiple times if mentioned more than once in a pam script)
190 debug(pamh, "OPEN %d", session_counter);
192 if (my_session_keyring > 0)
195 /* look up the target UID */
196 ret = pam_get_user(pamh, &username, "key user");
197 if (ret != PAM_SUCCESS)
200 pw = pam_modutil_getpwnam(pamh, username);
202 error(pamh, "Unable to look up user \"%s\"\n", username);
203 return PAM_USER_UNKNOWN;
206 revoke_as_uid = uid = pw->pw_uid;
208 revoke_as_gid = gid = pw->pw_gid;
210 debug(pamh, "UID:%d [%d] GID:%d [%d]", uid, old_uid, gid, old_gid);
212 /* switch to the real UID and GID so that the keyring ends up owned by
214 if (gid != old_gid && setregid(gid, -1) < 0) {
215 error(pamh, "Unable to change GID to %d temporarily\n", gid);
216 return PAM_SESSION_ERR;
219 if (uid != old_uid && setreuid(uid, -1) < 0) {
220 error(pamh, "Unable to change UID to %d temporarily\n", uid);
221 setregid(old_gid, -1);
222 return PAM_SESSION_ERR;
225 ret = init_keyrings(pamh, force);
227 /* return to the orignal UID and GID (probably root) */
228 if (uid != old_uid && setreuid(old_uid, -1) < 0)
229 ret = error(pamh, "Unable to change UID back to %d\n", old_uid);
231 if (gid != old_gid && setregid(old_gid, -1) < 0)
232 ret = error(pamh, "Unable to change GID back to %d\n", old_gid);
238 * close a PAM session by revoking the session keyring if requested
241 int pam_sm_close_session(pam_handle_t *pamh, int flags UNUSED,
242 int argc UNUSED, const char **argv UNUSED)
244 debug(pamh, "CLOSE %d,%d,%d",
245 session_counter, my_session_keyring, do_revoke);
249 if (session_counter == 0 && my_session_keyring > 0 && do_revoke)
257 /* static module data */
259 struct pam_module _pam_keyinit_modstruct = {
265 pam_sm_close_session,