1 /*
2 * The public APIs of the pam-afs-session PAM module.
3 *
4 * Provides the public pam_sm_authenticate, pam_sm_setcred,
5 * pam_sm_open_session, pam_sm_close_session, and pam_sm_chauthtok functions.
6 * These must all be specified in the same file to work with the symbol export
7 * and linking mechanism used in OpenPAM, since OpenPAM will mark them all as
8 * static functions and export a function table instead.
9 *
10 * Written by Russ Allbery <eagle@eyrie.org>
11 * Copyright 2005-2009, 2017, 2020 Russ Allbery <eagle@eyrie.org>
12 * Copyright 2011
13 * The Board of Trustees of the Leland Stanford Junior University
14 * Copyright 2005 Andres Salomon <dilinger@debian.org>
15 * Copyright 1999-2000 Frank Cusack <fcusack@fcusack.com>
16 *
17 * SPDX-License-Identifier: BSD-3-clause or GPL-1+
18 */
19
20 /* Get prototypes for all of the functions. */
21 #define PAM_SM_ACCOUNT
22 #define PAM_SM_AUTH
23 #define PAM_SM_PASSWORD
24 #define PAM_SM_SESSION
25
26 #include <config.h>
27 #include <portable/pam.h>
28 #include <portable/system.h>
29
30 #include <module/internal.h>
31 #include <pam-util/args.h>
32 #include <pam-util/logging.h>
33
34
35 /*
36 * The main PAM interface for authorization checking.
37 */
38 PAM_EXTERN int
pam_sm_acct_mgmt(pam_handle_t * pamh,int flags,int argc,const char ** argv)39 pam_sm_acct_mgmt(pam_handle_t *pamh, int flags, int argc, const char **argv)
40 {
41 struct pam_args *args;
42 int pamret;
43
44 args = pamk5_init(pamh, flags, argc, argv);
45 if (args == NULL) {
46 pamret = PAM_AUTH_ERR;
47 goto done;
48 }
49 pamret = pamk5_context_fetch(args);
50 ENTRY(args, flags);
51
52 /*
53 * Succeed if the user did not use krb5 to login. Ideally, we should
54 * probably fail and require that the user set up policy properly in their
55 * PAM configuration, but it's not common for the user to do so and that's
56 * not how other krb5 PAM modules work. If we don't do this, root logins
57 * with the system root password fail, which is a bad failure mode.
58 */
59 if (pamret != PAM_SUCCESS || args->config->ctx == NULL) {
60 pamret = PAM_IGNORE;
61 putil_debug(args, "skipping non-Kerberos login");
62 goto done;
63 }
64
65 pamret = pamk5_account(args);
66
67 done:
68 EXIT(args, pamret);
69 pamk5_free(args);
70 return pamret;
71 }
72
73
74 /*
75 * The main PAM interface for authentication. We also do authorization checks
76 * here, since many applications don't call pam_acct_mgmt.
77 */
78 PAM_EXTERN int
pam_sm_authenticate(pam_handle_t * pamh,int flags,int argc,const char ** argv)79 pam_sm_authenticate(pam_handle_t *pamh, int flags, int argc, const char **argv)
80 {
81 struct pam_args *args;
82 int pamret;
83
84 args = pamk5_init(pamh, flags, argc, argv);
85 if (args == NULL) {
86 pamret = PAM_SERVICE_ERR;
87 goto done;
88 }
89 ENTRY(args, flags);
90
91 pamret = pamk5_authenticate(args);
92
93 done:
94 EXIT(args, pamret);
95 pamk5_free(args);
96 return pamret;
97 }
98
99
100 /*
101 * The main PAM interface, in the auth stack, for establishing credentials
102 * obtained during authentication.
103 */
104 PAM_EXTERN int
pam_sm_setcred(pam_handle_t * pamh,int flags,int argc,const char ** argv)105 pam_sm_setcred(pam_handle_t *pamh, int flags, int argc, const char **argv)
106 {
107 struct pam_args *args;
108 bool refresh = false;
109 int pamret, allow;
110
111 args = pamk5_init(pamh, flags, argc, argv);
112 if (args == NULL) {
113 pamret = PAM_SERVICE_ERR;
114 goto done;
115 }
116 ENTRY(args, flags);
117
118 /*
119 * Special case. Just free the context data, which will destroy the
120 * ticket cache as well.
121 */
122 if (flags & PAM_DELETE_CRED) {
123 pamret = pam_set_data(pamh, "pam_krb5", NULL, NULL);
124 if (pamret != PAM_SUCCESS)
125 putil_err_pam(args, pamret, "cannot clear context data");
126 goto done;
127 }
128
129 /*
130 * Reinitialization requested, which means that rather than creating a new
131 * ticket cache and setting KRB5CCNAME, we should figure out the existing
132 * ticket cache and just refresh its tickets.
133 */
134 if (flags & (PAM_REINITIALIZE_CRED | PAM_REFRESH_CRED))
135 refresh = true;
136 if (refresh && (flags & PAM_ESTABLISH_CRED)) {
137 putil_err(args, "requested establish and refresh at the same time");
138 pamret = PAM_SERVICE_ERR;
139 goto done;
140 }
141 allow = PAM_REINITIALIZE_CRED | PAM_REFRESH_CRED | PAM_ESTABLISH_CRED;
142 if (!(flags & allow)) {
143 putil_err(args, "invalid pam_setcred flags %d", flags);
144 pamret = PAM_SERVICE_ERR;
145 goto done;
146 }
147
148 /* Do the work. */
149 pamret = pamk5_setcred(args, refresh);
150
151 /*
152 * Never return PAM_IGNORE from pam_setcred since this can confuse the
153 * Linux PAM library, at least for applications that call pam_setcred
154 * without pam_authenticate (possibly because authentication was done
155 * some other way), when used with jumps with the [] syntax. Since we
156 * do nothing in this case, and since the stack is already frozen from
157 * the auth group, success makes sense.
158 *
159 * Don't return an error here or the PAM stack will fail if pam-krb5 is
160 * used with [success=ok default=1], since jumps are treated as required
161 * during the second pass with pam_setcred.
162 */
163 if (pamret == PAM_IGNORE)
164 pamret = PAM_SUCCESS;
165
166 done:
167 EXIT(args, pamret);
168 pamk5_free(args);
169 return pamret;
170 }
171
172
173 /*
174 * The main PAM interface for password changing.
175 */
176 PAM_EXTERN int
pam_sm_chauthtok(pam_handle_t * pamh,int flags,int argc,const char ** argv)177 pam_sm_chauthtok(pam_handle_t *pamh, int flags, int argc, const char **argv)
178 {
179 struct pam_args *args;
180 int pamret;
181
182 args = pamk5_init(pamh, flags, argc, argv);
183 if (args == NULL) {
184 pamret = PAM_AUTHTOK_ERR;
185 goto done;
186 }
187 pamk5_context_fetch(args);
188 ENTRY(args, flags);
189
190 /* We only support password changes. */
191 if (!(flags & PAM_UPDATE_AUTHTOK) && !(flags & PAM_PRELIM_CHECK)) {
192 putil_err(args, "invalid pam_chauthtok flags %d", flags);
193 pamret = PAM_AUTHTOK_ERR;
194 goto done;
195 }
196
197 pamret = pamk5_password(args, (flags & PAM_PRELIM_CHECK) != 0);
198
199 done:
200 EXIT(args, pamret);
201 pamk5_free(args);
202 return pamret;
203 }
204
205
206 /*
207 * The main PAM interface for opening a session.
208 */
209 PAM_EXTERN int
pam_sm_open_session(pam_handle_t * pamh,int flags,int argc,const char ** argv)210 pam_sm_open_session(pam_handle_t *pamh, int flags, int argc, const char **argv)
211 {
212 struct pam_args *args;
213 int pamret;
214
215 args = pamk5_init(pamh, flags, argc, argv);
216 if (args == NULL) {
217 pamret = PAM_SERVICE_ERR;
218 goto done;
219 }
220 ENTRY(args, flags);
221 pamret = pamk5_setcred(args, 0);
222
223 done:
224 EXIT(args, pamret);
225 pamk5_free(args);
226 return pamret;
227 }
228
229
230 /*
231 * The main PAM interface for closing a session.
232 */
233 PAM_EXTERN int
pam_sm_close_session(pam_handle_t * pamh,int flags,int argc,const char ** argv)234 pam_sm_close_session(pam_handle_t *pamh, int flags, int argc,
235 const char **argv)
236 {
237 struct pam_args *args;
238 int pamret;
239
240 args = pamk5_init(pamh, flags, argc, argv);
241 if (args == NULL) {
242 pamret = PAM_SERVICE_ERR;
243 goto done;
244 }
245 ENTRY(args, flags);
246 pamret = pam_set_data(pamh, "pam_krb5", NULL, NULL);
247 if (pamret != PAM_SUCCESS)
248 putil_err_pam(args, pamret, "cannot clear context data");
249
250 done:
251 EXIT(args, pamret);
252 pamk5_free(args);
253 return pamret;
254 }
255
256
257 /* OpenPAM uses this macro to set up a table of entry points. */
258 #ifdef PAM_MODULE_ENTRY
259 PAM_MODULE_ENTRY("pam_krb5");
260 #endif
261