Deleted Added
full compact
pam_krb5.c (81477) pam_krb5.c (84218)
1/*-
2 * Copyright 2001 Mark R V Murray
3 * Copyright Frank Cusack fcusack@fcusack.com 1999-2000
4 * All rights reserved
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, and the entire permission notice in its entirety,
11 * including the disclaimer of warranties.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * 3. The name of the author may not be used to endorse or promote
16 * products derived from this software without specific prior
17 * written permission.
18 *
19 * ALTERNATIVELY, this product may be distributed under the terms of
20 * the GNU Public License, in which case the provisions of the GPL are
21 * required INSTEAD OF the above restrictions. (This clause is
22 * necessary due to a potential bad interaction between the GPL and
23 * the restrictions contained in a BSD-style copyright.)
24 *
25 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
26 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
27 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
28 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
29 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
30 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
31 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
32 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
33 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
34 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
35 * OF THE POSSIBILITY OF SUCH DAMAGE.
1/*-
2 * Copyright 2001 Mark R V Murray
3 * Copyright Frank Cusack fcusack@fcusack.com 1999-2000
4 * All rights reserved
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, and the entire permission notice in its entirety,
11 * including the disclaimer of warranties.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * 3. The name of the author may not be used to endorse or promote
16 * products derived from this software without specific prior
17 * written permission.
18 *
19 * ALTERNATIVELY, this product may be distributed under the terms of
20 * the GNU Public License, in which case the provisions of the GPL are
21 * required INSTEAD OF the above restrictions. (This clause is
22 * necessary due to a potential bad interaction between the GPL and
23 * the restrictions contained in a BSD-style copyright.)
24 *
25 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
26 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
27 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
28 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
29 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
30 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
31 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
32 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
33 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
34 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
35 * OF THE POSSIBILITY OF SUCH DAMAGE.
36 *
37 * $FreeBSD: head/lib/libpam/modules/pam_krb5/pam_krb5.c 81477 2001-08-10 19:24:34Z markm $
38 * ---------------------------------------------------------------------------
39 *
40 * This software may contain code from Naomaru Itoi:
41 *
42 * PAM-kerberos5 module Copyright notice.
43 * Naomaru Itoi <itoi@eecs.umich.edu>, June 24, 1997.
44 *
45 * ----------------------------------------------------------------------------
46 * COPYRIGHT (c) 1997
47 * THE REGENTS OF THE UNIVERSITY OF MICHIGAN
48 * ALL RIGHTS RESERVED
49 *
50 * PERMISSION IS GRANTED TO USE, COPY, CREATE DERIVATIVE WORKS AND REDISTRIBUTE
51 * THIS SOFTWARE AND SUCH DERIVATIVE WORKS FOR ANY PURPOSE, SO LONG AS THE NAME
52 * OF THE UNIVERSITY OF MICHIGAN IS NOT USED IN ANY ADVERTISING OR PUBLICITY
53 * PERTAINING TO THE USE OR DISTRIBUTION OF THIS SOFTWARE WITHOUT SPECIFIC,
54 * WRITTEN PRIOR AUTHORIZATION. IF THE ABOVE COPYRIGHT NOTICE OR ANY OTHER
55 * IDENTIFICATION OF THE UNIVERSITY OF MICHIGAN IS INCLUDED IN ANY COPY OF ANY
56 * PORTION OF THIS SOFTWARE, THEN THE DISCLAIMER BELOW MUST ALSO BE INCLUDED.
57 *
58 * THE SOFTWARE IS PROVIDED AS IS, WITHOUT REPRESENTATION FROM THE UNIVERSITY OF
59 * MICHIGAN AS TO ITS FITNESS FOR ANY PURPOSE, AND WITHOUT WARRANTY BY THE
60 * UNIVERSITY OF MICHIGAN OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING
61 * WITHOUT LIMITATION THE IMPLIED WARRANTIES OF MERCHANTABITILY AND FITNESS FOR A
62 * PARTICULAR PURPOSE. THE REGENTS OF THE UNIVERSITY OF MICHIGAN SHALL NOT BE
63 * LIABLE FOR ANY DAMAGES, INCLUDING SPECIAL, INDIRECT, INCIDENTAL, OR
64 * CONSEQUENTIAL DAMAGES, WITH RESPECT TO ANY CLAIM ARISING OUT OF OR IN
65 * CONNECTION WITH THE USE OF THE SOFTWARE, EVEN IF IT HAS BEEN OR IS HEREAFTER
66 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
67 *
68 * PAM-kerberos5 module is written based on PAM-kerberos4 module
69 * by Derrick J. Brashear and kerberos5-1.0pl1 by M.I.T. kerberos team.
70 * Permission to use, copy, modify, distribute this software is hereby
71 * granted, as long as it is granted by Derrick J. Brashear and
72 * M.I.T. kerberos team. Followings are their copyright information.
73 * ----------------------------------------------------------------------------
74 *
75 * This software may contain code from Derrick J. Brashear:
76 *
77 *
78 * Copyright (c) Derrick J. Brashear, 1996. All rights reserved
79 *
80 * Redistribution and use in source and binary forms, with or without
81 * modification, are permitted provided that the following conditions
82 * are met:
83 * 1. Redistributions of source code must retain the above copyright
84 * notice, and the entire permission notice in its entirety,
85 * including the disclaimer of warranties.
86 * 2. Redistributions in binary form must reproduce the above copyright
87 * notice, this list of conditions and the following disclaimer in the
88 * documentation and/or other materials provided with the distribution.
89 * 3. The name of the author may not be used to endorse or promote
90 * products derived from this software without specific prior
91 * written permission.
92 *
93 * ALTERNATIVELY, this product may be distributed under the terms of
94 * the GNU Public License, in which case the provisions of the GPL are
95 * required INSTEAD OF the above restrictions. (This clause is
96 * necessary due to a potential bad interaction between the GPL and
97 * the restrictions contained in a BSD-style copyright.)
98 *
99 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
100 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
101 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
102 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
103 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
104 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
105 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
106 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
107 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
108 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
109 * OF THE POSSIBILITY OF SUCH DAMAGE.
110 *
111 * ----------------------------------------------------------------------------
112 *
113 * This software may contain code from MIT Kerberos 5:
114 *
115 * Copyright Notice and Legal Administrivia
116 * ----------------------------------------
117 *
118 * Copyright (C) 1996 by the Massachusetts Institute of Technology.
119 *
120 * All rights reserved.
121 *
122 * Export of this software from the United States of America may require
123 * a specific license from the United States Government. It is the
124 * responsibility of any person or organization contemplating export to
125 * obtain such a license before exporting.
126 *
127 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
128 * distribute this software and its documentation for any purpose and
129 * without fee is hereby granted, provided that the above copyright
130 * notice appear in all copies and that both that copyright notice and
131 * this permission notice appear in supporting documentation, and that
132 * the name of M.I.T. not be used in advertising or publicity pertaining
133 * to distribution of the software without specific, written prior
134 * permission. M.I.T. makes no representations about the suitability of
135 * this software for any purpose. It is provided "as is" without express
136 * or implied warranty.
137 *
138 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
139 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
140 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
141 *
142 * Individual source code files are copyright MIT, Cygnus Support,
143 * OpenVision, Oracle, Sun Soft, and others.
144 *
145 * Project Athena, Athena, Athena MUSE, Discuss, Hesiod, Kerberos, Moira,
146 * and Zephyr are trademarks of the Massachusetts Institute of Technology
147 * (MIT). No commercial use of these trademarks may be made without
148 * prior written permission of MIT.
149 *
150 * "Commercial use" means use of a name in a product or other for-profit
151 * manner. It does NOT prevent a commercial firm from referring to the
152 * MIT trademarks in order to convey information (although in doing so,
153 * recognition of their trademark status should be given).
154 *
155 * The following copyright and permission notice applies to the
156 * OpenVision Kerberos Administration system located in kadmin/create,
157 * kadmin/dbutil, kadmin/passwd, kadmin/server, lib/kadm5, and portions
158 * of lib/rpc:
159 *
160 * Copyright, OpenVision Technologies, Inc., 1996, All Rights Reserved
161 *
162 * WARNING: Retrieving the OpenVision Kerberos Administration system
163 * source code, as described below, indicates your acceptance of the
164 * following terms. If you do not agree to the following terms, do not
165 * retrieve the OpenVision Kerberos administration system.
166 *
167 * You may freely use and distribute the Source Code and Object Code
168 * compiled from it, with or without modification, but this Source
169 * Code is provided to you "AS IS" EXCLUSIVE OF ANY WARRANTY,
170 * INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY OR
171 * FITNESS FOR A PARTICULAR PURPOSE, OR ANY OTHER WARRANTY, WHETHER
172 * EXPRESS OR IMPLIED. IN NO EVENT WILL OPENVISION HAVE ANY LIABILITY
173 * FOR ANY LOST PROFITS, LOSS OF DATA OR COSTS OF PROCUREMENT OF
174 * SUBSTITUTE GOODS OR SERVICES, OR FOR ANY SPECIAL, INDIRECT, OR
175 * CONSEQUENTIAL DAMAGES ARISING OUT OF THIS AGREEMENT, INCLUDING,
176 * WITHOUT LIMITATION, THOSE RESULTING FROM THE USE OF THE SOURCE
177 * CODE, OR THE FAILURE OF THE SOURCE CODE TO PERFORM, OR FOR ANY
178 * OTHER REASON.
179 *
180 * OpenVision retains all copyrights in the donated Source Code. OpenVision
181 * also retains copyright to derivative works of the Source Code, whether
182 * created by OpenVision or by a third party. The OpenVision copyright
183 * notice must be preserved if derivative works are made based on the
184 * donated Source Code.
185 *
186 * OpenVision Technologies, Inc. has donated this Kerberos
187 * Administration system to MIT for inclusion in the standard
188 * Kerberos 5 distribution. This donation underscores our
189 * commitment to continuing Kerberos technology development
190 * and our gratitude for the valuable work which has been
191 * performed by MIT and the Kerberos community.
192 *
193 */
194
36 * ---------------------------------------------------------------------------
37 *
38 * This software may contain code from Naomaru Itoi:
39 *
40 * PAM-kerberos5 module Copyright notice.
41 * Naomaru Itoi <itoi@eecs.umich.edu>, June 24, 1997.
42 *
43 * ----------------------------------------------------------------------------
44 * COPYRIGHT (c) 1997
45 * THE REGENTS OF THE UNIVERSITY OF MICHIGAN
46 * ALL RIGHTS RESERVED
47 *
48 * PERMISSION IS GRANTED TO USE, COPY, CREATE DERIVATIVE WORKS AND REDISTRIBUTE
49 * THIS SOFTWARE AND SUCH DERIVATIVE WORKS FOR ANY PURPOSE, SO LONG AS THE NAME
50 * OF THE UNIVERSITY OF MICHIGAN IS NOT USED IN ANY ADVERTISING OR PUBLICITY
51 * PERTAINING TO THE USE OR DISTRIBUTION OF THIS SOFTWARE WITHOUT SPECIFIC,
52 * WRITTEN PRIOR AUTHORIZATION. IF THE ABOVE COPYRIGHT NOTICE OR ANY OTHER
53 * IDENTIFICATION OF THE UNIVERSITY OF MICHIGAN IS INCLUDED IN ANY COPY OF ANY
54 * PORTION OF THIS SOFTWARE, THEN THE DISCLAIMER BELOW MUST ALSO BE INCLUDED.
55 *
56 * THE SOFTWARE IS PROVIDED AS IS, WITHOUT REPRESENTATION FROM THE UNIVERSITY OF
57 * MICHIGAN AS TO ITS FITNESS FOR ANY PURPOSE, AND WITHOUT WARRANTY BY THE
58 * UNIVERSITY OF MICHIGAN OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING
59 * WITHOUT LIMITATION THE IMPLIED WARRANTIES OF MERCHANTABITILY AND FITNESS FOR A
60 * PARTICULAR PURPOSE. THE REGENTS OF THE UNIVERSITY OF MICHIGAN SHALL NOT BE
61 * LIABLE FOR ANY DAMAGES, INCLUDING SPECIAL, INDIRECT, INCIDENTAL, OR
62 * CONSEQUENTIAL DAMAGES, WITH RESPECT TO ANY CLAIM ARISING OUT OF OR IN
63 * CONNECTION WITH THE USE OF THE SOFTWARE, EVEN IF IT HAS BEEN OR IS HEREAFTER
64 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
65 *
66 * PAM-kerberos5 module is written based on PAM-kerberos4 module
67 * by Derrick J. Brashear and kerberos5-1.0pl1 by M.I.T. kerberos team.
68 * Permission to use, copy, modify, distribute this software is hereby
69 * granted, as long as it is granted by Derrick J. Brashear and
70 * M.I.T. kerberos team. Followings are their copyright information.
71 * ----------------------------------------------------------------------------
72 *
73 * This software may contain code from Derrick J. Brashear:
74 *
75 *
76 * Copyright (c) Derrick J. Brashear, 1996. All rights reserved
77 *
78 * Redistribution and use in source and binary forms, with or without
79 * modification, are permitted provided that the following conditions
80 * are met:
81 * 1. Redistributions of source code must retain the above copyright
82 * notice, and the entire permission notice in its entirety,
83 * including the disclaimer of warranties.
84 * 2. Redistributions in binary form must reproduce the above copyright
85 * notice, this list of conditions and the following disclaimer in the
86 * documentation and/or other materials provided with the distribution.
87 * 3. The name of the author may not be used to endorse or promote
88 * products derived from this software without specific prior
89 * written permission.
90 *
91 * ALTERNATIVELY, this product may be distributed under the terms of
92 * the GNU Public License, in which case the provisions of the GPL are
93 * required INSTEAD OF the above restrictions. (This clause is
94 * necessary due to a potential bad interaction between the GPL and
95 * the restrictions contained in a BSD-style copyright.)
96 *
97 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
98 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
99 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
100 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
101 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
102 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
103 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
104 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
105 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
106 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
107 * OF THE POSSIBILITY OF SUCH DAMAGE.
108 *
109 * ----------------------------------------------------------------------------
110 *
111 * This software may contain code from MIT Kerberos 5:
112 *
113 * Copyright Notice and Legal Administrivia
114 * ----------------------------------------
115 *
116 * Copyright (C) 1996 by the Massachusetts Institute of Technology.
117 *
118 * All rights reserved.
119 *
120 * Export of this software from the United States of America may require
121 * a specific license from the United States Government. It is the
122 * responsibility of any person or organization contemplating export to
123 * obtain such a license before exporting.
124 *
125 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
126 * distribute this software and its documentation for any purpose and
127 * without fee is hereby granted, provided that the above copyright
128 * notice appear in all copies and that both that copyright notice and
129 * this permission notice appear in supporting documentation, and that
130 * the name of M.I.T. not be used in advertising or publicity pertaining
131 * to distribution of the software without specific, written prior
132 * permission. M.I.T. makes no representations about the suitability of
133 * this software for any purpose. It is provided "as is" without express
134 * or implied warranty.
135 *
136 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
137 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
138 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
139 *
140 * Individual source code files are copyright MIT, Cygnus Support,
141 * OpenVision, Oracle, Sun Soft, and others.
142 *
143 * Project Athena, Athena, Athena MUSE, Discuss, Hesiod, Kerberos, Moira,
144 * and Zephyr are trademarks of the Massachusetts Institute of Technology
145 * (MIT). No commercial use of these trademarks may be made without
146 * prior written permission of MIT.
147 *
148 * "Commercial use" means use of a name in a product or other for-profit
149 * manner. It does NOT prevent a commercial firm from referring to the
150 * MIT trademarks in order to convey information (although in doing so,
151 * recognition of their trademark status should be given).
152 *
153 * The following copyright and permission notice applies to the
154 * OpenVision Kerberos Administration system located in kadmin/create,
155 * kadmin/dbutil, kadmin/passwd, kadmin/server, lib/kadm5, and portions
156 * of lib/rpc:
157 *
158 * Copyright, OpenVision Technologies, Inc., 1996, All Rights Reserved
159 *
160 * WARNING: Retrieving the OpenVision Kerberos Administration system
161 * source code, as described below, indicates your acceptance of the
162 * following terms. If you do not agree to the following terms, do not
163 * retrieve the OpenVision Kerberos administration system.
164 *
165 * You may freely use and distribute the Source Code and Object Code
166 * compiled from it, with or without modification, but this Source
167 * Code is provided to you "AS IS" EXCLUSIVE OF ANY WARRANTY,
168 * INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY OR
169 * FITNESS FOR A PARTICULAR PURPOSE, OR ANY OTHER WARRANTY, WHETHER
170 * EXPRESS OR IMPLIED. IN NO EVENT WILL OPENVISION HAVE ANY LIABILITY
171 * FOR ANY LOST PROFITS, LOSS OF DATA OR COSTS OF PROCUREMENT OF
172 * SUBSTITUTE GOODS OR SERVICES, OR FOR ANY SPECIAL, INDIRECT, OR
173 * CONSEQUENTIAL DAMAGES ARISING OUT OF THIS AGREEMENT, INCLUDING,
174 * WITHOUT LIMITATION, THOSE RESULTING FROM THE USE OF THE SOURCE
175 * CODE, OR THE FAILURE OF THE SOURCE CODE TO PERFORM, OR FOR ANY
176 * OTHER REASON.
177 *
178 * OpenVision retains all copyrights in the donated Source Code. OpenVision
179 * also retains copyright to derivative works of the Source Code, whether
180 * created by OpenVision or by a third party. The OpenVision copyright
181 * notice must be preserved if derivative works are made based on the
182 * donated Source Code.
183 *
184 * OpenVision Technologies, Inc. has donated this Kerberos
185 * Administration system to MIT for inclusion in the standard
186 * Kerberos 5 distribution. This donation underscores our
187 * commitment to continuing Kerberos technology development
188 * and our gratitude for the valuable work which has been
189 * performed by MIT and the Kerberos community.
190 *
191 */
192
193#include <sys/cdefs.h>
194__FBSDID("$FreeBSD: head/lib/libpam/modules/pam_krb5/pam_krb5.c 84218 2001-09-30 22:11:06Z dillon $");
195
195#include <sys/types.h>
196#include <sys/stat.h>
197#include <errno.h>
198#include <limits.h>
199#include <pwd.h>
200#include <stdio.h>
201#include <stdlib.h>
202#include <strings.h>
203#include <syslog.h>
204#include <unistd.h>
205
206#include <krb5.h>
207#include <com_err.h>
208
209#define PAM_SM_AUTH
210#define PAM_SM_ACCOUNT
211#define PAM_SM_SESSION
212#define PAM_SM_PASSWORD
213
214#include <security/pam_appl.h>
215#include <security/pam_modules.h>
216
217#include "pam_mod_misc.h"
218
219#define COMPAT_HEIMDAL
220/* #define COMPAT_MIT */
221
222extern krb5_cc_ops krb5_mcc_ops;
223
224static int verify_krb_v5_tgt(krb5_context, krb5_ccache, char *, int);
225static void cleanup_cache(pam_handle_t *, void *, int);
226static const char *compat_princ_component(krb5_context, krb5_principal, int);
227static void compat_free_data_contents(krb5_context, krb5_data *);
228
229#define USER_PROMPT "Username: "
230#define PASSWORD_PROMPT "Password: "
231#define NEW_PASSWORD_PROMPT "New Password: "
232#define NEW_PASSWORD_PROMPT_2 "New Password (again): "
233
234enum { PAM_OPT_AUTH_AS_SELF=PAM_OPT_STD_MAX, PAM_OPT_CCACHE, PAM_OPT_FORWARDABLE, PAM_OPT_NO_CCACHE, PAM_OPT_REUSE_CCACHE };
235
236static struct opttab other_options[] = {
237 { "auth_as_self", PAM_OPT_AUTH_AS_SELF },
238 { "ccache", PAM_OPT_CCACHE },
239 { "forwardable", PAM_OPT_FORWARDABLE },
240 { "no_ccache", PAM_OPT_NO_CCACHE },
241 { "reuse_ccache", PAM_OPT_REUSE_CCACHE },
242 { NULL, 0 }
243};
244
245/*
246 * authentication management
247 */
248PAM_EXTERN int
249pam_sm_authenticate(pam_handle_t *pamh, int flags, int argc, const char **argv)
250{
251 krb5_error_code krbret;
252 krb5_context pam_context;
253 krb5_creds creds;
254 krb5_principal princ;
255 krb5_ccache ccache, ccache_check;
256 krb5_get_init_creds_opt opts;
257 struct options options;
258 struct passwd *pwd;
259 int retval;
260 const char *sourceuser, *user, *pass;
261 char *principal, *princ_name, *service, *cache_name, luser[32];
262
263 pam_std_option(&options, other_options, argc, argv);
264
265 PAM_LOG("Options processed");
266
267 retval = pam_get_user(pamh, &user, USER_PROMPT);
268 if (retval != PAM_SUCCESS)
269 PAM_RETURN(retval);
270
271 PAM_LOG("Got user: %s", user);
272
273 retval = pam_get_item(pamh, PAM_RUSER, (const void **)&sourceuser);
274 if (retval != PAM_SUCCESS)
275 PAM_RETURN(retval);
276
277 PAM_LOG("Got ruser: %s", sourceuser);
278
279 service = NULL;
280 pam_get_item(pamh, PAM_SERVICE, (const void **)&service);
281 if (service == NULL)
282 service = "unknown";
283
284 PAM_LOG("Got service: %s", service);
285
286 krbret = krb5_init_context(&pam_context);
287 if (krbret != 0) {
288 PAM_VERBOSE_ERROR("Kerberos 5 error");
289 PAM_RETURN(PAM_SERVICE_ERR);
290 }
291
292 PAM_LOG("Context initialised");
293
294 krb5_get_init_creds_opt_init(&opts);
295
296 if (pam_test_option(&options, PAM_OPT_FORWARDABLE, NULL))
297 krb5_get_init_creds_opt_set_forwardable(&opts, 1);
298
299 PAM_LOG("Credentials initialised");
300
301 krbret = krb5_cc_register(pam_context, &krb5_mcc_ops, FALSE);
302 if (krbret != 0 && krbret != KRB5_CC_TYPE_EXISTS) {
303 PAM_VERBOSE_ERROR("Kerberos 5 error");
304 retval = PAM_SERVICE_ERR;
305 goto cleanup3;
306 }
307
308 PAM_LOG("Done krb5_cc_register()");
309
310 /* Get principal name */
311 if (pam_test_option(&options, PAM_OPT_AUTH_AS_SELF, NULL))
312 asprintf(&principal, "%s/%s", sourceuser, user);
313 else
314 principal = strdup(user);
315
316 PAM_LOG("Created principal: %s", principal);
317
318 krbret = krb5_parse_name(pam_context, principal, &princ);
319 free(principal);
320 if (krbret != 0) {
321 PAM_LOG("Error krb5_parse_name(): %s", error_message(krbret));
322 PAM_VERBOSE_ERROR("Kerberos 5 error");
323 retval = PAM_SERVICE_ERR;
324 goto cleanup3;
325 }
326
327 PAM_LOG("Done krb5_parse_name()");
328
329 /* Now convert the principal name into something human readable */
330 princ_name = NULL;
331 krbret = krb5_unparse_name(pam_context, princ, &princ_name);
332 if (krbret != 0) {
333 PAM_LOG("Error krb5_unparse_name(): %s", error_message(krbret));
334 PAM_VERBOSE_ERROR("Kerberos 5 error");
335 retval = PAM_SERVICE_ERR;
336 goto cleanup2;
337 }
338
339 PAM_LOG("Got principal: %s", princ_name);
340
341 /* Get password */
342 retval = pam_get_pass(pamh, &pass, PASSWORD_PROMPT, &options);
343 if (retval != PAM_SUCCESS)
344 goto cleanup2;
345
346 PAM_LOG("Got password");
347
348 /* Verify the local user exists (AFTER getting the password) */
349 if (strchr(user, '@')) {
350 /* get a local account name for this principal */
351 krbret = krb5_aname_to_localname(pam_context, princ,
352 sizeof(luser), luser);
353 if (krbret != 0) {
354 PAM_VERBOSE_ERROR("Kerberos 5 error");
355 PAM_LOG("Error krb5_aname_to_localname(): %s",
356 error_message(krbret));
357 retval = PAM_USER_UNKNOWN;
358 goto cleanup2;
359 }
360
361 retval = pam_set_item(pamh, PAM_USER, luser);
362 if (retval != PAM_SUCCESS)
363 goto cleanup2;
364
365 retval = pam_get_item(pamh, PAM_USER, (const void **)&user);
366 if (retval != PAM_SUCCESS)
367 goto cleanup2;
368
369 PAM_LOG("PAM_USER Redone");
370 }
371
372 pwd = getpwnam(user);
373 if (pwd == NULL) {
374 retval = PAM_USER_UNKNOWN;
375 goto cleanup2;
376 }
377
378 PAM_LOG("Done getpwnam()");
379
380 /* Get a TGT */
381 memset(&creds, 0, sizeof(krb5_creds));
382 krbret = krb5_get_init_creds_password(pam_context, &creds, princ,
383 pass, NULL, pamh, 0, NULL, &opts);
384 if (krbret != 0) {
385 PAM_VERBOSE_ERROR("Kerberos 5 error");
386 PAM_LOG("Error krb5_get_init_creds_password(): %s",
387 error_message(krbret));
388 retval = PAM_AUTH_ERR;
389 goto cleanup2;
390 }
391
392 PAM_LOG("Got TGT");
393
394 /* Generate a unique cache_name */
395 asprintf(&cache_name, "MEMORY:/tmp/%s.%d", service, getpid());
396 krbret = krb5_cc_resolve(pam_context, cache_name, &ccache);
397 free(cache_name);
398 if (krbret != 0) {
399 PAM_VERBOSE_ERROR("Kerberos 5 error");
400 PAM_LOG("Error krb5_cc_resolve(): %s", error_message(krbret));
401 retval = PAM_SERVICE_ERR;
402 goto cleanup;
403 }
404 krbret = krb5_cc_initialize(pam_context, ccache, princ);
405 if (krbret != 0) {
406 PAM_VERBOSE_ERROR("Kerberos 5 error");
407 PAM_LOG("Error krb5_cc_initialize(): %s", error_message(krbret));
408 retval = PAM_SERVICE_ERR;
409 goto cleanup;
410 }
411 krbret = krb5_cc_store_cred(pam_context, ccache, &creds);
412 if (krbret != 0) {
413 PAM_VERBOSE_ERROR("Kerberos 5 error");
414 PAM_LOG("Error krb5_cc_store_cred(): %s", error_message(krbret));
415 krb5_cc_destroy(pam_context, ccache);
416 retval = PAM_SERVICE_ERR;
417 goto cleanup;
418 }
419
420 PAM_LOG("Credentials stashed");
421
422 /* Verify them */
423 if (verify_krb_v5_tgt(pam_context, ccache, service,
424 pam_test_option(&options, PAM_OPT_FORWARDABLE, NULL)) == -1) {
425 PAM_VERBOSE_ERROR("Kerberos 5 error");
426 krb5_cc_destroy(pam_context, ccache);
427 retval = PAM_AUTH_ERR;
428 goto cleanup;
429 }
430
431 PAM_LOG("Credentials stash verified");
432
433 retval = pam_get_data(pamh, "ccache", (const void **)&ccache_check);
434 if (retval == PAM_SUCCESS) {
435 krb5_cc_destroy(pam_context, ccache);
436 PAM_VERBOSE_ERROR("Kerberos 5 error");
437 retval = PAM_AUTH_ERR;
438 goto cleanup;
439 }
440
441 PAM_LOG("Credentials stash not pre-existing");
442
443 retval = pam_set_data(pamh, "ccache", ccache, cleanup_cache);
444 if (retval != 0) {
445 krb5_cc_destroy(pam_context, ccache);
446 PAM_VERBOSE_ERROR("Kerberos 5 error");
447 retval = PAM_SERVICE_ERR;
448 goto cleanup;
449 }
450
451 PAM_LOG("Credentials stash saved");
452
453cleanup:
454 krb5_free_cred_contents(pam_context, &creds);
455 PAM_LOG("Done cleanup");
456cleanup2:
457 krb5_free_principal(pam_context, princ);
458 PAM_LOG("Done cleanup2");
459cleanup3:
460 if (princ_name)
461 free(princ_name);
462
463 krb5_free_context(pam_context);
464
465 PAM_LOG("Done cleanup3");
466
467 if (retval != PAM_SUCCESS)
468 PAM_VERBOSE_ERROR("Kerberos 5 refuses you");
469
470 PAM_RETURN(retval);
471}
472
473PAM_EXTERN int
474pam_sm_setcred(pam_handle_t *pamh, int flags, int argc, const char **argv)
475{
476
477 krb5_error_code krbret;
478 krb5_context pam_context;
479 krb5_principal princ;
480 krb5_creds creds;
481 krb5_ccache ccache_temp, ccache_perm;
482 krb5_cc_cursor cursor;
483 struct options options;
484 struct passwd *pwd = NULL;
485 int retval;
486 char *user;
487 char *cache_name, *cache_env_name, *p, *q;
488
489 uid_t euid;
490 gid_t egid;
491
492 pam_std_option(&options, other_options, argc, argv);
493
494 PAM_LOG("Options processed");
495
496 if (flags & PAM_DELETE_CRED)
497 PAM_RETURN(PAM_SUCCESS);
498
499 if (flags & PAM_REFRESH_CRED)
500 PAM_RETURN(PAM_SUCCESS);
501
502 if (flags & PAM_REINITIALIZE_CRED)
503 PAM_RETURN(PAM_SUCCESS);
504
505 if (!(flags & PAM_ESTABLISH_CRED))
506 PAM_RETURN(PAM_SERVICE_ERR);
507
508 PAM_LOG("Establishing credentials");
509
510 /* Get username */
511 retval = pam_get_item(pamh, PAM_USER, (const void **)&user);
512 if (retval != PAM_SUCCESS)
513 PAM_RETURN(retval);
514
515 PAM_LOG("Got user: %s", user);
516
517 krbret = krb5_init_context(&pam_context);
518 if (krbret != 0) {
519 PAM_LOG("Error krb5_init_context(): %s", error_message(krbret));
520 PAM_RETURN(PAM_SERVICE_ERR);
521 }
522
523 PAM_LOG("Context initialised");
524
525 euid = geteuid(); /* Usually 0 */
526 egid = getegid();
527
528 PAM_LOG("Got euid, egid: %d %d", euid, egid);
529
530 /* Retrieve the cache name */
531 retval = pam_get_data(pamh, "ccache", (const void **)&ccache_temp);
532 if (retval != PAM_SUCCESS)
533 goto cleanup3;
534
535 /* Get the uid. This should exist. */
536 pwd = getpwnam(user);
537 if (pwd == NULL) {
538 retval = PAM_USER_UNKNOWN;
539 goto cleanup3;
540 }
541
542 PAM_LOG("Done getpwnam()");
543
544 /* Avoid following a symlink as root */
545 if (setegid(pwd->pw_gid)) {
546 retval = PAM_SERVICE_ERR;
547 goto cleanup3;
548 }
549 if (seteuid(pwd->pw_uid)) {
550 retval = PAM_SERVICE_ERR;
551 goto cleanup3;
552 }
553
554 PAM_LOG("Done setegid() & seteuid()");
555
556 /* Get the cache name */
557 cache_name = NULL;
558 pam_test_option(&options, PAM_OPT_CCACHE, &cache_name);
559 if (cache_name == NULL)
560 asprintf(&cache_name, "FILE:/tmp/krb5cc_%d", pwd->pw_uid);
561
562 p = calloc(PATH_MAX + 16, sizeof(char));
563 q = cache_name;
564
565 if (p == NULL) {
566 PAM_LOG("Error malloc(): failure");
567 retval = PAM_BUF_ERR;
568 goto cleanup3;
569 }
570 cache_name = p;
571
572 /* convert %u and %p */
573 while (*q) {
574 if (*q == '%') {
575 q++;
576 if (*q == 'u') {
577 sprintf(p, "%d", pwd->pw_uid);
578 p += strlen(p);
579 }
580 else if (*q == 'p') {
581 sprintf(p, "%d", getpid());
582 p += strlen(p);
583 }
584 else {
585 /* Not a special token */
586 *p++ = '%';
587 q--;
588 }
589 q++;
590 }
591 else {
592 *p++ = *q++;
593 }
594 }
595
596 PAM_LOG("Got cache_name: %s", cache_name);
597
598 /* Initialize the new ccache */
599 krbret = krb5_cc_get_principal(pam_context, ccache_temp, &princ);
600 if (krbret != 0) {
601 PAM_LOG("Error krb5_cc_get_principal(): %s",
602 error_message(krbret));
603 retval = PAM_SERVICE_ERR;
604 goto cleanup3;
605 }
606 krbret = krb5_cc_resolve(pam_context, cache_name, &ccache_perm);
607 if (krbret != 0) {
608 PAM_LOG("Error krb5_cc_resolve(): %s", error_message(krbret));
609 retval = PAM_SERVICE_ERR;
610 goto cleanup2;
611 }
612 krbret = krb5_cc_initialize(pam_context, ccache_perm, princ);
613 if (krbret != 0) {
614 PAM_LOG("Error krb5_cc_initialize(): %s", error_message(krbret));
615 retval = PAM_SERVICE_ERR;
616 goto cleanup2;
617 }
618
619 PAM_LOG("Cache initialised");
620
621 /* Prepare for iteration over creds */
622 krbret = krb5_cc_start_seq_get(pam_context, ccache_temp, &cursor);
623 if (krbret != 0) {
624 PAM_LOG("Error krb5_cc_start_seq_get(): %s", error_message(krbret));
625 krb5_cc_destroy(pam_context, ccache_perm);
626 retval = PAM_SERVICE_ERR;
627 goto cleanup2;
628 }
629
630 PAM_LOG("Prepared for iteration");
631
632 /* Copy the creds (should be two of them) */
633 while ((krbret = krb5_cc_next_cred(pam_context, ccache_temp,
634 &cursor, &creds) == 0)) {
635 krbret = krb5_cc_store_cred(pam_context, ccache_perm, &creds);
636 if (krbret != 0) {
637 PAM_LOG("Error krb5_cc_store_cred(): %s",
638 error_message(krbret));
639 krb5_cc_destroy(pam_context, ccache_perm);
640 krb5_free_cred_contents(pam_context, &creds);
641 retval = PAM_SERVICE_ERR;
642 goto cleanup2;
643 }
644 krb5_free_cred_contents(pam_context, &creds);
645 PAM_LOG("Iteration");
646 }
647 krb5_cc_end_seq_get(pam_context, ccache_temp, &cursor);
648
649 PAM_LOG("Done iterating");
650
651 if (strstr(cache_name, "FILE:") == cache_name) {
652 if (chown(&cache_name[5], pwd->pw_uid, pwd->pw_gid) == -1) {
653 PAM_LOG("Error chown(): %s", strerror(errno));
654 krb5_cc_destroy(pam_context, ccache_perm);
655 retval = PAM_SERVICE_ERR;
656 goto cleanup2;
657 }
658 PAM_LOG("Done chown()");
659
660 if (chmod(&cache_name[5], (S_IRUSR | S_IWUSR)) == -1) {
661 PAM_LOG("Error chmod(): %s", strerror(errno));
662 krb5_cc_destroy(pam_context, ccache_perm);
663 retval = PAM_SERVICE_ERR;
664 goto cleanup2;
665 }
666 PAM_LOG("Done chmod()");
667 }
668
669 krb5_cc_close(pam_context, ccache_perm);
670
671 PAM_LOG("Cache closed");
672
673 cache_env_name = malloc(strlen(cache_name) + 12);
674 if (!cache_env_name) {
675 PAM_LOG("Error malloc(): failure");
676 krb5_cc_destroy(pam_context, ccache_perm);
677 retval = PAM_BUF_ERR;
678 goto cleanup2;
679 }
680
681 sprintf(cache_env_name, "KRB5CCNAME=%s", cache_name);
682 if ((retval = pam_putenv(pamh, cache_env_name)) != 0) {
683 PAM_LOG("Error pam_putenv(): %s", pam_strerror(pamh, retval));
684 krb5_cc_destroy(pam_context, ccache_perm);
685 retval = PAM_SERVICE_ERR;
686 goto cleanup2;
687 }
688
689 PAM_LOG("Environment done: KRB5CCNAME=%s", cache_name);
690
691cleanup2:
692 krb5_free_principal(pam_context, princ);
693 PAM_LOG("Done cleanup2");
694cleanup3:
695 krb5_free_context(pam_context);
696 PAM_LOG("Done cleanup3");
697
698 seteuid(euid);
699 setegid(egid);
700
701 PAM_LOG("Done seteuid() & setegid()");
702
703 PAM_RETURN(retval);
704}
705
706/*
707 * account management
708 */
709PAM_EXTERN int
710pam_sm_acct_mgmt(pam_handle_t *pamh, int flags, int argc, const char **argv)
711{
712 krb5_error_code krbret;
713 krb5_context pam_context;
714 krb5_ccache ccache;
715 krb5_principal princ;
716 struct options options;
717 int retval;
718 const char *user;
719
720 pam_std_option(&options, other_options, argc, argv);
721
722 PAM_LOG("Options processed");
723
724 retval = pam_get_item(pamh, PAM_USER, (const void **)&user);
725 if (retval != PAM_SUCCESS)
726 PAM_RETURN(retval);
727
728 PAM_LOG("Got user: %s", user);
729
730 retval = pam_get_data(pamh, "ccache", (const void **)&ccache);
731 if (retval != PAM_SUCCESS)
732 PAM_RETURN(PAM_SUCCESS);
733
734 PAM_LOG("Got ccache");
735
736 krbret = krb5_init_context(&pam_context);
737 if (krbret != 0) {
738 PAM_LOG("Error krb5_init_context(): %s", error_message(krbret));
739 PAM_RETURN(PAM_PERM_DENIED);
740 }
741
742 PAM_LOG("Context initialised");
743
744 krbret = krb5_cc_get_principal(pam_context, ccache, &princ);
745 if (krbret != 0) {
746 PAM_LOG("Error krb5_cc_get_principal(): %s", error_message(krbret));
747 retval = PAM_PERM_DENIED;;
748 goto cleanup;
749 }
750
751 PAM_LOG("Got principal");
752
753 if (krb5_kuserok(pam_context, princ, user))
754 retval = PAM_SUCCESS;
755 else
756 retval = PAM_PERM_DENIED;
757 krb5_free_principal(pam_context, princ);
758
759 PAM_LOG("Done kuserok()");
760
761cleanup:
762 krb5_free_context(pam_context);
763 PAM_LOG("Done cleanup");
764
765 PAM_RETURN(retval);
766
767}
768
769/*
770 * session management
771 *
772 * logging only
773 */
774PAM_EXTERN int
775pam_sm_open_session(pam_handle_t *pamh, int flags, int argc, const char **argv)
776{
777 struct options options;
778
779 pam_std_option(&options, NULL, argc, argv);
780
781 PAM_LOG("Options processed");
782
783 PAM_RETURN(PAM_SUCCESS);
784}
785
786PAM_EXTERN int
787pam_sm_close_session(pam_handle_t *pamh, int flags, int argc, const char **argv)
788{
789 struct options options;
790
791 pam_std_option(&options, NULL, argc, argv);
792
793 PAM_LOG("Options processed");
794
795 PAM_RETURN(PAM_SUCCESS);
796}
797
798/*
799 * password management
800 */
801PAM_EXTERN int
802pam_sm_chauthtok(pam_handle_t *pamh, int flags, int argc, const char **argv)
803{
804 krb5_error_code krbret;
805 krb5_context pam_context;
806 krb5_creds creds;
807 krb5_principal princ;
808 krb5_get_init_creds_opt opts;
809 krb5_data result_code_string, result_string;
810 struct options options;
811 int result_code, retval;
812 const char *user, *pass, *pass2;
813 char *princ_name;
814
815 pam_std_option(&options, other_options, argc, argv);
816
817 PAM_LOG("Options processed");
818
819 if (!(flags & PAM_UPDATE_AUTHTOK))
820 PAM_RETURN(PAM_AUTHTOK_ERR);
821
822 retval = pam_get_item(pamh, PAM_USER, (const void **)&user);
823 if (retval != PAM_SUCCESS)
824 PAM_RETURN(retval);
825
826 PAM_LOG("Got user: %s", user);
827
828 krbret = krb5_init_context(&pam_context);
829 if (krbret != 0) {
830 PAM_LOG("Error krb5_init_context(): %s", error_message(krbret));
831 PAM_RETURN(PAM_SERVICE_ERR);
832 }
833
834 PAM_LOG("Context initialised");
835
836 krb5_get_init_creds_opt_init(&opts);
837
838 PAM_LOG("Credentials options initialised");
839
840 /* Get principal name */
841 krbret = krb5_parse_name(pam_context, user, &princ);
842 if (krbret != 0) {
843 PAM_LOG("Error krb5_parse_name(): %s", error_message(krbret));
844 retval = PAM_USER_UNKNOWN;
845 goto cleanup3;
846 }
847
848 /* Now convert the principal name into something human readable */
849 princ_name = NULL;
850 krbret = krb5_unparse_name(pam_context, princ, &princ_name);
851 if (krbret != 0) {
852 PAM_LOG("Error krb5_unparse_name(): %s", error_message(krbret));
853 retval = PAM_SERVICE_ERR;
854 goto cleanup2;
855 }
856
857 PAM_LOG("Got principal: %s", princ_name);
858
859 /* Get password */
860 retval = pam_get_pass(pamh, &pass, PASSWORD_PROMPT, &options);
861 if (retval != PAM_SUCCESS)
862 goto cleanup2;
863
864 PAM_LOG("Got password");
865
866 memset(&creds, 0, sizeof(krb5_creds));
867 krbret = krb5_get_init_creds_password(pam_context, &creds, princ,
868 pass, NULL, pamh, 0, "kadmin/changepw", &opts);
869 if (krbret != 0) {
870 PAM_LOG("Error krb5_get_init_creds_password()",
871 error_message(krbret));
872 retval = PAM_AUTH_ERR;
873 goto cleanup2;
874 }
875
876 PAM_LOG("Credentials established");
877
878 /* Now get the new password */
879 retval = pam_get_pass(pamh, &pass, NEW_PASSWORD_PROMPT, &options);
880 if (retval != PAM_SUCCESS)
881 goto cleanup;
882
883 retval = pam_get_pass(pamh, &pass2, NEW_PASSWORD_PROMPT_2, &options);
884 if (retval != PAM_SUCCESS)
885 goto cleanup;
886
887 PAM_LOG("Got new password twice");
888
889 if (strcmp(pass, pass2) != 0) {
890 PAM_LOG("Error strcmp(): passwords are different");
891 retval = PAM_AUTHTOK_ERR;
892 goto cleanup;
893 }
894
895 PAM_LOG("New passwords are the same");
896
897 /* Change it */
898 krbret = krb5_change_password(pam_context, &creds, (char *)pass,
899 &result_code, &result_code_string, &result_string);
900 if (krbret != 0) {
901 PAM_LOG("Error krb5_change_password(): %s",
902 error_message(krbret));
903 retval = PAM_AUTHTOK_ERR;
904 goto cleanup;
905 }
906 if (result_code) {
907 PAM_LOG("Error krb5_change_password(): (result_code)");
908 retval = PAM_AUTHTOK_ERR;
909 goto cleanup;
910 }
911
912 PAM_LOG("Password changed");
913
914 if (result_string.data)
915 free(result_string.data);
916 if (result_code_string.data)
917 free(result_code_string.data);
918
919cleanup:
920 krb5_free_cred_contents(pam_context, &creds);
921 PAM_LOG("Done cleanup");
922cleanup2:
923 krb5_free_principal(pam_context, princ);
924 PAM_LOG("Done cleanup2");
925cleanup3:
926 if (princ_name)
927 free(princ_name);
928
929 krb5_free_context(pam_context);
930
931 PAM_LOG("Done cleanup3");
932
933 PAM_RETURN(retval);
934}
935
936PAM_MODULE_ENTRY("pam_krb5");
937
938/*
939 * This routine with some modification is from the MIT V5B6 appl/bsd/login.c
940 * Modified by Sam Hartman <hartmans@mit.edu> to support PAM services
941 * for Debian.
942 *
943 * Verify the Kerberos ticket-granting ticket just retrieved for the
944 * user. If the Kerberos server doesn't respond, assume the user is
945 * trying to fake us out (since we DID just get a TGT from what is
946 * supposedly our KDC). If the host/<host> service is unknown (i.e.,
947 * the local keytab doesn't have it), and we cannot find another
948 * service we do have, let her in.
949 *
950 * Returns 1 for confirmation, -1 for failure, 0 for uncertainty.
951 */
952static int
953verify_krb_v5_tgt(krb5_context context, krb5_ccache ccache,
954 char *pam_service, int debug)
955{
956 krb5_error_code retval;
957 krb5_principal princ;
958 krb5_keyblock *keyblock;
959 krb5_data packet;
960 krb5_auth_context auth_context;
961 char phost[BUFSIZ], *services[3], **service;
962
963 packet.data = 0;
964
965 /* If possible we want to try and verify the ticket we have
966 * received against a keytab. We will try multiple service
967 * principals, including at least the host principal and the PAM
968 * service principal. The host principal is preferred because access
969 * to that key is generally sufficient to compromise root, while the
970 * service key for this PAM service may be less carefully guarded.
971 * It is important to check the keytab first before the KDC so we do
972 * not get spoofed by a fake KDC.
973 */
974 services[0] = "host";
975 services[1] = pam_service;
976 services[2] = NULL;
977 keyblock = 0;
978 retval = -1;
979 for (service = &services[0]; *service != NULL; service++) {
980 retval = krb5_sname_to_principal(context, NULL, *service,
981 KRB5_NT_SRV_HST, &princ);
982 if (retval != 0) {
983 if (debug)
984 syslog(LOG_DEBUG, "pam_krb5: verify_krb_v5_tgt(): %s: %s", "krb5_sname_to_principal()", error_message(retval));
985 return -1;
986 }
987
988 /* Extract the name directly. */
989 strncpy(phost, compat_princ_component(context, princ, 1),
990 BUFSIZ);
991 phost[BUFSIZ - 1] = '\0';
992
993 /*
994 * Do we have service/<host> keys?
995 * (use default/configured keytab, kvno IGNORE_VNO to get the
996 * first match, and ignore enctype.)
997 */
998 retval = krb5_kt_read_service_key(context, NULL, princ, 0, 0,
999 &keyblock);
1000 if (retval != 0)
1001 continue;
1002 break;
1003 }
1004 if (retval != 0) { /* failed to find key */
1005 /* Keytab or service key does not exist */
1006 if (debug)
1007 syslog(LOG_DEBUG, "pam_krb5: verify_krb_v5_tgt(): %s: %s", "krb5_kt_read_service_key()", error_message(retval));
1008 retval = 0;
1009 goto cleanup;
1010 }
1011 if (keyblock)
1012 krb5_free_keyblock(context, keyblock);
1013
1014 /* Talk to the kdc and construct the ticket. */
1015 auth_context = NULL;
1016 retval = krb5_mk_req(context, &auth_context, 0, *service, phost,
1017 NULL, ccache, &packet);
1018 if (auth_context) {
1019 krb5_auth_con_free(context, auth_context);
1020 auth_context = NULL; /* setup for rd_req */
1021 }
1022 if (retval) {
1023 if (debug)
1024 syslog(LOG_DEBUG, "pam_krb5: verify_krb_v5_tgt(): %s: %s", "krb5_mk_req()", error_message(retval));
1025 retval = -1;
1026 goto cleanup;
1027 }
1028
1029 /* Try to use the ticket. */
1030 retval = krb5_rd_req(context, &auth_context, &packet, princ, NULL,
1031 NULL, NULL);
1032 if (retval) {
1033 if (debug)
1034 syslog(LOG_DEBUG, "pam_krb5: verify_krb_v5_tgt(): %s: %s", "krb5_rd_req()", error_message(retval));
1035 retval = -1;
1036 }
1037 else
1038 retval = 1;
1039
1040cleanup:
1041 if (packet.data)
1042 compat_free_data_contents(context, &packet);
1043 krb5_free_principal(context, princ);
1044 return retval;
1045}
1046
1047/* Free the memory for cache_name. Called by pam_end() */
1048static void
1049cleanup_cache(pam_handle_t *pamh, void *data, int pam_end_status)
1050{
1051 krb5_context pam_context;
1052 krb5_ccache ccache;
1053
1054 if (krb5_init_context(&pam_context))
1055 return;
1056
1057 ccache = (krb5_ccache)data;
1058 krb5_cc_destroy(pam_context, ccache);
1059 krb5_free_context(pam_context);
1060}
1061
1062#ifdef COMPAT_HEIMDAL
1063#ifdef COMPAT_MIT
1064#error This cannot be MIT and Heimdal compatible!
1065#endif
1066#endif
1067
1068#ifndef COMPAT_HEIMDAL
1069#ifndef COMPAT_MIT
1070#error One of COMPAT_MIT and COMPAT_HEIMDAL must be specified!
1071#endif
1072#endif
1073
1074#ifdef COMPAT_HEIMDAL
1075static const char *
1076compat_princ_component(krb5_context context, krb5_principal princ, int n)
1077{
1078 return princ->name.name_string.val[n];
1079}
1080
1081static void
1082compat_free_data_contents(krb5_context context, krb5_data * data)
1083{
1084 krb5_xfree(data->data);
1085}
1086#endif
1087
1088#ifdef COMPAT_MIT
1089static const char *
1090compat_princ_component(krb5_context context, krb5_principal princ, int n)
1091{
1092 return krb5_princ_component(context, princ, n)->data;
1093}
1094
1095static void
1096compat_free_data_contents(krb5_context context, krb5_data * data)
1097{
1098 krb5_free_data_contents(context, data);
1099}
1100#endif
196#include <sys/types.h>
197#include <sys/stat.h>
198#include <errno.h>
199#include <limits.h>
200#include <pwd.h>
201#include <stdio.h>
202#include <stdlib.h>
203#include <strings.h>
204#include <syslog.h>
205#include <unistd.h>
206
207#include <krb5.h>
208#include <com_err.h>
209
210#define PAM_SM_AUTH
211#define PAM_SM_ACCOUNT
212#define PAM_SM_SESSION
213#define PAM_SM_PASSWORD
214
215#include <security/pam_appl.h>
216#include <security/pam_modules.h>
217
218#include "pam_mod_misc.h"
219
220#define COMPAT_HEIMDAL
221/* #define COMPAT_MIT */
222
223extern krb5_cc_ops krb5_mcc_ops;
224
225static int verify_krb_v5_tgt(krb5_context, krb5_ccache, char *, int);
226static void cleanup_cache(pam_handle_t *, void *, int);
227static const char *compat_princ_component(krb5_context, krb5_principal, int);
228static void compat_free_data_contents(krb5_context, krb5_data *);
229
230#define USER_PROMPT "Username: "
231#define PASSWORD_PROMPT "Password: "
232#define NEW_PASSWORD_PROMPT "New Password: "
233#define NEW_PASSWORD_PROMPT_2 "New Password (again): "
234
235enum { PAM_OPT_AUTH_AS_SELF=PAM_OPT_STD_MAX, PAM_OPT_CCACHE, PAM_OPT_FORWARDABLE, PAM_OPT_NO_CCACHE, PAM_OPT_REUSE_CCACHE };
236
237static struct opttab other_options[] = {
238 { "auth_as_self", PAM_OPT_AUTH_AS_SELF },
239 { "ccache", PAM_OPT_CCACHE },
240 { "forwardable", PAM_OPT_FORWARDABLE },
241 { "no_ccache", PAM_OPT_NO_CCACHE },
242 { "reuse_ccache", PAM_OPT_REUSE_CCACHE },
243 { NULL, 0 }
244};
245
246/*
247 * authentication management
248 */
249PAM_EXTERN int
250pam_sm_authenticate(pam_handle_t *pamh, int flags, int argc, const char **argv)
251{
252 krb5_error_code krbret;
253 krb5_context pam_context;
254 krb5_creds creds;
255 krb5_principal princ;
256 krb5_ccache ccache, ccache_check;
257 krb5_get_init_creds_opt opts;
258 struct options options;
259 struct passwd *pwd;
260 int retval;
261 const char *sourceuser, *user, *pass;
262 char *principal, *princ_name, *service, *cache_name, luser[32];
263
264 pam_std_option(&options, other_options, argc, argv);
265
266 PAM_LOG("Options processed");
267
268 retval = pam_get_user(pamh, &user, USER_PROMPT);
269 if (retval != PAM_SUCCESS)
270 PAM_RETURN(retval);
271
272 PAM_LOG("Got user: %s", user);
273
274 retval = pam_get_item(pamh, PAM_RUSER, (const void **)&sourceuser);
275 if (retval != PAM_SUCCESS)
276 PAM_RETURN(retval);
277
278 PAM_LOG("Got ruser: %s", sourceuser);
279
280 service = NULL;
281 pam_get_item(pamh, PAM_SERVICE, (const void **)&service);
282 if (service == NULL)
283 service = "unknown";
284
285 PAM_LOG("Got service: %s", service);
286
287 krbret = krb5_init_context(&pam_context);
288 if (krbret != 0) {
289 PAM_VERBOSE_ERROR("Kerberos 5 error");
290 PAM_RETURN(PAM_SERVICE_ERR);
291 }
292
293 PAM_LOG("Context initialised");
294
295 krb5_get_init_creds_opt_init(&opts);
296
297 if (pam_test_option(&options, PAM_OPT_FORWARDABLE, NULL))
298 krb5_get_init_creds_opt_set_forwardable(&opts, 1);
299
300 PAM_LOG("Credentials initialised");
301
302 krbret = krb5_cc_register(pam_context, &krb5_mcc_ops, FALSE);
303 if (krbret != 0 && krbret != KRB5_CC_TYPE_EXISTS) {
304 PAM_VERBOSE_ERROR("Kerberos 5 error");
305 retval = PAM_SERVICE_ERR;
306 goto cleanup3;
307 }
308
309 PAM_LOG("Done krb5_cc_register()");
310
311 /* Get principal name */
312 if (pam_test_option(&options, PAM_OPT_AUTH_AS_SELF, NULL))
313 asprintf(&principal, "%s/%s", sourceuser, user);
314 else
315 principal = strdup(user);
316
317 PAM_LOG("Created principal: %s", principal);
318
319 krbret = krb5_parse_name(pam_context, principal, &princ);
320 free(principal);
321 if (krbret != 0) {
322 PAM_LOG("Error krb5_parse_name(): %s", error_message(krbret));
323 PAM_VERBOSE_ERROR("Kerberos 5 error");
324 retval = PAM_SERVICE_ERR;
325 goto cleanup3;
326 }
327
328 PAM_LOG("Done krb5_parse_name()");
329
330 /* Now convert the principal name into something human readable */
331 princ_name = NULL;
332 krbret = krb5_unparse_name(pam_context, princ, &princ_name);
333 if (krbret != 0) {
334 PAM_LOG("Error krb5_unparse_name(): %s", error_message(krbret));
335 PAM_VERBOSE_ERROR("Kerberos 5 error");
336 retval = PAM_SERVICE_ERR;
337 goto cleanup2;
338 }
339
340 PAM_LOG("Got principal: %s", princ_name);
341
342 /* Get password */
343 retval = pam_get_pass(pamh, &pass, PASSWORD_PROMPT, &options);
344 if (retval != PAM_SUCCESS)
345 goto cleanup2;
346
347 PAM_LOG("Got password");
348
349 /* Verify the local user exists (AFTER getting the password) */
350 if (strchr(user, '@')) {
351 /* get a local account name for this principal */
352 krbret = krb5_aname_to_localname(pam_context, princ,
353 sizeof(luser), luser);
354 if (krbret != 0) {
355 PAM_VERBOSE_ERROR("Kerberos 5 error");
356 PAM_LOG("Error krb5_aname_to_localname(): %s",
357 error_message(krbret));
358 retval = PAM_USER_UNKNOWN;
359 goto cleanup2;
360 }
361
362 retval = pam_set_item(pamh, PAM_USER, luser);
363 if (retval != PAM_SUCCESS)
364 goto cleanup2;
365
366 retval = pam_get_item(pamh, PAM_USER, (const void **)&user);
367 if (retval != PAM_SUCCESS)
368 goto cleanup2;
369
370 PAM_LOG("PAM_USER Redone");
371 }
372
373 pwd = getpwnam(user);
374 if (pwd == NULL) {
375 retval = PAM_USER_UNKNOWN;
376 goto cleanup2;
377 }
378
379 PAM_LOG("Done getpwnam()");
380
381 /* Get a TGT */
382 memset(&creds, 0, sizeof(krb5_creds));
383 krbret = krb5_get_init_creds_password(pam_context, &creds, princ,
384 pass, NULL, pamh, 0, NULL, &opts);
385 if (krbret != 0) {
386 PAM_VERBOSE_ERROR("Kerberos 5 error");
387 PAM_LOG("Error krb5_get_init_creds_password(): %s",
388 error_message(krbret));
389 retval = PAM_AUTH_ERR;
390 goto cleanup2;
391 }
392
393 PAM_LOG("Got TGT");
394
395 /* Generate a unique cache_name */
396 asprintf(&cache_name, "MEMORY:/tmp/%s.%d", service, getpid());
397 krbret = krb5_cc_resolve(pam_context, cache_name, &ccache);
398 free(cache_name);
399 if (krbret != 0) {
400 PAM_VERBOSE_ERROR("Kerberos 5 error");
401 PAM_LOG("Error krb5_cc_resolve(): %s", error_message(krbret));
402 retval = PAM_SERVICE_ERR;
403 goto cleanup;
404 }
405 krbret = krb5_cc_initialize(pam_context, ccache, princ);
406 if (krbret != 0) {
407 PAM_VERBOSE_ERROR("Kerberos 5 error");
408 PAM_LOG("Error krb5_cc_initialize(): %s", error_message(krbret));
409 retval = PAM_SERVICE_ERR;
410 goto cleanup;
411 }
412 krbret = krb5_cc_store_cred(pam_context, ccache, &creds);
413 if (krbret != 0) {
414 PAM_VERBOSE_ERROR("Kerberos 5 error");
415 PAM_LOG("Error krb5_cc_store_cred(): %s", error_message(krbret));
416 krb5_cc_destroy(pam_context, ccache);
417 retval = PAM_SERVICE_ERR;
418 goto cleanup;
419 }
420
421 PAM_LOG("Credentials stashed");
422
423 /* Verify them */
424 if (verify_krb_v5_tgt(pam_context, ccache, service,
425 pam_test_option(&options, PAM_OPT_FORWARDABLE, NULL)) == -1) {
426 PAM_VERBOSE_ERROR("Kerberos 5 error");
427 krb5_cc_destroy(pam_context, ccache);
428 retval = PAM_AUTH_ERR;
429 goto cleanup;
430 }
431
432 PAM_LOG("Credentials stash verified");
433
434 retval = pam_get_data(pamh, "ccache", (const void **)&ccache_check);
435 if (retval == PAM_SUCCESS) {
436 krb5_cc_destroy(pam_context, ccache);
437 PAM_VERBOSE_ERROR("Kerberos 5 error");
438 retval = PAM_AUTH_ERR;
439 goto cleanup;
440 }
441
442 PAM_LOG("Credentials stash not pre-existing");
443
444 retval = pam_set_data(pamh, "ccache", ccache, cleanup_cache);
445 if (retval != 0) {
446 krb5_cc_destroy(pam_context, ccache);
447 PAM_VERBOSE_ERROR("Kerberos 5 error");
448 retval = PAM_SERVICE_ERR;
449 goto cleanup;
450 }
451
452 PAM_LOG("Credentials stash saved");
453
454cleanup:
455 krb5_free_cred_contents(pam_context, &creds);
456 PAM_LOG("Done cleanup");
457cleanup2:
458 krb5_free_principal(pam_context, princ);
459 PAM_LOG("Done cleanup2");
460cleanup3:
461 if (princ_name)
462 free(princ_name);
463
464 krb5_free_context(pam_context);
465
466 PAM_LOG("Done cleanup3");
467
468 if (retval != PAM_SUCCESS)
469 PAM_VERBOSE_ERROR("Kerberos 5 refuses you");
470
471 PAM_RETURN(retval);
472}
473
474PAM_EXTERN int
475pam_sm_setcred(pam_handle_t *pamh, int flags, int argc, const char **argv)
476{
477
478 krb5_error_code krbret;
479 krb5_context pam_context;
480 krb5_principal princ;
481 krb5_creds creds;
482 krb5_ccache ccache_temp, ccache_perm;
483 krb5_cc_cursor cursor;
484 struct options options;
485 struct passwd *pwd = NULL;
486 int retval;
487 char *user;
488 char *cache_name, *cache_env_name, *p, *q;
489
490 uid_t euid;
491 gid_t egid;
492
493 pam_std_option(&options, other_options, argc, argv);
494
495 PAM_LOG("Options processed");
496
497 if (flags & PAM_DELETE_CRED)
498 PAM_RETURN(PAM_SUCCESS);
499
500 if (flags & PAM_REFRESH_CRED)
501 PAM_RETURN(PAM_SUCCESS);
502
503 if (flags & PAM_REINITIALIZE_CRED)
504 PAM_RETURN(PAM_SUCCESS);
505
506 if (!(flags & PAM_ESTABLISH_CRED))
507 PAM_RETURN(PAM_SERVICE_ERR);
508
509 PAM_LOG("Establishing credentials");
510
511 /* Get username */
512 retval = pam_get_item(pamh, PAM_USER, (const void **)&user);
513 if (retval != PAM_SUCCESS)
514 PAM_RETURN(retval);
515
516 PAM_LOG("Got user: %s", user);
517
518 krbret = krb5_init_context(&pam_context);
519 if (krbret != 0) {
520 PAM_LOG("Error krb5_init_context(): %s", error_message(krbret));
521 PAM_RETURN(PAM_SERVICE_ERR);
522 }
523
524 PAM_LOG("Context initialised");
525
526 euid = geteuid(); /* Usually 0 */
527 egid = getegid();
528
529 PAM_LOG("Got euid, egid: %d %d", euid, egid);
530
531 /* Retrieve the cache name */
532 retval = pam_get_data(pamh, "ccache", (const void **)&ccache_temp);
533 if (retval != PAM_SUCCESS)
534 goto cleanup3;
535
536 /* Get the uid. This should exist. */
537 pwd = getpwnam(user);
538 if (pwd == NULL) {
539 retval = PAM_USER_UNKNOWN;
540 goto cleanup3;
541 }
542
543 PAM_LOG("Done getpwnam()");
544
545 /* Avoid following a symlink as root */
546 if (setegid(pwd->pw_gid)) {
547 retval = PAM_SERVICE_ERR;
548 goto cleanup3;
549 }
550 if (seteuid(pwd->pw_uid)) {
551 retval = PAM_SERVICE_ERR;
552 goto cleanup3;
553 }
554
555 PAM_LOG("Done setegid() & seteuid()");
556
557 /* Get the cache name */
558 cache_name = NULL;
559 pam_test_option(&options, PAM_OPT_CCACHE, &cache_name);
560 if (cache_name == NULL)
561 asprintf(&cache_name, "FILE:/tmp/krb5cc_%d", pwd->pw_uid);
562
563 p = calloc(PATH_MAX + 16, sizeof(char));
564 q = cache_name;
565
566 if (p == NULL) {
567 PAM_LOG("Error malloc(): failure");
568 retval = PAM_BUF_ERR;
569 goto cleanup3;
570 }
571 cache_name = p;
572
573 /* convert %u and %p */
574 while (*q) {
575 if (*q == '%') {
576 q++;
577 if (*q == 'u') {
578 sprintf(p, "%d", pwd->pw_uid);
579 p += strlen(p);
580 }
581 else if (*q == 'p') {
582 sprintf(p, "%d", getpid());
583 p += strlen(p);
584 }
585 else {
586 /* Not a special token */
587 *p++ = '%';
588 q--;
589 }
590 q++;
591 }
592 else {
593 *p++ = *q++;
594 }
595 }
596
597 PAM_LOG("Got cache_name: %s", cache_name);
598
599 /* Initialize the new ccache */
600 krbret = krb5_cc_get_principal(pam_context, ccache_temp, &princ);
601 if (krbret != 0) {
602 PAM_LOG("Error krb5_cc_get_principal(): %s",
603 error_message(krbret));
604 retval = PAM_SERVICE_ERR;
605 goto cleanup3;
606 }
607 krbret = krb5_cc_resolve(pam_context, cache_name, &ccache_perm);
608 if (krbret != 0) {
609 PAM_LOG("Error krb5_cc_resolve(): %s", error_message(krbret));
610 retval = PAM_SERVICE_ERR;
611 goto cleanup2;
612 }
613 krbret = krb5_cc_initialize(pam_context, ccache_perm, princ);
614 if (krbret != 0) {
615 PAM_LOG("Error krb5_cc_initialize(): %s", error_message(krbret));
616 retval = PAM_SERVICE_ERR;
617 goto cleanup2;
618 }
619
620 PAM_LOG("Cache initialised");
621
622 /* Prepare for iteration over creds */
623 krbret = krb5_cc_start_seq_get(pam_context, ccache_temp, &cursor);
624 if (krbret != 0) {
625 PAM_LOG("Error krb5_cc_start_seq_get(): %s", error_message(krbret));
626 krb5_cc_destroy(pam_context, ccache_perm);
627 retval = PAM_SERVICE_ERR;
628 goto cleanup2;
629 }
630
631 PAM_LOG("Prepared for iteration");
632
633 /* Copy the creds (should be two of them) */
634 while ((krbret = krb5_cc_next_cred(pam_context, ccache_temp,
635 &cursor, &creds) == 0)) {
636 krbret = krb5_cc_store_cred(pam_context, ccache_perm, &creds);
637 if (krbret != 0) {
638 PAM_LOG("Error krb5_cc_store_cred(): %s",
639 error_message(krbret));
640 krb5_cc_destroy(pam_context, ccache_perm);
641 krb5_free_cred_contents(pam_context, &creds);
642 retval = PAM_SERVICE_ERR;
643 goto cleanup2;
644 }
645 krb5_free_cred_contents(pam_context, &creds);
646 PAM_LOG("Iteration");
647 }
648 krb5_cc_end_seq_get(pam_context, ccache_temp, &cursor);
649
650 PAM_LOG("Done iterating");
651
652 if (strstr(cache_name, "FILE:") == cache_name) {
653 if (chown(&cache_name[5], pwd->pw_uid, pwd->pw_gid) == -1) {
654 PAM_LOG("Error chown(): %s", strerror(errno));
655 krb5_cc_destroy(pam_context, ccache_perm);
656 retval = PAM_SERVICE_ERR;
657 goto cleanup2;
658 }
659 PAM_LOG("Done chown()");
660
661 if (chmod(&cache_name[5], (S_IRUSR | S_IWUSR)) == -1) {
662 PAM_LOG("Error chmod(): %s", strerror(errno));
663 krb5_cc_destroy(pam_context, ccache_perm);
664 retval = PAM_SERVICE_ERR;
665 goto cleanup2;
666 }
667 PAM_LOG("Done chmod()");
668 }
669
670 krb5_cc_close(pam_context, ccache_perm);
671
672 PAM_LOG("Cache closed");
673
674 cache_env_name = malloc(strlen(cache_name) + 12);
675 if (!cache_env_name) {
676 PAM_LOG("Error malloc(): failure");
677 krb5_cc_destroy(pam_context, ccache_perm);
678 retval = PAM_BUF_ERR;
679 goto cleanup2;
680 }
681
682 sprintf(cache_env_name, "KRB5CCNAME=%s", cache_name);
683 if ((retval = pam_putenv(pamh, cache_env_name)) != 0) {
684 PAM_LOG("Error pam_putenv(): %s", pam_strerror(pamh, retval));
685 krb5_cc_destroy(pam_context, ccache_perm);
686 retval = PAM_SERVICE_ERR;
687 goto cleanup2;
688 }
689
690 PAM_LOG("Environment done: KRB5CCNAME=%s", cache_name);
691
692cleanup2:
693 krb5_free_principal(pam_context, princ);
694 PAM_LOG("Done cleanup2");
695cleanup3:
696 krb5_free_context(pam_context);
697 PAM_LOG("Done cleanup3");
698
699 seteuid(euid);
700 setegid(egid);
701
702 PAM_LOG("Done seteuid() & setegid()");
703
704 PAM_RETURN(retval);
705}
706
707/*
708 * account management
709 */
710PAM_EXTERN int
711pam_sm_acct_mgmt(pam_handle_t *pamh, int flags, int argc, const char **argv)
712{
713 krb5_error_code krbret;
714 krb5_context pam_context;
715 krb5_ccache ccache;
716 krb5_principal princ;
717 struct options options;
718 int retval;
719 const char *user;
720
721 pam_std_option(&options, other_options, argc, argv);
722
723 PAM_LOG("Options processed");
724
725 retval = pam_get_item(pamh, PAM_USER, (const void **)&user);
726 if (retval != PAM_SUCCESS)
727 PAM_RETURN(retval);
728
729 PAM_LOG("Got user: %s", user);
730
731 retval = pam_get_data(pamh, "ccache", (const void **)&ccache);
732 if (retval != PAM_SUCCESS)
733 PAM_RETURN(PAM_SUCCESS);
734
735 PAM_LOG("Got ccache");
736
737 krbret = krb5_init_context(&pam_context);
738 if (krbret != 0) {
739 PAM_LOG("Error krb5_init_context(): %s", error_message(krbret));
740 PAM_RETURN(PAM_PERM_DENIED);
741 }
742
743 PAM_LOG("Context initialised");
744
745 krbret = krb5_cc_get_principal(pam_context, ccache, &princ);
746 if (krbret != 0) {
747 PAM_LOG("Error krb5_cc_get_principal(): %s", error_message(krbret));
748 retval = PAM_PERM_DENIED;;
749 goto cleanup;
750 }
751
752 PAM_LOG("Got principal");
753
754 if (krb5_kuserok(pam_context, princ, user))
755 retval = PAM_SUCCESS;
756 else
757 retval = PAM_PERM_DENIED;
758 krb5_free_principal(pam_context, princ);
759
760 PAM_LOG("Done kuserok()");
761
762cleanup:
763 krb5_free_context(pam_context);
764 PAM_LOG("Done cleanup");
765
766 PAM_RETURN(retval);
767
768}
769
770/*
771 * session management
772 *
773 * logging only
774 */
775PAM_EXTERN int
776pam_sm_open_session(pam_handle_t *pamh, int flags, int argc, const char **argv)
777{
778 struct options options;
779
780 pam_std_option(&options, NULL, argc, argv);
781
782 PAM_LOG("Options processed");
783
784 PAM_RETURN(PAM_SUCCESS);
785}
786
787PAM_EXTERN int
788pam_sm_close_session(pam_handle_t *pamh, int flags, int argc, const char **argv)
789{
790 struct options options;
791
792 pam_std_option(&options, NULL, argc, argv);
793
794 PAM_LOG("Options processed");
795
796 PAM_RETURN(PAM_SUCCESS);
797}
798
799/*
800 * password management
801 */
802PAM_EXTERN int
803pam_sm_chauthtok(pam_handle_t *pamh, int flags, int argc, const char **argv)
804{
805 krb5_error_code krbret;
806 krb5_context pam_context;
807 krb5_creds creds;
808 krb5_principal princ;
809 krb5_get_init_creds_opt opts;
810 krb5_data result_code_string, result_string;
811 struct options options;
812 int result_code, retval;
813 const char *user, *pass, *pass2;
814 char *princ_name;
815
816 pam_std_option(&options, other_options, argc, argv);
817
818 PAM_LOG("Options processed");
819
820 if (!(flags & PAM_UPDATE_AUTHTOK))
821 PAM_RETURN(PAM_AUTHTOK_ERR);
822
823 retval = pam_get_item(pamh, PAM_USER, (const void **)&user);
824 if (retval != PAM_SUCCESS)
825 PAM_RETURN(retval);
826
827 PAM_LOG("Got user: %s", user);
828
829 krbret = krb5_init_context(&pam_context);
830 if (krbret != 0) {
831 PAM_LOG("Error krb5_init_context(): %s", error_message(krbret));
832 PAM_RETURN(PAM_SERVICE_ERR);
833 }
834
835 PAM_LOG("Context initialised");
836
837 krb5_get_init_creds_opt_init(&opts);
838
839 PAM_LOG("Credentials options initialised");
840
841 /* Get principal name */
842 krbret = krb5_parse_name(pam_context, user, &princ);
843 if (krbret != 0) {
844 PAM_LOG("Error krb5_parse_name(): %s", error_message(krbret));
845 retval = PAM_USER_UNKNOWN;
846 goto cleanup3;
847 }
848
849 /* Now convert the principal name into something human readable */
850 princ_name = NULL;
851 krbret = krb5_unparse_name(pam_context, princ, &princ_name);
852 if (krbret != 0) {
853 PAM_LOG("Error krb5_unparse_name(): %s", error_message(krbret));
854 retval = PAM_SERVICE_ERR;
855 goto cleanup2;
856 }
857
858 PAM_LOG("Got principal: %s", princ_name);
859
860 /* Get password */
861 retval = pam_get_pass(pamh, &pass, PASSWORD_PROMPT, &options);
862 if (retval != PAM_SUCCESS)
863 goto cleanup2;
864
865 PAM_LOG("Got password");
866
867 memset(&creds, 0, sizeof(krb5_creds));
868 krbret = krb5_get_init_creds_password(pam_context, &creds, princ,
869 pass, NULL, pamh, 0, "kadmin/changepw", &opts);
870 if (krbret != 0) {
871 PAM_LOG("Error krb5_get_init_creds_password()",
872 error_message(krbret));
873 retval = PAM_AUTH_ERR;
874 goto cleanup2;
875 }
876
877 PAM_LOG("Credentials established");
878
879 /* Now get the new password */
880 retval = pam_get_pass(pamh, &pass, NEW_PASSWORD_PROMPT, &options);
881 if (retval != PAM_SUCCESS)
882 goto cleanup;
883
884 retval = pam_get_pass(pamh, &pass2, NEW_PASSWORD_PROMPT_2, &options);
885 if (retval != PAM_SUCCESS)
886 goto cleanup;
887
888 PAM_LOG("Got new password twice");
889
890 if (strcmp(pass, pass2) != 0) {
891 PAM_LOG("Error strcmp(): passwords are different");
892 retval = PAM_AUTHTOK_ERR;
893 goto cleanup;
894 }
895
896 PAM_LOG("New passwords are the same");
897
898 /* Change it */
899 krbret = krb5_change_password(pam_context, &creds, (char *)pass,
900 &result_code, &result_code_string, &result_string);
901 if (krbret != 0) {
902 PAM_LOG("Error krb5_change_password(): %s",
903 error_message(krbret));
904 retval = PAM_AUTHTOK_ERR;
905 goto cleanup;
906 }
907 if (result_code) {
908 PAM_LOG("Error krb5_change_password(): (result_code)");
909 retval = PAM_AUTHTOK_ERR;
910 goto cleanup;
911 }
912
913 PAM_LOG("Password changed");
914
915 if (result_string.data)
916 free(result_string.data);
917 if (result_code_string.data)
918 free(result_code_string.data);
919
920cleanup:
921 krb5_free_cred_contents(pam_context, &creds);
922 PAM_LOG("Done cleanup");
923cleanup2:
924 krb5_free_principal(pam_context, princ);
925 PAM_LOG("Done cleanup2");
926cleanup3:
927 if (princ_name)
928 free(princ_name);
929
930 krb5_free_context(pam_context);
931
932 PAM_LOG("Done cleanup3");
933
934 PAM_RETURN(retval);
935}
936
937PAM_MODULE_ENTRY("pam_krb5");
938
939/*
940 * This routine with some modification is from the MIT V5B6 appl/bsd/login.c
941 * Modified by Sam Hartman <hartmans@mit.edu> to support PAM services
942 * for Debian.
943 *
944 * Verify the Kerberos ticket-granting ticket just retrieved for the
945 * user. If the Kerberos server doesn't respond, assume the user is
946 * trying to fake us out (since we DID just get a TGT from what is
947 * supposedly our KDC). If the host/<host> service is unknown (i.e.,
948 * the local keytab doesn't have it), and we cannot find another
949 * service we do have, let her in.
950 *
951 * Returns 1 for confirmation, -1 for failure, 0 for uncertainty.
952 */
953static int
954verify_krb_v5_tgt(krb5_context context, krb5_ccache ccache,
955 char *pam_service, int debug)
956{
957 krb5_error_code retval;
958 krb5_principal princ;
959 krb5_keyblock *keyblock;
960 krb5_data packet;
961 krb5_auth_context auth_context;
962 char phost[BUFSIZ], *services[3], **service;
963
964 packet.data = 0;
965
966 /* If possible we want to try and verify the ticket we have
967 * received against a keytab. We will try multiple service
968 * principals, including at least the host principal and the PAM
969 * service principal. The host principal is preferred because access
970 * to that key is generally sufficient to compromise root, while the
971 * service key for this PAM service may be less carefully guarded.
972 * It is important to check the keytab first before the KDC so we do
973 * not get spoofed by a fake KDC.
974 */
975 services[0] = "host";
976 services[1] = pam_service;
977 services[2] = NULL;
978 keyblock = 0;
979 retval = -1;
980 for (service = &services[0]; *service != NULL; service++) {
981 retval = krb5_sname_to_principal(context, NULL, *service,
982 KRB5_NT_SRV_HST, &princ);
983 if (retval != 0) {
984 if (debug)
985 syslog(LOG_DEBUG, "pam_krb5: verify_krb_v5_tgt(): %s: %s", "krb5_sname_to_principal()", error_message(retval));
986 return -1;
987 }
988
989 /* Extract the name directly. */
990 strncpy(phost, compat_princ_component(context, princ, 1),
991 BUFSIZ);
992 phost[BUFSIZ - 1] = '\0';
993
994 /*
995 * Do we have service/<host> keys?
996 * (use default/configured keytab, kvno IGNORE_VNO to get the
997 * first match, and ignore enctype.)
998 */
999 retval = krb5_kt_read_service_key(context, NULL, princ, 0, 0,
1000 &keyblock);
1001 if (retval != 0)
1002 continue;
1003 break;
1004 }
1005 if (retval != 0) { /* failed to find key */
1006 /* Keytab or service key does not exist */
1007 if (debug)
1008 syslog(LOG_DEBUG, "pam_krb5: verify_krb_v5_tgt(): %s: %s", "krb5_kt_read_service_key()", error_message(retval));
1009 retval = 0;
1010 goto cleanup;
1011 }
1012 if (keyblock)
1013 krb5_free_keyblock(context, keyblock);
1014
1015 /* Talk to the kdc and construct the ticket. */
1016 auth_context = NULL;
1017 retval = krb5_mk_req(context, &auth_context, 0, *service, phost,
1018 NULL, ccache, &packet);
1019 if (auth_context) {
1020 krb5_auth_con_free(context, auth_context);
1021 auth_context = NULL; /* setup for rd_req */
1022 }
1023 if (retval) {
1024 if (debug)
1025 syslog(LOG_DEBUG, "pam_krb5: verify_krb_v5_tgt(): %s: %s", "krb5_mk_req()", error_message(retval));
1026 retval = -1;
1027 goto cleanup;
1028 }
1029
1030 /* Try to use the ticket. */
1031 retval = krb5_rd_req(context, &auth_context, &packet, princ, NULL,
1032 NULL, NULL);
1033 if (retval) {
1034 if (debug)
1035 syslog(LOG_DEBUG, "pam_krb5: verify_krb_v5_tgt(): %s: %s", "krb5_rd_req()", error_message(retval));
1036 retval = -1;
1037 }
1038 else
1039 retval = 1;
1040
1041cleanup:
1042 if (packet.data)
1043 compat_free_data_contents(context, &packet);
1044 krb5_free_principal(context, princ);
1045 return retval;
1046}
1047
1048/* Free the memory for cache_name. Called by pam_end() */
1049static void
1050cleanup_cache(pam_handle_t *pamh, void *data, int pam_end_status)
1051{
1052 krb5_context pam_context;
1053 krb5_ccache ccache;
1054
1055 if (krb5_init_context(&pam_context))
1056 return;
1057
1058 ccache = (krb5_ccache)data;
1059 krb5_cc_destroy(pam_context, ccache);
1060 krb5_free_context(pam_context);
1061}
1062
1063#ifdef COMPAT_HEIMDAL
1064#ifdef COMPAT_MIT
1065#error This cannot be MIT and Heimdal compatible!
1066#endif
1067#endif
1068
1069#ifndef COMPAT_HEIMDAL
1070#ifndef COMPAT_MIT
1071#error One of COMPAT_MIT and COMPAT_HEIMDAL must be specified!
1072#endif
1073#endif
1074
1075#ifdef COMPAT_HEIMDAL
1076static const char *
1077compat_princ_component(krb5_context context, krb5_principal princ, int n)
1078{
1079 return princ->name.name_string.val[n];
1080}
1081
1082static void
1083compat_free_data_contents(krb5_context context, krb5_data * data)
1084{
1085 krb5_xfree(data->data);
1086}
1087#endif
1088
1089#ifdef COMPAT_MIT
1090static const char *
1091compat_princ_component(krb5_context context, krb5_principal princ, int n)
1092{
1093 return krb5_princ_component(context, princ, n)->data;
1094}
1095
1096static void
1097compat_free_data_contents(krb5_context context, krb5_data * data)
1098{
1099 krb5_free_data_contents(context, data);
1100}
1101#endif