xref: /freebsd/lib/libpam/modules/pam_krb5/pam_krb5.c (revision c2065008)
139384277SMark Murray /*-
239384277SMark Murray  * Copyright 2001 Mark R V Murray
339384277SMark Murray  * Copyright Frank Cusack fcusack@fcusack.com 1999-2000
439384277SMark Murray  * All rights reserved
539384277SMark Murray  *
639384277SMark Murray  * Redistribution and use in source and binary forms, with or without
739384277SMark Murray  * modification, are permitted provided that the following conditions
839384277SMark Murray  * are met:
939384277SMark Murray  * 1. Redistributions of source code must retain the above copyright
1039384277SMark Murray  *    notice, and the entire permission notice in its entirety,
1139384277SMark Murray  *    including the disclaimer of warranties.
1239384277SMark Murray  * 2. Redistributions in binary form must reproduce the above copyright
1339384277SMark Murray  *    notice, this list of conditions and the following disclaimer in the
1439384277SMark Murray  *    documentation and/or other materials provided with the distribution.
1539384277SMark Murray  * 3. The name of the author may not be used to endorse or promote
1639384277SMark Murray  *    products derived from this software without specific prior
1739384277SMark Murray  *    written permission.
1839384277SMark Murray  *
1939384277SMark Murray  * ALTERNATIVELY, this product may be distributed under the terms of
2039384277SMark Murray  * the GNU Public License, in which case the provisions of the GPL are
2139384277SMark Murray  * required INSTEAD OF the above restrictions.  (This clause is
2239384277SMark Murray  * necessary due to a potential bad interaction between the GPL and
2339384277SMark Murray  * the restrictions contained in a BSD-style copyright.)
2439384277SMark Murray  *
2539384277SMark Murray  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
2639384277SMark Murray  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
2739384277SMark Murray  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
2839384277SMark Murray  * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
2939384277SMark Murray  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
3039384277SMark Murray  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
3139384277SMark Murray  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
3239384277SMark Murray  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
3339384277SMark Murray  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
3439384277SMark Murray  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
3539384277SMark Murray  * OF THE POSSIBILITY OF SUCH DAMAGE.
3639384277SMark Murray  * ---------------------------------------------------------------------------
3739384277SMark Murray  *
3839384277SMark Murray  * This software may contain code from Naomaru Itoi:
3939384277SMark Murray  *
4039384277SMark Murray  * PAM-kerberos5 module Copyright notice.
4139384277SMark Murray  * Naomaru Itoi <itoi@eecs.umich.edu>, June 24, 1997.
4239384277SMark Murray  *
4339384277SMark Murray  * ----------------------------------------------------------------------------
4439384277SMark Murray  * COPYRIGHT (c)  1997
4539384277SMark Murray  * THE REGENTS OF THE UNIVERSITY OF MICHIGAN
4639384277SMark Murray  * ALL RIGHTS RESERVED
4739384277SMark Murray  *
4839384277SMark Murray  * PERMISSION IS GRANTED TO USE, COPY, CREATE DERIVATIVE WORKS AND REDISTRIBUTE
4939384277SMark Murray  * THIS SOFTWARE AND SUCH DERIVATIVE WORKS FOR ANY PURPOSE, SO LONG AS THE NAME
5039384277SMark Murray  * OF THE UNIVERSITY OF MICHIGAN IS NOT USED IN ANY ADVERTISING OR PUBLICITY
5139384277SMark Murray  * PERTAINING TO THE USE OR DISTRIBUTION OF THIS SOFTWARE WITHOUT SPECIFIC,
5239384277SMark Murray  * WRITTEN PRIOR AUTHORIZATION.  IF THE ABOVE COPYRIGHT NOTICE OR ANY OTHER
5339384277SMark Murray  * IDENTIFICATION OF THE UNIVERSITY OF MICHIGAN IS INCLUDED IN ANY COPY OF ANY
5439384277SMark Murray  * PORTION OF THIS SOFTWARE, THEN THE DISCLAIMER BELOW MUST ALSO BE INCLUDED.
5539384277SMark Murray  *
5639384277SMark Murray  * THE SOFTWARE IS PROVIDED AS IS, WITHOUT REPRESENTATION FROM THE UNIVERSITY OF
5739384277SMark Murray  * MICHIGAN AS TO ITS FITNESS FOR ANY PURPOSE, AND WITHOUT WARRANTY BY THE
5839384277SMark Murray  * UNIVERSITY OF MICHIGAN OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING
5939384277SMark Murray  * WITHOUT LIMITATION THE IMPLIED WARRANTIES OF MERCHANTABITILY AND FITNESS FOR A
6039384277SMark Murray  * PARTICULAR PURPOSE.  THE REGENTS OF THE UNIVERSITY OF MICHIGAN SHALL NOT BE
6139384277SMark Murray  * LIABLE FOR ANY DAMAGES, INCLUDING SPECIAL, INDIRECT, INCIDENTAL, OR
6239384277SMark Murray  * CONSEQUENTIAL DAMAGES, WITH RESPECT TO ANY CLAIM ARISING OUT OF OR IN
6339384277SMark Murray  * CONNECTION WITH THE USE OF THE SOFTWARE, EVEN IF IT HAS BEEN OR IS HEREAFTER
6439384277SMark Murray  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
6539384277SMark Murray  *
6639384277SMark Murray  * PAM-kerberos5 module is written based on PAM-kerberos4 module
6739384277SMark Murray  * by Derrick J. Brashear and kerberos5-1.0pl1 by M.I.T. kerberos team.
6839384277SMark Murray  * Permission to use, copy, modify, distribute this software is hereby
6939384277SMark Murray  * granted, as long as it is granted by Derrick J. Brashear and
7039384277SMark Murray  * M.I.T. kerberos team. Followings are their copyright information.
7139384277SMark Murray  * ----------------------------------------------------------------------------
7239384277SMark Murray  *
7339384277SMark Murray  * This software may contain code from Derrick J. Brashear:
7439384277SMark Murray  *
7539384277SMark Murray  *
7639384277SMark Murray  * Copyright (c) Derrick J. Brashear, 1996. All rights reserved
7739384277SMark Murray  *
7839384277SMark Murray  * Redistribution and use in source and binary forms, with or without
7939384277SMark Murray  * modification, are permitted provided that the following conditions
8039384277SMark Murray  * are met:
8139384277SMark Murray  * 1. Redistributions of source code must retain the above copyright
8239384277SMark Murray  *    notice, and the entire permission notice in its entirety,
8339384277SMark Murray  *    including the disclaimer of warranties.
8439384277SMark Murray  * 2. Redistributions in binary form must reproduce the above copyright
8539384277SMark Murray  *    notice, this list of conditions and the following disclaimer in the
8639384277SMark Murray  *    documentation and/or other materials provided with the distribution.
8739384277SMark Murray  * 3. The name of the author may not be used to endorse or promote
8839384277SMark Murray  *    products derived from this software without specific prior
8939384277SMark Murray  *    written permission.
9039384277SMark Murray  *
9139384277SMark Murray  * ALTERNATIVELY, this product may be distributed under the terms of
9239384277SMark Murray  * the GNU Public License, in which case the provisions of the GPL are
9339384277SMark Murray  * required INSTEAD OF the above restrictions.  (This clause is
9439384277SMark Murray  * necessary due to a potential bad interaction between the GPL and
9539384277SMark Murray  * the restrictions contained in a BSD-style copyright.)
9639384277SMark Murray  *
9739384277SMark Murray  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
9839384277SMark Murray  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
9939384277SMark Murray  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
10039384277SMark Murray  * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
10139384277SMark Murray  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
10239384277SMark Murray  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
10339384277SMark Murray  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
10439384277SMark Murray  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
10539384277SMark Murray  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
10639384277SMark Murray  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
10739384277SMark Murray  * OF THE POSSIBILITY OF SUCH DAMAGE.
10839384277SMark Murray  *
10939384277SMark Murray  * ----------------------------------------------------------------------------
11039384277SMark Murray  *
11139384277SMark Murray  * This software may contain code from MIT Kerberos 5:
11239384277SMark Murray  *
11339384277SMark Murray  * Copyright Notice and Legal Administrivia
11439384277SMark Murray  * ----------------------------------------
11539384277SMark Murray  *
11639384277SMark Murray  * Copyright (C) 1996 by the Massachusetts Institute of Technology.
11739384277SMark Murray  *
11839384277SMark Murray  * All rights reserved.
11939384277SMark Murray  *
12039384277SMark Murray  * Export of this software from the United States of America may require
12139384277SMark Murray  * a specific license from the United States Government.  It is the
12239384277SMark Murray  * responsibility of any person or organization contemplating export to
12339384277SMark Murray  * obtain such a license before exporting.
12439384277SMark Murray  *
12539384277SMark Murray  * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
12639384277SMark Murray  * distribute this software and its documentation for any purpose and
12739384277SMark Murray  * without fee is hereby granted, provided that the above copyright
12839384277SMark Murray  * notice appear in all copies and that both that copyright notice and
12939384277SMark Murray  * this permission notice appear in supporting documentation, and that
13039384277SMark Murray  * the name of M.I.T. not be used in advertising or publicity pertaining
13139384277SMark Murray  * to distribution of the software without specific, written prior
13239384277SMark Murray  * permission.  M.I.T. makes no representations about the suitability of
13339384277SMark Murray  * this software for any purpose.  It is provided "as is" without express
13439384277SMark Murray  * or implied warranty.
13539384277SMark Murray  *
13639384277SMark Murray  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
13739384277SMark Murray  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
13839384277SMark Murray  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
13939384277SMark Murray  *
14039384277SMark Murray  * Individual source code files are copyright MIT, Cygnus Support,
14139384277SMark Murray  * OpenVision, Oracle, Sun Soft, and others.
14239384277SMark Murray  *
14339384277SMark Murray  * Project Athena, Athena, Athena MUSE, Discuss, Hesiod, Kerberos, Moira,
14439384277SMark Murray  * and Zephyr are trademarks of the Massachusetts Institute of Technology
14539384277SMark Murray  * (MIT).  No commercial use of these trademarks may be made without
14639384277SMark Murray  * prior written permission of MIT.
14739384277SMark Murray  *
14839384277SMark Murray  * "Commercial use" means use of a name in a product or other for-profit
14939384277SMark Murray  * manner.  It does NOT prevent a commercial firm from referring to the
15039384277SMark Murray  * MIT trademarks in order to convey information (although in doing so,
15139384277SMark Murray  * recognition of their trademark status should be given).
15239384277SMark Murray  *
15339384277SMark Murray  * The following copyright and permission notice applies to the
15439384277SMark Murray  * OpenVision Kerberos Administration system located in kadmin/create,
15539384277SMark Murray  * kadmin/dbutil, kadmin/passwd, kadmin/server, lib/kadm5, and portions
15639384277SMark Murray  * of lib/rpc:
15739384277SMark Murray  *
15839384277SMark Murray  *    Copyright, OpenVision Technologies, Inc., 1996, All Rights Reserved
15939384277SMark Murray  *
16039384277SMark Murray  *    WARNING: Retrieving the OpenVision Kerberos Administration system
16139384277SMark Murray  *    source code, as described below, indicates your acceptance of the
16239384277SMark Murray  *    following terms.  If you do not agree to the following terms, do not
16339384277SMark Murray  *    retrieve the OpenVision Kerberos administration system.
16439384277SMark Murray  *
16539384277SMark Murray  *    You may freely use and distribute the Source Code and Object Code
16639384277SMark Murray  *    compiled from it, with or without modification, but this Source
16739384277SMark Murray  *    Code is provided to you "AS IS" EXCLUSIVE OF ANY WARRANTY,
16839384277SMark Murray  *    INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY OR
16939384277SMark Murray  *    FITNESS FOR A PARTICULAR PURPOSE, OR ANY OTHER WARRANTY, WHETHER
17039384277SMark Murray  *    EXPRESS OR IMPLIED.  IN NO EVENT WILL OPENVISION HAVE ANY LIABILITY
17139384277SMark Murray  *    FOR ANY LOST PROFITS, LOSS OF DATA OR COSTS OF PROCUREMENT OF
17239384277SMark Murray  *    SUBSTITUTE GOODS OR SERVICES, OR FOR ANY SPECIAL, INDIRECT, OR
17339384277SMark Murray  *    CONSEQUENTIAL DAMAGES ARISING OUT OF THIS AGREEMENT, INCLUDING,
17439384277SMark Murray  *    WITHOUT LIMITATION, THOSE RESULTING FROM THE USE OF THE SOURCE
17539384277SMark Murray  *    CODE, OR THE FAILURE OF THE SOURCE CODE TO PERFORM, OR FOR ANY
17639384277SMark Murray  *    OTHER REASON.
17739384277SMark Murray  *
17839384277SMark Murray  *    OpenVision retains all copyrights in the donated Source Code. OpenVision
17939384277SMark Murray  *    also retains copyright to derivative works of the Source Code, whether
18039384277SMark Murray  *    created by OpenVision or by a third party. The OpenVision copyright
18139384277SMark Murray  *    notice must be preserved if derivative works are made based on the
18239384277SMark Murray  *    donated Source Code.
18339384277SMark Murray  *
18439384277SMark Murray  *    OpenVision Technologies, Inc. has donated this Kerberos
18539384277SMark Murray  *    Administration system to MIT for inclusion in the standard
18639384277SMark Murray  *    Kerberos 5 distribution.  This donation underscores our
18739384277SMark Murray  *    commitment to continuing Kerberos technology development
18839384277SMark Murray  *    and our gratitude for the valuable work which has been
18939384277SMark Murray  *    performed by MIT and the Kerberos community.
19039384277SMark Murray  *
19139384277SMark Murray  */
19239384277SMark Murray 
193ceaf33f5SMatthew Dillon #include <sys/cdefs.h>
194ceaf33f5SMatthew Dillon __FBSDID("$FreeBSD$");
195ceaf33f5SMatthew Dillon 
19639384277SMark Murray #include <sys/types.h>
19739384277SMark Murray #include <sys/stat.h>
19839384277SMark Murray #include <errno.h>
19939384277SMark Murray #include <limits.h>
20039384277SMark Murray #include <pwd.h>
20139384277SMark Murray #include <stdio.h>
20239384277SMark Murray #include <stdlib.h>
20339384277SMark Murray #include <strings.h>
20439384277SMark Murray #include <syslog.h>
20539384277SMark Murray #include <unistd.h>
20639384277SMark Murray 
20739384277SMark Murray #include <krb5.h>
20839384277SMark Murray #include <com_err.h>
20939384277SMark Murray 
21039384277SMark Murray #define	PAM_SM_AUTH
21139384277SMark Murray #define	PAM_SM_ACCOUNT
21239384277SMark Murray #define	PAM_SM_SESSION
21339384277SMark Murray #define	PAM_SM_PASSWORD
21439384277SMark Murray 
21539384277SMark Murray #include <security/pam_appl.h>
21639384277SMark Murray #include <security/pam_modules.h>
21739384277SMark Murray 
21839384277SMark Murray #include "pam_mod_misc.h"
21939384277SMark Murray 
22039384277SMark Murray #define	COMPAT_HEIMDAL
22139384277SMark Murray /* #define	COMPAT_MIT */
22239384277SMark Murray 
22339384277SMark Murray extern	krb5_cc_ops	krb5_mcc_ops;
22439384277SMark Murray 
22539384277SMark Murray static int	verify_krb_v5_tgt(krb5_context, krb5_ccache, char *, int);
22639384277SMark Murray static void	cleanup_cache(pam_handle_t *, void *, int);
22739384277SMark Murray static const	char *compat_princ_component(krb5_context, krb5_principal, int);
22839384277SMark Murray static void	compat_free_data_contents(krb5_context, krb5_data *);
22939384277SMark Murray 
23039384277SMark Murray #define USER_PROMPT		"Username: "
23139384277SMark Murray #define PASSWORD_PROMPT		"Password:"
23239384277SMark Murray #define NEW_PASSWORD_PROMPT	"New Password:"
23339384277SMark Murray #define NEW_PASSWORD_PROMPT_2	"New Password (again):"
23439384277SMark Murray 
23539384277SMark Murray enum { PAM_OPT_AUTH_AS_SELF=PAM_OPT_STD_MAX, PAM_OPT_CCACHE, PAM_OPT_FORWARDABLE, PAM_OPT_NO_CCACHE, PAM_OPT_REUSE_CCACHE };
23639384277SMark Murray 
23739384277SMark Murray static struct opttab other_options[] = {
23839384277SMark Murray 	{ "auth_as_self",	PAM_OPT_AUTH_AS_SELF },
23939384277SMark Murray 	{ "ccache",		PAM_OPT_CCACHE },
24039384277SMark Murray 	{ "forwardable",	PAM_OPT_FORWARDABLE },
24139384277SMark Murray 	{ "no_ccache",		PAM_OPT_NO_CCACHE },
24239384277SMark Murray 	{ "reuse_ccache",	PAM_OPT_REUSE_CCACHE },
24339384277SMark Murray 	{ NULL, 0 }
24439384277SMark Murray };
24539384277SMark Murray 
24639384277SMark Murray /*
24739384277SMark Murray  * authentication management
24839384277SMark Murray  */
24939384277SMark Murray PAM_EXTERN int
250c2065008SMark Murray pam_sm_authenticate(pam_handle_t *pamh, int flags __unused, int argc, const char **argv)
25139384277SMark Murray {
25239384277SMark Murray 	krb5_error_code krbret;
25339384277SMark Murray 	krb5_context pam_context;
25439384277SMark Murray 	krb5_creds creds;
25539384277SMark Murray 	krb5_principal princ;
25639384277SMark Murray 	krb5_ccache ccache, ccache_check;
25739384277SMark Murray 	krb5_get_init_creds_opt opts;
25839384277SMark Murray 	struct options options;
25939384277SMark Murray 	struct passwd *pwd;
26039384277SMark Murray 	int retval;
261c2065008SMark Murray 	const char *sourceuser, *user, *pass, *service;
262c2065008SMark Murray 	char *principal, *princ_name, *cache_name, luser[32];
26339384277SMark Murray 
26439384277SMark Murray 	pam_std_option(&options, other_options, argc, argv);
26539384277SMark Murray 
26639384277SMark Murray 	PAM_LOG("Options processed");
26739384277SMark Murray 
26839384277SMark Murray 	retval = pam_get_user(pamh, &user, USER_PROMPT);
26939384277SMark Murray 	if (retval != PAM_SUCCESS)
27039384277SMark Murray 		PAM_RETURN(retval);
27139384277SMark Murray 
27239384277SMark Murray 	PAM_LOG("Got user: %s", user);
27339384277SMark Murray 
27439384277SMark Murray 	retval = pam_get_item(pamh, PAM_RUSER, (const void **)&sourceuser);
27539384277SMark Murray 	if (retval != PAM_SUCCESS)
27639384277SMark Murray 		PAM_RETURN(retval);
27739384277SMark Murray 
27839384277SMark Murray 	PAM_LOG("Got ruser: %s", sourceuser);
27939384277SMark Murray 
28039384277SMark Murray 	service = NULL;
28139384277SMark Murray 	pam_get_item(pamh, PAM_SERVICE, (const void **)&service);
28239384277SMark Murray 	if (service == NULL)
28339384277SMark Murray 		service = "unknown";
28439384277SMark Murray 
28539384277SMark Murray 	PAM_LOG("Got service: %s", service);
28639384277SMark Murray 
28739384277SMark Murray 	krbret = krb5_init_context(&pam_context);
28839384277SMark Murray 	if (krbret != 0) {
28939384277SMark Murray 		PAM_VERBOSE_ERROR("Kerberos 5 error");
29039384277SMark Murray 		PAM_RETURN(PAM_SERVICE_ERR);
29139384277SMark Murray 	}
29239384277SMark Murray 
29339384277SMark Murray 	PAM_LOG("Context initialised");
29439384277SMark Murray 
29539384277SMark Murray 	krb5_get_init_creds_opt_init(&opts);
29639384277SMark Murray 
29739384277SMark Murray 	if (pam_test_option(&options, PAM_OPT_FORWARDABLE, NULL))
29839384277SMark Murray 		krb5_get_init_creds_opt_set_forwardable(&opts, 1);
29939384277SMark Murray 
30039384277SMark Murray 	PAM_LOG("Credentials initialised");
30139384277SMark Murray 
30239384277SMark Murray 	krbret = krb5_cc_register(pam_context, &krb5_mcc_ops, FALSE);
30339384277SMark Murray 	if (krbret != 0 && krbret != KRB5_CC_TYPE_EXISTS) {
30439384277SMark Murray 		PAM_VERBOSE_ERROR("Kerberos 5 error");
30539384277SMark Murray 		retval = PAM_SERVICE_ERR;
30639384277SMark Murray 		goto cleanup3;
30739384277SMark Murray 	}
30839384277SMark Murray 
30939384277SMark Murray 	PAM_LOG("Done krb5_cc_register()");
31039384277SMark Murray 
31139384277SMark Murray 	/* Get principal name */
31239384277SMark Murray 	if (pam_test_option(&options, PAM_OPT_AUTH_AS_SELF, NULL))
31339384277SMark Murray 		asprintf(&principal, "%s/%s", sourceuser, user);
31439384277SMark Murray 	else
31539384277SMark Murray 		principal = strdup(user);
31639384277SMark Murray 
31739384277SMark Murray 	PAM_LOG("Created principal: %s", principal);
31839384277SMark Murray 
31939384277SMark Murray 	krbret = krb5_parse_name(pam_context, principal, &princ);
32039384277SMark Murray 	free(principal);
32139384277SMark Murray 	if (krbret != 0) {
32239384277SMark Murray 		PAM_LOG("Error krb5_parse_name(): %s", error_message(krbret));
32339384277SMark Murray 		PAM_VERBOSE_ERROR("Kerberos 5 error");
32439384277SMark Murray 		retval = PAM_SERVICE_ERR;
32539384277SMark Murray 		goto cleanup3;
32639384277SMark Murray 	}
32739384277SMark Murray 
32839384277SMark Murray 	PAM_LOG("Done krb5_parse_name()");
32939384277SMark Murray 
33039384277SMark Murray 	/* Now convert the principal name into something human readable */
33139384277SMark Murray 	princ_name = NULL;
33239384277SMark Murray 	krbret = krb5_unparse_name(pam_context, princ, &princ_name);
33339384277SMark Murray 	if (krbret != 0) {
33439384277SMark Murray 		PAM_LOG("Error krb5_unparse_name(): %s", error_message(krbret));
33539384277SMark Murray 		PAM_VERBOSE_ERROR("Kerberos 5 error");
33639384277SMark Murray 		retval = PAM_SERVICE_ERR;
33739384277SMark Murray 		goto cleanup2;
33839384277SMark Murray 	}
33939384277SMark Murray 
34039384277SMark Murray 	PAM_LOG("Got principal: %s", princ_name);
34139384277SMark Murray 
34239384277SMark Murray 	/* Get password */
34339384277SMark Murray 	retval = pam_get_pass(pamh, &pass, PASSWORD_PROMPT, &options);
34439384277SMark Murray 	if (retval != PAM_SUCCESS)
34539384277SMark Murray 		goto cleanup2;
34639384277SMark Murray 
34739384277SMark Murray 	PAM_LOG("Got password");
34839384277SMark Murray 
34939384277SMark Murray 	/* Verify the local user exists (AFTER getting the password) */
35039384277SMark Murray 	if (strchr(user, '@')) {
35139384277SMark Murray 		/* get a local account name for this principal */
35239384277SMark Murray 		krbret = krb5_aname_to_localname(pam_context, princ,
35339384277SMark Murray 		    sizeof(luser), luser);
35439384277SMark Murray 		if (krbret != 0) {
35539384277SMark Murray 			PAM_VERBOSE_ERROR("Kerberos 5 error");
35639384277SMark Murray 			PAM_LOG("Error krb5_aname_to_localname(): %s",
35739384277SMark Murray 			    error_message(krbret));
35839384277SMark Murray 			retval = PAM_USER_UNKNOWN;
35939384277SMark Murray 			goto cleanup2;
36039384277SMark Murray 		}
36139384277SMark Murray 
36239384277SMark Murray 		retval = pam_set_item(pamh, PAM_USER, luser);
36339384277SMark Murray 		if (retval != PAM_SUCCESS)
36439384277SMark Murray 			goto cleanup2;
36539384277SMark Murray 
36639384277SMark Murray 		retval = pam_get_item(pamh, PAM_USER, (const void **)&user);
36739384277SMark Murray 		if (retval != PAM_SUCCESS)
36839384277SMark Murray 			goto cleanup2;
36939384277SMark Murray 
37039384277SMark Murray 		PAM_LOG("PAM_USER Redone");
37139384277SMark Murray 	}
37239384277SMark Murray 
37339384277SMark Murray 	pwd = getpwnam(user);
37439384277SMark Murray 	if (pwd == NULL) {
37539384277SMark Murray 		retval = PAM_USER_UNKNOWN;
37639384277SMark Murray 		goto cleanup2;
37739384277SMark Murray 	}
37839384277SMark Murray 
37939384277SMark Murray 	PAM_LOG("Done getpwnam()");
38039384277SMark Murray 
38139384277SMark Murray 	/* Get a TGT */
38239384277SMark Murray 	memset(&creds, 0, sizeof(krb5_creds));
38339384277SMark Murray 	krbret = krb5_get_init_creds_password(pam_context, &creds, princ,
38439384277SMark Murray 	    pass, NULL, pamh, 0, NULL, &opts);
38539384277SMark Murray 	if (krbret != 0) {
38639384277SMark Murray 		PAM_VERBOSE_ERROR("Kerberos 5 error");
38739384277SMark Murray 		PAM_LOG("Error krb5_get_init_creds_password(): %s",
38839384277SMark Murray 		    error_message(krbret));
38939384277SMark Murray 		retval = PAM_AUTH_ERR;
39039384277SMark Murray 		goto cleanup2;
39139384277SMark Murray 	}
39239384277SMark Murray 
39339384277SMark Murray 	PAM_LOG("Got TGT");
39439384277SMark Murray 
39539384277SMark Murray 	/* Generate a unique cache_name */
39639384277SMark Murray 	asprintf(&cache_name, "MEMORY:/tmp/%s.%d", service, getpid());
39739384277SMark Murray 	krbret = krb5_cc_resolve(pam_context, cache_name, &ccache);
39839384277SMark Murray 	free(cache_name);
39939384277SMark Murray 	if (krbret != 0) {
40039384277SMark Murray 		PAM_VERBOSE_ERROR("Kerberos 5 error");
40139384277SMark Murray 		PAM_LOG("Error krb5_cc_resolve(): %s", error_message(krbret));
40239384277SMark Murray 		retval = PAM_SERVICE_ERR;
40339384277SMark Murray 		goto cleanup;
40439384277SMark Murray 	}
40539384277SMark Murray 	krbret = krb5_cc_initialize(pam_context, ccache, princ);
40639384277SMark Murray 	if (krbret != 0) {
40739384277SMark Murray 		PAM_VERBOSE_ERROR("Kerberos 5 error");
40839384277SMark Murray 		PAM_LOG("Error krb5_cc_initialize(): %s", error_message(krbret));
40939384277SMark Murray 		retval = PAM_SERVICE_ERR;
41039384277SMark Murray 		goto cleanup;
41139384277SMark Murray 	}
41239384277SMark Murray 	krbret = krb5_cc_store_cred(pam_context, ccache, &creds);
41339384277SMark Murray 	if (krbret != 0) {
41439384277SMark Murray 		PAM_VERBOSE_ERROR("Kerberos 5 error");
41539384277SMark Murray 		PAM_LOG("Error krb5_cc_store_cred(): %s", error_message(krbret));
41639384277SMark Murray 		krb5_cc_destroy(pam_context, ccache);
41739384277SMark Murray 		retval = PAM_SERVICE_ERR;
41839384277SMark Murray 		goto cleanup;
41939384277SMark Murray 	}
42039384277SMark Murray 
42139384277SMark Murray 	PAM_LOG("Credentials stashed");
42239384277SMark Murray 
42339384277SMark Murray 	/* Verify them */
42439384277SMark Murray 	if (verify_krb_v5_tgt(pam_context, ccache, service,
42539384277SMark Murray 	    pam_test_option(&options, PAM_OPT_FORWARDABLE, NULL)) == -1) {
42639384277SMark Murray 		PAM_VERBOSE_ERROR("Kerberos 5 error");
42739384277SMark Murray 		krb5_cc_destroy(pam_context, ccache);
42839384277SMark Murray 		retval = PAM_AUTH_ERR;
42939384277SMark Murray 		goto cleanup;
43039384277SMark Murray 	}
43139384277SMark Murray 
43239384277SMark Murray 	PAM_LOG("Credentials stash verified");
43339384277SMark Murray 
43439384277SMark Murray 	retval = pam_get_data(pamh, "ccache", (const void **)&ccache_check);
43539384277SMark Murray 	if (retval == PAM_SUCCESS) {
43639384277SMark Murray 		krb5_cc_destroy(pam_context, ccache);
43739384277SMark Murray 		PAM_VERBOSE_ERROR("Kerberos 5 error");
43839384277SMark Murray 		retval = PAM_AUTH_ERR;
43939384277SMark Murray 		goto cleanup;
44039384277SMark Murray 	}
44139384277SMark Murray 
44239384277SMark Murray 	PAM_LOG("Credentials stash not pre-existing");
44339384277SMark Murray 
44439384277SMark Murray 	retval = pam_set_data(pamh, "ccache", ccache, cleanup_cache);
44539384277SMark Murray 	if (retval != 0) {
44639384277SMark Murray 		krb5_cc_destroy(pam_context, ccache);
44739384277SMark Murray 		PAM_VERBOSE_ERROR("Kerberos 5 error");
44839384277SMark Murray 		retval = PAM_SERVICE_ERR;
44939384277SMark Murray 		goto cleanup;
45039384277SMark Murray 	}
45139384277SMark Murray 
45239384277SMark Murray 	PAM_LOG("Credentials stash saved");
45339384277SMark Murray 
45439384277SMark Murray cleanup:
45539384277SMark Murray 	krb5_free_cred_contents(pam_context, &creds);
45639384277SMark Murray 	PAM_LOG("Done cleanup");
45739384277SMark Murray cleanup2:
45839384277SMark Murray 	krb5_free_principal(pam_context, princ);
45939384277SMark Murray 	PAM_LOG("Done cleanup2");
46039384277SMark Murray cleanup3:
46139384277SMark Murray 	if (princ_name)
46239384277SMark Murray 		free(princ_name);
46339384277SMark Murray 
46439384277SMark Murray 	krb5_free_context(pam_context);
46539384277SMark Murray 
46639384277SMark Murray 	PAM_LOG("Done cleanup3");
46739384277SMark Murray 
46839384277SMark Murray 	if (retval != PAM_SUCCESS)
46939384277SMark Murray 		PAM_VERBOSE_ERROR("Kerberos 5 refuses you");
47039384277SMark Murray 
47139384277SMark Murray 	PAM_RETURN(retval);
47239384277SMark Murray }
47339384277SMark Murray 
47439384277SMark Murray PAM_EXTERN int
47539384277SMark Murray pam_sm_setcred(pam_handle_t *pamh, int flags, int argc, const char **argv)
47639384277SMark Murray {
47739384277SMark Murray 
47839384277SMark Murray 	krb5_error_code krbret;
47939384277SMark Murray 	krb5_context pam_context;
48039384277SMark Murray 	krb5_principal princ;
48139384277SMark Murray 	krb5_creds creds;
48239384277SMark Murray 	krb5_ccache ccache_temp, ccache_perm;
48339384277SMark Murray 	krb5_cc_cursor cursor;
48439384277SMark Murray 	struct options options;
48539384277SMark Murray 	struct passwd *pwd = NULL;
48639384277SMark Murray 	int retval;
48739384277SMark Murray 	char *user;
48839384277SMark Murray 	char *cache_name, *cache_env_name, *p, *q;
48939384277SMark Murray 
49039384277SMark Murray 	uid_t euid;
49139384277SMark Murray 	gid_t egid;
49239384277SMark Murray 
49339384277SMark Murray 	pam_std_option(&options, other_options, argc, argv);
49439384277SMark Murray 
49539384277SMark Murray 	PAM_LOG("Options processed");
49639384277SMark Murray 
49739384277SMark Murray 	if (flags & PAM_DELETE_CRED)
49839384277SMark Murray 		PAM_RETURN(PAM_SUCCESS);
49939384277SMark Murray 
50039384277SMark Murray 	if (flags & PAM_REFRESH_CRED)
50139384277SMark Murray 		PAM_RETURN(PAM_SUCCESS);
50239384277SMark Murray 
50339384277SMark Murray 	if (flags & PAM_REINITIALIZE_CRED)
50439384277SMark Murray 		PAM_RETURN(PAM_SUCCESS);
50539384277SMark Murray 
50639384277SMark Murray 	if (!(flags & PAM_ESTABLISH_CRED))
50739384277SMark Murray 		PAM_RETURN(PAM_SERVICE_ERR);
50839384277SMark Murray 
50939384277SMark Murray 	PAM_LOG("Establishing credentials");
51039384277SMark Murray 
51139384277SMark Murray 	/* Get username */
51239384277SMark Murray 	retval = pam_get_item(pamh, PAM_USER, (const void **)&user);
51339384277SMark Murray 	if (retval != PAM_SUCCESS)
51439384277SMark Murray 		PAM_RETURN(retval);
51539384277SMark Murray 
51639384277SMark Murray 	PAM_LOG("Got user: %s", user);
51739384277SMark Murray 
51839384277SMark Murray 	krbret = krb5_init_context(&pam_context);
51939384277SMark Murray 	if (krbret != 0) {
52039384277SMark Murray 		PAM_LOG("Error krb5_init_context(): %s", error_message(krbret));
52139384277SMark Murray 		PAM_RETURN(PAM_SERVICE_ERR);
52239384277SMark Murray 	}
52339384277SMark Murray 
52439384277SMark Murray 	PAM_LOG("Context initialised");
52539384277SMark Murray 
52639384277SMark Murray 	euid = geteuid();	/* Usually 0 */
52739384277SMark Murray 	egid = getegid();
52839384277SMark Murray 
52939384277SMark Murray 	PAM_LOG("Got euid, egid: %d %d", euid, egid);
53039384277SMark Murray 
53139384277SMark Murray 	/* Retrieve the cache name */
53239384277SMark Murray 	retval = pam_get_data(pamh, "ccache", (const void **)&ccache_temp);
53339384277SMark Murray 	if (retval != PAM_SUCCESS)
53439384277SMark Murray 		goto cleanup3;
53539384277SMark Murray 
53639384277SMark Murray 	/* Get the uid. This should exist. */
53739384277SMark Murray 	pwd = getpwnam(user);
53839384277SMark Murray 	if (pwd == NULL) {
53939384277SMark Murray 		retval = PAM_USER_UNKNOWN;
54039384277SMark Murray 		goto cleanup3;
54139384277SMark Murray 	}
54239384277SMark Murray 
54339384277SMark Murray 	PAM_LOG("Done getpwnam()");
54439384277SMark Murray 
54539384277SMark Murray 	/* Avoid following a symlink as root */
54639384277SMark Murray 	if (setegid(pwd->pw_gid)) {
54739384277SMark Murray 		retval = PAM_SERVICE_ERR;
54839384277SMark Murray 		goto cleanup3;
54939384277SMark Murray 	}
55039384277SMark Murray 	if (seteuid(pwd->pw_uid)) {
55139384277SMark Murray 		retval = PAM_SERVICE_ERR;
55239384277SMark Murray 		goto cleanup3;
55339384277SMark Murray 	}
55439384277SMark Murray 
55539384277SMark Murray 	PAM_LOG("Done setegid() & seteuid()");
55639384277SMark Murray 
55739384277SMark Murray 	/* Get the cache name */
55839384277SMark Murray 	cache_name = NULL;
55939384277SMark Murray 	pam_test_option(&options, PAM_OPT_CCACHE, &cache_name);
56039384277SMark Murray 	if (cache_name == NULL)
56139384277SMark Murray 		asprintf(&cache_name, "FILE:/tmp/krb5cc_%d", pwd->pw_uid);
56239384277SMark Murray 
56339384277SMark Murray 	p = calloc(PATH_MAX + 16, sizeof(char));
56439384277SMark Murray 	q = cache_name;
56539384277SMark Murray 
56639384277SMark Murray 	if (p == NULL) {
56739384277SMark Murray 		PAM_LOG("Error malloc(): failure");
56839384277SMark Murray 		retval = PAM_BUF_ERR;
56939384277SMark Murray 		goto cleanup3;
57039384277SMark Murray 	}
57139384277SMark Murray 	cache_name = p;
57239384277SMark Murray 
57339384277SMark Murray 	/* convert %u and %p */
57439384277SMark Murray 	while (*q) {
57539384277SMark Murray 		if (*q == '%') {
57639384277SMark Murray 			q++;
57739384277SMark Murray 			if (*q == 'u') {
57839384277SMark Murray 				sprintf(p, "%d", pwd->pw_uid);
57939384277SMark Murray 				p += strlen(p);
58039384277SMark Murray 			}
58139384277SMark Murray 			else if (*q == 'p') {
58239384277SMark Murray 				sprintf(p, "%d", getpid());
58339384277SMark Murray 				p += strlen(p);
58439384277SMark Murray 			}
58539384277SMark Murray 			else {
58639384277SMark Murray 				/* Not a special token */
58739384277SMark Murray 				*p++ = '%';
58839384277SMark Murray 				q--;
58939384277SMark Murray 			}
59039384277SMark Murray 			q++;
59139384277SMark Murray 		}
59239384277SMark Murray 		else {
59339384277SMark Murray 			*p++ = *q++;
59439384277SMark Murray 		}
59539384277SMark Murray 	}
59639384277SMark Murray 
59739384277SMark Murray 	PAM_LOG("Got cache_name: %s", cache_name);
59839384277SMark Murray 
59939384277SMark Murray 	/* Initialize the new ccache */
60039384277SMark Murray 	krbret = krb5_cc_get_principal(pam_context, ccache_temp, &princ);
60139384277SMark Murray 	if (krbret != 0) {
60239384277SMark Murray 		PAM_LOG("Error krb5_cc_get_principal(): %s",
60339384277SMark Murray 		    error_message(krbret));
60439384277SMark Murray 		retval = PAM_SERVICE_ERR;
60539384277SMark Murray 		goto cleanup3;
60639384277SMark Murray 	}
60739384277SMark Murray 	krbret = krb5_cc_resolve(pam_context, cache_name, &ccache_perm);
60839384277SMark Murray 	if (krbret != 0) {
60939384277SMark Murray 		PAM_LOG("Error krb5_cc_resolve(): %s", error_message(krbret));
61039384277SMark Murray 		retval = PAM_SERVICE_ERR;
61139384277SMark Murray 		goto cleanup2;
61239384277SMark Murray 	}
61339384277SMark Murray 	krbret = krb5_cc_initialize(pam_context, ccache_perm, princ);
61439384277SMark Murray 	if (krbret != 0) {
61539384277SMark Murray 		PAM_LOG("Error krb5_cc_initialize(): %s", error_message(krbret));
61639384277SMark Murray 		retval = PAM_SERVICE_ERR;
61739384277SMark Murray 		goto cleanup2;
61839384277SMark Murray 	}
61939384277SMark Murray 
62039384277SMark Murray 	PAM_LOG("Cache initialised");
62139384277SMark Murray 
62239384277SMark Murray 	/* Prepare for iteration over creds */
62339384277SMark Murray 	krbret = krb5_cc_start_seq_get(pam_context, ccache_temp, &cursor);
62439384277SMark Murray 	if (krbret != 0) {
62539384277SMark Murray 		PAM_LOG("Error krb5_cc_start_seq_get(): %s", error_message(krbret));
62639384277SMark Murray 		krb5_cc_destroy(pam_context, ccache_perm);
62739384277SMark Murray 		retval = PAM_SERVICE_ERR;
62839384277SMark Murray 		goto cleanup2;
62939384277SMark Murray 	}
63039384277SMark Murray 
63139384277SMark Murray 	PAM_LOG("Prepared for iteration");
63239384277SMark Murray 
63339384277SMark Murray 	/* Copy the creds (should be two of them) */
63439384277SMark Murray 	while ((krbret = krb5_cc_next_cred(pam_context, ccache_temp,
63539384277SMark Murray 				&cursor, &creds) == 0)) {
63639384277SMark Murray 		krbret = krb5_cc_store_cred(pam_context, ccache_perm, &creds);
63739384277SMark Murray 		if (krbret != 0) {
63839384277SMark Murray 			PAM_LOG("Error krb5_cc_store_cred(): %s",
63939384277SMark Murray 			    error_message(krbret));
64039384277SMark Murray 			krb5_cc_destroy(pam_context, ccache_perm);
64139384277SMark Murray 			krb5_free_cred_contents(pam_context, &creds);
64239384277SMark Murray 			retval = PAM_SERVICE_ERR;
64339384277SMark Murray 			goto cleanup2;
64439384277SMark Murray 		}
64539384277SMark Murray 		krb5_free_cred_contents(pam_context, &creds);
64639384277SMark Murray 		PAM_LOG("Iteration");
64739384277SMark Murray 	}
64839384277SMark Murray 	krb5_cc_end_seq_get(pam_context, ccache_temp, &cursor);
64939384277SMark Murray 
65039384277SMark Murray 	PAM_LOG("Done iterating");
65139384277SMark Murray 
65239384277SMark Murray 	if (strstr(cache_name, "FILE:") == cache_name) {
65339384277SMark Murray 		if (chown(&cache_name[5], pwd->pw_uid, pwd->pw_gid) == -1) {
65439384277SMark Murray 			PAM_LOG("Error chown(): %s", strerror(errno));
65539384277SMark Murray 			krb5_cc_destroy(pam_context, ccache_perm);
65639384277SMark Murray 			retval = PAM_SERVICE_ERR;
65739384277SMark Murray 			goto cleanup2;
65839384277SMark Murray 		}
65939384277SMark Murray 		PAM_LOG("Done chown()");
66039384277SMark Murray 
66139384277SMark Murray 		if (chmod(&cache_name[5], (S_IRUSR | S_IWUSR)) == -1) {
66239384277SMark Murray 			PAM_LOG("Error chmod(): %s", strerror(errno));
66339384277SMark Murray 			krb5_cc_destroy(pam_context, ccache_perm);
66439384277SMark Murray 			retval = PAM_SERVICE_ERR;
66539384277SMark Murray 			goto cleanup2;
66639384277SMark Murray 		}
66739384277SMark Murray 		PAM_LOG("Done chmod()");
66839384277SMark Murray 	}
66939384277SMark Murray 
67039384277SMark Murray 	krb5_cc_close(pam_context, ccache_perm);
67139384277SMark Murray 
67239384277SMark Murray 	PAM_LOG("Cache closed");
67339384277SMark Murray 
67439384277SMark Murray 	cache_env_name = malloc(strlen(cache_name) + 12);
67539384277SMark Murray 	if (!cache_env_name) {
67639384277SMark Murray 		PAM_LOG("Error malloc(): failure");
67739384277SMark Murray 		krb5_cc_destroy(pam_context, ccache_perm);
67839384277SMark Murray 		retval = PAM_BUF_ERR;
67939384277SMark Murray 		goto cleanup2;
68039384277SMark Murray 	}
68139384277SMark Murray 
68239384277SMark Murray 	sprintf(cache_env_name, "KRB5CCNAME=%s", cache_name);
68339384277SMark Murray 	if ((retval = pam_putenv(pamh, cache_env_name)) != 0) {
68439384277SMark Murray 		PAM_LOG("Error pam_putenv(): %s", pam_strerror(pamh, retval));
68539384277SMark Murray 		krb5_cc_destroy(pam_context, ccache_perm);
68639384277SMark Murray 		retval = PAM_SERVICE_ERR;
68739384277SMark Murray 		goto cleanup2;
68839384277SMark Murray 	}
68939384277SMark Murray 
69039384277SMark Murray 	PAM_LOG("Environment done: KRB5CCNAME=%s", cache_name);
69139384277SMark Murray 
69239384277SMark Murray cleanup2:
69339384277SMark Murray 	krb5_free_principal(pam_context, princ);
69439384277SMark Murray 	PAM_LOG("Done cleanup2");
69539384277SMark Murray cleanup3:
69639384277SMark Murray 	krb5_free_context(pam_context);
69739384277SMark Murray 	PAM_LOG("Done cleanup3");
69839384277SMark Murray 
69939384277SMark Murray 	seteuid(euid);
70039384277SMark Murray 	setegid(egid);
70139384277SMark Murray 
70239384277SMark Murray 	PAM_LOG("Done seteuid() & setegid()");
70339384277SMark Murray 
70439384277SMark Murray 	PAM_RETURN(retval);
70539384277SMark Murray }
70639384277SMark Murray 
70739384277SMark Murray /*
70839384277SMark Murray  * account management
70939384277SMark Murray  */
71039384277SMark Murray PAM_EXTERN int
711c2065008SMark Murray pam_sm_acct_mgmt(pam_handle_t *pamh, int flags __unused, int argc, const char **argv)
71239384277SMark Murray {
71339384277SMark Murray 	krb5_error_code krbret;
71439384277SMark Murray 	krb5_context pam_context;
71539384277SMark Murray 	krb5_ccache ccache;
71639384277SMark Murray 	krb5_principal princ;
71739384277SMark Murray 	struct options options;
71839384277SMark Murray 	int retval;
71939384277SMark Murray 	const char *user;
72039384277SMark Murray 
72139384277SMark Murray 	pam_std_option(&options, other_options, argc, argv);
72239384277SMark Murray 
72339384277SMark Murray 	PAM_LOG("Options processed");
72439384277SMark Murray 
72539384277SMark Murray 	retval = pam_get_item(pamh, PAM_USER, (const void **)&user);
72639384277SMark Murray 	if (retval != PAM_SUCCESS)
72739384277SMark Murray 		PAM_RETURN(retval);
72839384277SMark Murray 
72939384277SMark Murray 	PAM_LOG("Got user: %s", user);
73039384277SMark Murray 
73139384277SMark Murray 	retval = pam_get_data(pamh, "ccache", (const void **)&ccache);
73239384277SMark Murray 	if (retval != PAM_SUCCESS)
73339384277SMark Murray 		PAM_RETURN(PAM_SUCCESS);
73439384277SMark Murray 
73539384277SMark Murray 	PAM_LOG("Got ccache");
73639384277SMark Murray 
73739384277SMark Murray 	krbret = krb5_init_context(&pam_context);
73839384277SMark Murray 	if (krbret != 0) {
73939384277SMark Murray 		PAM_LOG("Error krb5_init_context(): %s", error_message(krbret));
74039384277SMark Murray 		PAM_RETURN(PAM_PERM_DENIED);
74139384277SMark Murray 	}
74239384277SMark Murray 
74339384277SMark Murray 	PAM_LOG("Context initialised");
74439384277SMark Murray 
74539384277SMark Murray 	krbret = krb5_cc_get_principal(pam_context, ccache, &princ);
74639384277SMark Murray 	if (krbret != 0) {
74739384277SMark Murray 		PAM_LOG("Error krb5_cc_get_principal(): %s", error_message(krbret));
74839384277SMark Murray 		retval = PAM_PERM_DENIED;;
74939384277SMark Murray 		goto cleanup;
75039384277SMark Murray 	}
75139384277SMark Murray 
75239384277SMark Murray 	PAM_LOG("Got principal");
75339384277SMark Murray 
75439384277SMark Murray 	if (krb5_kuserok(pam_context, princ, user))
75539384277SMark Murray 		retval = PAM_SUCCESS;
75639384277SMark Murray 	else
75739384277SMark Murray 		retval = PAM_PERM_DENIED;
75839384277SMark Murray 	krb5_free_principal(pam_context, princ);
75939384277SMark Murray 
76039384277SMark Murray 	PAM_LOG("Done kuserok()");
76139384277SMark Murray 
76239384277SMark Murray cleanup:
76339384277SMark Murray 	krb5_free_context(pam_context);
76439384277SMark Murray 	PAM_LOG("Done cleanup");
76539384277SMark Murray 
76639384277SMark Murray 	PAM_RETURN(retval);
76739384277SMark Murray 
76839384277SMark Murray }
76939384277SMark Murray 
77039384277SMark Murray /*
77139384277SMark Murray  * session management
77239384277SMark Murray  *
77339384277SMark Murray  * logging only
77439384277SMark Murray  */
77539384277SMark Murray PAM_EXTERN int
776c2065008SMark Murray pam_sm_open_session(pam_handle_t *pamh __unused, int flags __unused, int argc, const char **argv)
77739384277SMark Murray {
77839384277SMark Murray 	struct options options;
77939384277SMark Murray 
78039384277SMark Murray 	pam_std_option(&options, NULL, argc, argv);
78139384277SMark Murray 
78239384277SMark Murray 	PAM_LOG("Options processed");
78339384277SMark Murray 
78439384277SMark Murray 	PAM_RETURN(PAM_SUCCESS);
78539384277SMark Murray }
78639384277SMark Murray 
78739384277SMark Murray PAM_EXTERN int
788c2065008SMark Murray pam_sm_close_session(pam_handle_t *pamh __unused, int flags __unused, int argc, const char **argv)
78939384277SMark Murray {
79039384277SMark Murray 	struct options options;
79139384277SMark Murray 
79239384277SMark Murray 	pam_std_option(&options, NULL, argc, argv);
79339384277SMark Murray 
79439384277SMark Murray 	PAM_LOG("Options processed");
79539384277SMark Murray 
79639384277SMark Murray 	PAM_RETURN(PAM_SUCCESS);
79739384277SMark Murray }
79839384277SMark Murray 
79939384277SMark Murray /*
80039384277SMark Murray  * password management
80139384277SMark Murray  */
80239384277SMark Murray PAM_EXTERN int
80339384277SMark Murray pam_sm_chauthtok(pam_handle_t *pamh, int flags, int argc, const char **argv)
80439384277SMark Murray {
80539384277SMark Murray 	krb5_error_code krbret;
80639384277SMark Murray 	krb5_context pam_context;
80739384277SMark Murray 	krb5_creds creds;
80839384277SMark Murray 	krb5_principal princ;
80939384277SMark Murray 	krb5_get_init_creds_opt opts;
81039384277SMark Murray 	krb5_data result_code_string, result_string;
81139384277SMark Murray 	struct options options;
81239384277SMark Murray 	int result_code, retval;
81339384277SMark Murray 	const char *user, *pass, *pass2;
81439384277SMark Murray 	char *princ_name;
81539384277SMark Murray 
81639384277SMark Murray 	pam_std_option(&options, other_options, argc, argv);
81739384277SMark Murray 
81839384277SMark Murray 	PAM_LOG("Options processed");
81939384277SMark Murray 
82039384277SMark Murray 	if (!(flags & PAM_UPDATE_AUTHTOK))
82139384277SMark Murray 		PAM_RETURN(PAM_AUTHTOK_ERR);
82239384277SMark Murray 
82339384277SMark Murray 	retval = pam_get_item(pamh, PAM_USER, (const void **)&user);
82439384277SMark Murray 	if (retval != PAM_SUCCESS)
82539384277SMark Murray 		PAM_RETURN(retval);
82639384277SMark Murray 
82739384277SMark Murray 	PAM_LOG("Got user: %s", user);
82839384277SMark Murray 
82939384277SMark Murray 	krbret = krb5_init_context(&pam_context);
83039384277SMark Murray 	if (krbret != 0) {
83139384277SMark Murray 		PAM_LOG("Error krb5_init_context(): %s", error_message(krbret));
83239384277SMark Murray 		PAM_RETURN(PAM_SERVICE_ERR);
83339384277SMark Murray 	}
83439384277SMark Murray 
83539384277SMark Murray 	PAM_LOG("Context initialised");
83639384277SMark Murray 
83739384277SMark Murray 	krb5_get_init_creds_opt_init(&opts);
83839384277SMark Murray 
83939384277SMark Murray 	PAM_LOG("Credentials options initialised");
84039384277SMark Murray 
84139384277SMark Murray 	/* Get principal name */
84239384277SMark Murray 	krbret = krb5_parse_name(pam_context, user, &princ);
84339384277SMark Murray 	if (krbret != 0) {
84439384277SMark Murray 		PAM_LOG("Error krb5_parse_name(): %s", error_message(krbret));
84539384277SMark Murray 		retval = PAM_USER_UNKNOWN;
84639384277SMark Murray 		goto cleanup3;
84739384277SMark Murray 	}
84839384277SMark Murray 
84939384277SMark Murray 	/* Now convert the principal name into something human readable */
85039384277SMark Murray 	princ_name = NULL;
85139384277SMark Murray 	krbret = krb5_unparse_name(pam_context, princ, &princ_name);
85239384277SMark Murray 	if (krbret != 0) {
85339384277SMark Murray 		PAM_LOG("Error krb5_unparse_name(): %s", error_message(krbret));
85439384277SMark Murray 		retval = PAM_SERVICE_ERR;
85539384277SMark Murray 		goto cleanup2;
85639384277SMark Murray 	}
85739384277SMark Murray 
85839384277SMark Murray 	PAM_LOG("Got principal: %s", princ_name);
85939384277SMark Murray 
86039384277SMark Murray 	/* Get password */
86139384277SMark Murray 	retval = pam_get_pass(pamh, &pass, PASSWORD_PROMPT, &options);
86239384277SMark Murray 	if (retval != PAM_SUCCESS)
86339384277SMark Murray 		goto cleanup2;
86439384277SMark Murray 
86539384277SMark Murray 	PAM_LOG("Got password");
86639384277SMark Murray 
86739384277SMark Murray 	memset(&creds, 0, sizeof(krb5_creds));
86839384277SMark Murray 	krbret = krb5_get_init_creds_password(pam_context, &creds, princ,
86939384277SMark Murray 	    pass, NULL, pamh, 0, "kadmin/changepw", &opts);
87039384277SMark Murray 	if (krbret != 0) {
87139384277SMark Murray 		PAM_LOG("Error krb5_get_init_creds_password()",
87239384277SMark Murray 		    error_message(krbret));
87339384277SMark Murray 		retval = PAM_AUTH_ERR;
87439384277SMark Murray 		goto cleanup2;
87539384277SMark Murray 	}
87639384277SMark Murray 
87739384277SMark Murray 	PAM_LOG("Credentials established");
87839384277SMark Murray 
87939384277SMark Murray 	/* Now get the new password */
88039384277SMark Murray 	retval = pam_get_pass(pamh, &pass, NEW_PASSWORD_PROMPT, &options);
88139384277SMark Murray 	if (retval != PAM_SUCCESS)
88239384277SMark Murray 		goto cleanup;
88339384277SMark Murray 
88439384277SMark Murray 	retval = pam_get_pass(pamh, &pass2, NEW_PASSWORD_PROMPT_2, &options);
88539384277SMark Murray 	if (retval != PAM_SUCCESS)
88639384277SMark Murray 		goto cleanup;
88739384277SMark Murray 
88839384277SMark Murray 	PAM_LOG("Got new password twice");
88939384277SMark Murray 
89039384277SMark Murray 	if (strcmp(pass, pass2) != 0) {
89139384277SMark Murray 		PAM_LOG("Error strcmp(): passwords are different");
89239384277SMark Murray 		retval = PAM_AUTHTOK_ERR;
89339384277SMark Murray 		goto cleanup;
89439384277SMark Murray 	}
89539384277SMark Murray 
89639384277SMark Murray 	PAM_LOG("New passwords are the same");
89739384277SMark Murray 
89839384277SMark Murray 	/* Change it */
899c2065008SMark Murray 	krbret = krb5_change_password(pam_context, &creds, pass,
90039384277SMark Murray 	    &result_code, &result_code_string, &result_string);
90139384277SMark Murray 	if (krbret != 0) {
90239384277SMark Murray 		PAM_LOG("Error krb5_change_password(): %s",
90339384277SMark Murray 		    error_message(krbret));
90439384277SMark Murray 		retval = PAM_AUTHTOK_ERR;
90539384277SMark Murray 		goto cleanup;
90639384277SMark Murray 	}
90739384277SMark Murray 	if (result_code) {
90839384277SMark Murray 		PAM_LOG("Error krb5_change_password(): (result_code)");
90939384277SMark Murray 		retval = PAM_AUTHTOK_ERR;
91039384277SMark Murray 		goto cleanup;
91139384277SMark Murray 	}
91239384277SMark Murray 
91339384277SMark Murray 	PAM_LOG("Password changed");
91439384277SMark Murray 
91539384277SMark Murray 	if (result_string.data)
91639384277SMark Murray 		free(result_string.data);
91739384277SMark Murray 	if (result_code_string.data)
91839384277SMark Murray 		free(result_code_string.data);
91939384277SMark Murray 
92039384277SMark Murray cleanup:
92139384277SMark Murray 	krb5_free_cred_contents(pam_context, &creds);
92239384277SMark Murray 	PAM_LOG("Done cleanup");
92339384277SMark Murray cleanup2:
92439384277SMark Murray 	krb5_free_principal(pam_context, princ);
92539384277SMark Murray 	PAM_LOG("Done cleanup2");
92639384277SMark Murray cleanup3:
92739384277SMark Murray 	if (princ_name)
92839384277SMark Murray 		free(princ_name);
92939384277SMark Murray 
93039384277SMark Murray 	krb5_free_context(pam_context);
93139384277SMark Murray 
93239384277SMark Murray 	PAM_LOG("Done cleanup3");
93339384277SMark Murray 
93439384277SMark Murray 	PAM_RETURN(retval);
93539384277SMark Murray }
93639384277SMark Murray 
93739384277SMark Murray PAM_MODULE_ENTRY("pam_krb5");
93839384277SMark Murray 
93939384277SMark Murray /*
94039384277SMark Murray  * This routine with some modification is from the MIT V5B6 appl/bsd/login.c
94139384277SMark Murray  * Modified by Sam Hartman <hartmans@mit.edu> to support PAM services
94239384277SMark Murray  * for Debian.
94339384277SMark Murray  *
94439384277SMark Murray  * Verify the Kerberos ticket-granting ticket just retrieved for the
94539384277SMark Murray  * user.  If the Kerberos server doesn't respond, assume the user is
94639384277SMark Murray  * trying to fake us out (since we DID just get a TGT from what is
94739384277SMark Murray  * supposedly our KDC).  If the host/<host> service is unknown (i.e.,
94839384277SMark Murray  * the local keytab doesn't have it), and we cannot find another
94939384277SMark Murray  * service we do have, let her in.
95039384277SMark Murray  *
95139384277SMark Murray  * Returns 1 for confirmation, -1 for failure, 0 for uncertainty.
95239384277SMark Murray  */
95339384277SMark Murray static int
95439384277SMark Murray verify_krb_v5_tgt(krb5_context context, krb5_ccache ccache,
95539384277SMark Murray     char *pam_service, int debug)
95639384277SMark Murray {
95739384277SMark Murray 	krb5_error_code retval;
95839384277SMark Murray 	krb5_principal princ;
95939384277SMark Murray 	krb5_keyblock *keyblock;
96039384277SMark Murray 	krb5_data packet;
96139384277SMark Murray 	krb5_auth_context auth_context;
962c2065008SMark Murray 	char phost[BUFSIZ];
963c2065008SMark Murray 	const char *services[3], **service;
96439384277SMark Murray 
96539384277SMark Murray 	packet.data = 0;
96639384277SMark Murray 
96739384277SMark Murray 	/* If possible we want to try and verify the ticket we have
96839384277SMark Murray 	 * received against a keytab.  We will try multiple service
96939384277SMark Murray 	 * principals, including at least the host principal and the PAM
97039384277SMark Murray 	 * service principal.  The host principal is preferred because access
97139384277SMark Murray 	 * to that key is generally sufficient to compromise root, while the
97239384277SMark Murray 	 * service key for this PAM service may be less carefully guarded.
97339384277SMark Murray 	 * It is important to check the keytab first before the KDC so we do
97439384277SMark Murray 	 * not get spoofed by a fake KDC.
97539384277SMark Murray 	 */
97639384277SMark Murray 	services[0] = "host";
97739384277SMark Murray 	services[1] = pam_service;
97839384277SMark Murray 	services[2] = NULL;
97939384277SMark Murray 	keyblock = 0;
98039384277SMark Murray 	retval = -1;
98139384277SMark Murray 	for (service = &services[0]; *service != NULL; service++) {
98239384277SMark Murray 		retval = krb5_sname_to_principal(context, NULL, *service,
98339384277SMark Murray 		    KRB5_NT_SRV_HST, &princ);
98439384277SMark Murray 		if (retval != 0) {
98539384277SMark Murray 			if (debug)
98639384277SMark Murray 				syslog(LOG_DEBUG, "pam_krb5: verify_krb_v5_tgt(): %s: %s", "krb5_sname_to_principal()", error_message(retval));
98739384277SMark Murray 			return -1;
98839384277SMark Murray 		}
98939384277SMark Murray 
99039384277SMark Murray 		/* Extract the name directly. */
99139384277SMark Murray 		strncpy(phost, compat_princ_component(context, princ, 1),
99239384277SMark Murray 		    BUFSIZ);
99339384277SMark Murray 		phost[BUFSIZ - 1] = '\0';
99439384277SMark Murray 
99539384277SMark Murray 		/*
99639384277SMark Murray 	         * Do we have service/<host> keys?
99739384277SMark Murray 	         * (use default/configured keytab, kvno IGNORE_VNO to get the
99839384277SMark Murray 	         * first match, and ignore enctype.)
99939384277SMark Murray 	         */
100039384277SMark Murray 		retval = krb5_kt_read_service_key(context, NULL, princ, 0, 0,
100139384277SMark Murray 		    &keyblock);
100239384277SMark Murray 		if (retval != 0)
100339384277SMark Murray 			continue;
100439384277SMark Murray 		break;
100539384277SMark Murray 	}
100639384277SMark Murray 	if (retval != 0) {	/* failed to find key */
100739384277SMark Murray 		/* Keytab or service key does not exist */
100839384277SMark Murray 		if (debug)
100939384277SMark Murray 			syslog(LOG_DEBUG, "pam_krb5: verify_krb_v5_tgt(): %s: %s", "krb5_kt_read_service_key()", error_message(retval));
101039384277SMark Murray 		retval = 0;
101139384277SMark Murray 		goto cleanup;
101239384277SMark Murray 	}
101339384277SMark Murray 	if (keyblock)
101439384277SMark Murray 		krb5_free_keyblock(context, keyblock);
101539384277SMark Murray 
101639384277SMark Murray 	/* Talk to the kdc and construct the ticket. */
101739384277SMark Murray 	auth_context = NULL;
101839384277SMark Murray 	retval = krb5_mk_req(context, &auth_context, 0, *service, phost,
101939384277SMark Murray 		NULL, ccache, &packet);
102039384277SMark Murray 	if (auth_context) {
102139384277SMark Murray 		krb5_auth_con_free(context, auth_context);
102239384277SMark Murray 		auth_context = NULL;	/* setup for rd_req */
102339384277SMark Murray 	}
102439384277SMark Murray 	if (retval) {
102539384277SMark Murray 		if (debug)
102639384277SMark Murray 			syslog(LOG_DEBUG, "pam_krb5: verify_krb_v5_tgt(): %s: %s", "krb5_mk_req()", error_message(retval));
102739384277SMark Murray 		retval = -1;
102839384277SMark Murray 		goto cleanup;
102939384277SMark Murray 	}
103039384277SMark Murray 
103139384277SMark Murray 	/* Try to use the ticket. */
103239384277SMark Murray 	retval = krb5_rd_req(context, &auth_context, &packet, princ, NULL,
103339384277SMark Murray 	    NULL, NULL);
103439384277SMark Murray 	if (retval) {
103539384277SMark Murray 		if (debug)
103639384277SMark Murray 			syslog(LOG_DEBUG, "pam_krb5: verify_krb_v5_tgt(): %s: %s", "krb5_rd_req()", error_message(retval));
103739384277SMark Murray 		retval = -1;
103839384277SMark Murray 	}
103939384277SMark Murray 	else
104039384277SMark Murray 		retval = 1;
104139384277SMark Murray 
104239384277SMark Murray cleanup:
104339384277SMark Murray 	if (packet.data)
104439384277SMark Murray 		compat_free_data_contents(context, &packet);
104539384277SMark Murray 	krb5_free_principal(context, princ);
104639384277SMark Murray 	return retval;
104739384277SMark Murray }
104839384277SMark Murray 
104939384277SMark Murray /* Free the memory for cache_name. Called by pam_end() */
105039384277SMark Murray static void
1051c2065008SMark Murray cleanup_cache(pam_handle_t *pamh __unused, void *data, int pam_end_status __unused)
105239384277SMark Murray {
105339384277SMark Murray 	krb5_context pam_context;
105439384277SMark Murray 	krb5_ccache ccache;
105539384277SMark Murray 
105639384277SMark Murray 	if (krb5_init_context(&pam_context))
105739384277SMark Murray 		return;
105839384277SMark Murray 
105939384277SMark Murray 	ccache = (krb5_ccache)data;
106039384277SMark Murray 	krb5_cc_destroy(pam_context, ccache);
106139384277SMark Murray 	krb5_free_context(pam_context);
106239384277SMark Murray }
106339384277SMark Murray 
106439384277SMark Murray #ifdef COMPAT_HEIMDAL
106539384277SMark Murray #ifdef COMPAT_MIT
106639384277SMark Murray #error This cannot be MIT and Heimdal compatible!
106739384277SMark Murray #endif
106839384277SMark Murray #endif
106939384277SMark Murray 
107039384277SMark Murray #ifndef COMPAT_HEIMDAL
107139384277SMark Murray #ifndef COMPAT_MIT
107239384277SMark Murray #error One of COMPAT_MIT and COMPAT_HEIMDAL must be specified!
107339384277SMark Murray #endif
107439384277SMark Murray #endif
107539384277SMark Murray 
107639384277SMark Murray #ifdef COMPAT_HEIMDAL
107739384277SMark Murray static const char *
1078c2065008SMark Murray compat_princ_component(krb5_context context __unused, krb5_principal princ, int n)
107939384277SMark Murray {
108039384277SMark Murray 	return princ->name.name_string.val[n];
108139384277SMark Murray }
108239384277SMark Murray 
108339384277SMark Murray static void
1084c2065008SMark Murray compat_free_data_contents(krb5_context context __unused, krb5_data * data)
108539384277SMark Murray {
108639384277SMark Murray 	krb5_xfree(data->data);
108739384277SMark Murray }
108839384277SMark Murray #endif
108939384277SMark Murray 
109039384277SMark Murray #ifdef COMPAT_MIT
109139384277SMark Murray static const char *
109239384277SMark Murray compat_princ_component(krb5_context context, krb5_principal princ, int n)
109339384277SMark Murray {
109439384277SMark Murray 	return krb5_princ_component(context, princ, n)->data;
109539384277SMark Murray }
109639384277SMark Murray 
109739384277SMark Murray static void
109839384277SMark Murray compat_free_data_contents(krb5_context context, krb5_data * data)
109939384277SMark Murray {
110039384277SMark Murray 	krb5_free_data_contents(context, data);
110139384277SMark Murray }
110239384277SMark Murray #endif
1103