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