krb5_setcred.c revision 3924:4a2c8e3e6786
1/*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21/*
22 * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
23 * Use is subject to license terms.
24 */
25
26#pragma ident	"%Z%%M%	%I%	%E% SMI"
27
28#include <libintl.h>
29#include <security/pam_appl.h>
30#include <security/pam_modules.h>
31#include <string.h>
32#include <stdio.h>
33#include <stdlib.h>
34#include <sys/types.h>
35#include <pwd.h>
36#include <syslog.h>
37#include <libintl.h>
38#include <krb5.h>
39#include <netdb.h>
40#include <unistd.h>
41#include <sys/stat.h>
42#include <fcntl.h>
43#include <errno.h>
44#include <com_err.h>
45
46#include "utils.h"
47#include "krb5_repository.h"
48
49#define	PAMTXD			"SUNW_OST_SYSOSPAM"
50#define	KRB5_DEFAULT_LIFE	60*60*10  /* 10 hours */
51
52extern void krb5_cleanup(pam_handle_t *, void *, int);
53
54static int attempt_refresh_cred(krb5_module_data_t *, char *, int);
55static int attempt_delete_initcred(krb5_module_data_t *);
56static krb5_error_code krb5_renew_tgt(krb5_module_data_t *, krb5_principal,
57		krb5_principal, int);
58static krb5_boolean creds_match(krb5_context, const krb5_creds *,
59	const krb5_creds *);
60
61extern uint_t kwarn_add_warning(char *, int);
62extern uint_t kwarn_del_warning(char *);
63
64/*
65 * pam_sm_setcred
66 */
67int
68pam_sm_setcred(
69	pam_handle_t *pamh,
70	int	flags,
71	int	argc,
72	const char **argv)
73{
74	int	i;
75	int	err = 0;
76	int	debug = 0;
77	krb5_module_data_t	*kmd = NULL;
78	char			*user = NULL;
79	int			result;
80	krb5_repository_data_t	*krb5_data = NULL;
81	pam_repository_t	*rep_data = NULL;
82
83	for (i = 0; i < argc; i++) {
84		if (strcasecmp(argv[i], "debug") == 0)
85			debug = 1;
86		else if (strcasecmp(argv[i], "nowarn") == 0)
87			flags = flags | PAM_SILENT;
88	}
89
90	if (debug)
91		__pam_log(LOG_AUTH | LOG_DEBUG,
92		    "PAM-KRB5 (setcred): start: nowarn = %d, flags = 0x%x",
93		    flags & PAM_SILENT ? 1 : 0, flags);
94
95	/* make sure flags are valid */
96	if (flags &&
97	    !(flags & PAM_ESTABLISH_CRED) &&
98	    !(flags & PAM_REINITIALIZE_CRED) &&
99	    !(flags & PAM_REFRESH_CRED) &&
100	    !(flags & PAM_DELETE_CRED) &&
101	    !(flags & PAM_SILENT)) {
102		__pam_log(LOG_AUTH | LOG_ERR,
103			    "PAM-KRB5 (setcred): illegal flag %d", flags);
104		err = PAM_SYSTEM_ERR;
105		goto out;
106	}
107
108	(void) pam_get_item(pamh, PAM_USER, (void**) &user);
109
110	if (user == NULL || *user == '\0')
111		return (PAM_USER_UNKNOWN);
112
113	if (pam_get_data(pamh, KRB5_DATA, (const void**)&kmd) != PAM_SUCCESS) {
114		if (debug) {
115			__pam_log(LOG_AUTH | LOG_DEBUG,
116			    "PAM-KRB5 (setcred): kmd get failed, kmd=0x%p",
117			    kmd);
118		}
119
120		/*
121		 * User  doesn't need to authenticate for PAM_REFRESH_CRED
122		 * or for PAM_DELETE_CRED
123		 */
124		if (flags & (PAM_REFRESH_CRED|PAM_DELETE_CRED)) {
125			__pam_log(LOG_AUTH | LOG_DEBUG,
126				"PAM-KRB5 (setcred): inst kmd structure");
127
128			kmd = calloc(1, sizeof (krb5_module_data_t));
129
130			if (kmd == NULL) {
131				result = PAM_BUF_ERR;
132				return (result);
133			}
134
135			if ((err = pam_set_data(pamh, KRB5_DATA,
136				kmd, &krb5_cleanup)) != PAM_SUCCESS) {
137				free(kmd);
138				return (PAM_SYSTEM_ERR);
139			}
140		} else {
141				err = PAM_CRED_UNAVAIL;
142				goto out;
143		}
144
145	} else {  /* pam_get_data success */
146		if (kmd == NULL) {
147			if (debug) {
148				__pam_log(LOG_AUTH | LOG_DEBUG,
149				    "PAM-KRB5 (setcred): kmd structure"
150				    " gotten but is NULL for user %s", user);
151			}
152			err = PAM_CRED_UNAVAIL;
153			goto out;
154		}
155
156		if (debug)
157			__pam_log(LOG_AUTH | LOG_DEBUG,
158			    "PAM-KRB5 (setcred): kmd auth_status: %s",
159			    pam_strerror(pamh, kmd->auth_status));
160
161		/*
162		 * pam_auth has set status to ignore, so we also return ignore
163		 */
164		if (kmd->auth_status == PAM_IGNORE) {
165			err = PAM_IGNORE;
166			goto out;
167		}
168	}
169
170	kmd->debug = debug;
171
172	/*
173	 * User must have passed pam_authenticate()
174	 * in order to use PAM_ESTABLISH_CRED or PAM_REINITIALIZE_CRED
175	 */
176	if ((flags & (PAM_ESTABLISH_CRED|PAM_REINITIALIZE_CRED)) &&
177	    (kmd->auth_status != PAM_SUCCESS)) {
178		if (kmd->debug)
179			__pam_log(LOG_AUTH | LOG_DEBUG,
180			    "PAM-KRB5 (setcred): unable to "
181			    "setcreds, not authenticated!");
182		return (PAM_CRED_UNAVAIL);
183	}
184
185	/*
186	 * We cannot assume that kmd->kcontext being non-NULL
187	 * means it is valid.  Other pam_krb5 mods may have
188	 * freed it but not reset it to NULL.
189	 * Log a message when debugging to track down memory
190	 * leaks.
191	 */
192	if (kmd->kcontext != NULL && kmd->debug)
193		__pam_log(LOG_AUTH | LOG_DEBUG,
194			"PAM-KRB5 (setcred): kcontext != NULL, "
195			"possible memory leak.");
196
197	/*
198	 * Use the authenticated and validated user, if applicable.
199	 */
200	if (kmd->user != NULL)
201		user = kmd->user;
202
203	/*
204	 * If auth was short-circuited we will not have anything to
205	 * renew, so just return here.
206	 */
207	(void) pam_get_item(pamh, PAM_REPOSITORY, (void **)&rep_data);
208
209	if (rep_data != NULL) {
210		if (strcmp(rep_data->type, KRB5_REPOSITORY_NAME) != 0) {
211			if (debug)
212				__pam_log(LOG_AUTH | LOG_DEBUG,
213					"PAM-KRB5 (setcred): wrong"
214					"repository found (%s), returning "
215					"PAM_IGNORE", rep_data->type);
216			return (PAM_IGNORE);
217		}
218		if (rep_data->scope_len == sizeof (krb5_repository_data_t)) {
219			krb5_data = (krb5_repository_data_t *)rep_data->scope;
220
221			if (krb5_data->flags ==
222				SUNW_PAM_KRB5_ALREADY_AUTHENTICATED &&
223				krb5_data->principal != NULL &&
224				strlen(krb5_data->principal)) {
225				if (debug)
226					__pam_log(LOG_AUTH | LOG_DEBUG,
227						"PAM-KRB5 (setcred): "
228						"Principal %s already "
229						"authenticated, "
230						"cannot setcred",
231						krb5_data->principal);
232				return (PAM_SUCCESS);
233			}
234		}
235	}
236
237	if (flags & PAM_REINITIALIZE_CRED)
238		err = attempt_refresh_cred(kmd, user, PAM_REINITIALIZE_CRED);
239	else if (flags & PAM_REFRESH_CRED)
240		err = attempt_refresh_cred(kmd, user, PAM_REFRESH_CRED);
241	else if (flags & PAM_DELETE_CRED)
242		err = attempt_delete_initcred(kmd);
243	else {
244		/*
245		 * Default case:  PAM_ESTABLISH_CRED
246		 */
247		err = attempt_refresh_cred(kmd, user, PAM_ESTABLISH_CRED);
248	}
249
250	if (err != PAM_SUCCESS)
251		__pam_log(LOG_AUTH | LOG_ERR,
252		    "PAM-KRB5 (setcred): pam_setcred failed "
253		    "for %s (%s).", user, pam_strerror(pamh, err));
254
255out:
256	if (kmd && kmd->kcontext) {
257		/*
258		 * free 'kcontext' field if it is allocated,
259		 * kcontext is local to the operation being performed
260		 * not considered global to the entire pam module.
261		 */
262		krb5_free_context(kmd->kcontext);
263		kmd->kcontext = NULL;
264	}
265
266	/*
267	 * 'kmd' is not freed here, it is handled in krb5_cleanup
268	 */
269	if (debug)
270		__pam_log(LOG_AUTH | LOG_DEBUG,
271		    "PAM-KRB5 (setcred): end: %s",
272		    pam_strerror(pamh, err));
273	return (err);
274}
275
276static int
277attempt_refresh_cred(
278	krb5_module_data_t	*kmd,
279	char		*user,
280	int	flag)
281{
282	krb5_principal	me;
283	krb5_principal	server;
284	krb5_error_code	code;
285	char		kuser[2*MAXHOSTNAMELEN];
286	krb5_data tgtname = {
287		0,
288		KRB5_TGS_NAME_SIZE,
289		KRB5_TGS_NAME
290	};
291
292	/* User must have passed pam_authenticate() */
293	if (kmd->auth_status != PAM_SUCCESS) {
294		if (kmd->debug)
295			__pam_log(LOG_AUTH | LOG_DEBUG,
296			    "PAM-KRB5 (setcred): unable to "
297			    "setcreds, not authenticated!");
298		return (PAM_CRED_UNAVAIL);
299	}
300
301	/* Create a new context here. */
302	if (krb5_init_context(&kmd->kcontext) != 0) {
303		if (kmd->debug)
304			__pam_log(LOG_AUTH | LOG_DEBUG,
305			    "PAM-KRB5 (setcred): unable to "
306			    "initialize krb5 context");
307		return (PAM_SYSTEM_ERR);
308	}
309
310	if (krb5_cc_default(kmd->kcontext, &kmd->ccache) != 0) {
311		return (PAM_SYSTEM_ERR);
312	}
313
314	if ((code = get_kmd_kuser(kmd->kcontext, (const char *)user, kuser,
315		2*MAXHOSTNAMELEN)) != 0) {
316		return (code);
317	}
318
319	if (krb5_parse_name(kmd->kcontext, kuser, &me) != 0) {
320		return (PAM_SYSTEM_ERR);
321	}
322
323	if (code = krb5_build_principal_ext(kmd->kcontext, &server,
324			    krb5_princ_realm(kmd->kcontext, me)->length,
325			    krb5_princ_realm(kmd->kcontext, me)->data,
326			    tgtname.length, tgtname.data,
327			    krb5_princ_realm(kmd->kcontext, me)->length,
328			    krb5_princ_realm(kmd->kcontext, me)->data, 0)) {
329		krb5_free_principal(kmd->kcontext, me);
330		return (PAM_SYSTEM_ERR);
331	}
332
333	code = krb5_renew_tgt(kmd, me, server, flag);
334
335	krb5_free_principal(kmd->kcontext, server);
336	krb5_free_principal(kmd->kcontext, me);
337
338	if (code) {
339		if (kmd->debug)
340			__pam_log(LOG_AUTH | LOG_DEBUG,
341				"PAM-KRB5(setcred): krb5_renew_tgt() "
342				"failed: %s", error_message((errcode_t)code));
343		return (PAM_CRED_ERR);
344	} else {
345		return (PAM_SUCCESS);
346	}
347}
348
349/*
350 * This code will update the credential matching "server" in the user's
351 * credential cache.  The flag may be set to one of:
352 * PAM_ESTABLISH_CRED -  Create a new cred cache if one doesnt exist,
353 *                       else refresh the existing one.
354 * PAM_REINITIALIZE_CRED  - destroy current cred cache and create a new one
355 * PAM_REFRESH_CRED  - update the existing cred cache (default action)
356 */
357static krb5_error_code
358krb5_renew_tgt(
359	krb5_module_data_t *kmd,
360	krb5_principal	me,
361	krb5_principal	server,
362	int	flag)
363{
364	krb5_error_code	retval;
365	krb5_creds	creds;
366	krb5_creds	*renewed_cred = NULL;
367	char		*client_name = NULL;
368	typedef struct _cred_node {
369		krb5_creds		*creds;
370		struct _cred_node	*next;
371	} cred_node;
372	cred_node *cred_list_head = NULL;
373	cred_node *fetched = NULL;
374
375#define	my_creds	(kmd->initcreds)
376
377	if ((flag != PAM_REFRESH_CRED) &&
378		(flag != PAM_REINITIALIZE_CRED) &&
379		(flag != PAM_ESTABLISH_CRED))
380			return (KRB5KRB_ERR_GENERIC);
381
382	/* this is needed only for the ktkt_warnd */
383	if ((retval = krb5_unparse_name(kmd->kcontext, me, &client_name)) != 0)
384		return (retval);
385
386	(void) memset(&creds, 0, sizeof (krb5_creds));
387	if ((retval = krb5_copy_principal(kmd->kcontext,
388				server, &creds.server))) {
389		if (kmd->debug)
390			__pam_log(LOG_AUTH | LOG_DEBUG,
391				"PAM-KRB5 (setcred): krb5_copy_principal "
392				"failed: %s",
393				error_message((errcode_t)retval));
394		goto cleanup_creds;
395	}
396
397	/* obtain ticket & session key */
398	retval = krb5_cc_get_principal(kmd->kcontext,
399				kmd->ccache, &creds.client);
400	if (retval && (kmd->debug))
401		__pam_log(LOG_AUTH | LOG_DEBUG,
402			"PAM-KRB5 (setcred): User not in cred "
403			"cache (%s)", error_message((errcode_t)retval));
404
405	if ((retval == KRB5_FCC_NOFILE) &&
406		(flag & (PAM_ESTABLISH_CRED|PAM_REINITIALIZE_CRED))) {
407		/*
408		 * Create a fresh ccache, and store the credentials
409		 * we got from pam_authenticate()
410		 */
411		if ((retval = krb5_cc_initialize(kmd->kcontext,
412				kmd->ccache, me)) != 0) {
413			__pam_log(LOG_AUTH | LOG_DEBUG,
414				"PAM-KRB5 (setcred): krb5_cc_initialize "
415				"failed: %s",
416				error_message((errcode_t)retval));
417			goto cleanup_creds;
418		} else if ((retval = krb5_cc_store_cred(kmd->kcontext,
419				kmd->ccache, &my_creds)) != 0) {
420			__pam_log(LOG_AUTH | LOG_DEBUG,
421				"PAM-KRB5 (setcred): krb5_cc_store_cred "
422				"failed: %s",
423				error_message((errcode_t)retval));
424			goto cleanup_creds;
425		}
426	} else if (retval) {
427		/*
428		 * We failed to get the user's credentials.
429		 * This might be due to permission error on the cache,
430		 * or maybe we are looking in the wrong cache file!
431		 */
432		__pam_log(LOG_AUTH | LOG_ERR,
433			"PAM-KRB5 (setcred): Cannot find creds"
434			" for %s (%s)",
435			client_name ? client_name : "(unknown)",
436			error_message((errcode_t)retval));
437
438	} else if (flag & PAM_REINITIALIZE_CRED) {
439		/*
440		 * This destroys the credential cache, and stores a new
441		 * krbtgt with updated startime, endtime and renewable
442		 * lifetime.
443		 */
444		creds.times.starttime = my_creds.times.starttime;
445		creds.times.endtime = my_creds.times.endtime;
446		creds.times.renew_till = my_creds.times.renew_till;
447		if ((retval = krb5_get_credentials_renew(kmd->kcontext, 0,
448				kmd->ccache, &creds, &renewed_cred))) {
449			if (kmd->debug)
450			    __pam_log(LOG_AUTH | LOG_DEBUG,
451				"PAM-KRB5 (setcred): krb5_get_credentials",
452				"_renew(reinitialize) failed: %s",
453				error_message((errcode_t)retval));
454			/* perhaps the tgt lifetime has expired */
455			if ((retval = krb5_cc_initialize(kmd->kcontext,
456					kmd->ccache, me)) != 0) {
457				goto cleanup_creds;
458			} else if ((retval = krb5_cc_store_cred(kmd->kcontext,
459					kmd->ccache, &my_creds)) != 0) {
460				goto cleanup_creds;
461			}
462		}
463	} else {
464		/*
465		 * Creds already exist, update them if possible.
466		 * We got here either with the ESTABLISH or REFRESH flag.
467		 *
468		 * The credential cache does exist, and we are going to
469		 * read in each cred, looking for our own.  When we find
470		 * a matching credential, we will update it, and store it.
471		 * Any nonmatching credentials are stored as is.
472		 *
473		 * Rules:
474		 *    TGT must exist in cache to get to this point.
475		 *	if flag == ESTABLISH
476		 *		refresh it if possible, else overwrite
477		 *		with new TGT, other tickets in cache remain
478		 *		unchanged.
479		 *	else if flag == REFRESH
480		 *		refresh it if possible, else return error.
481		 *		- Will not work if "R" flag is not set in
482		 *		original cred, we dont want to 2nd guess the
483		 *		intention of the person who created the
484		 *		existing TGT.
485		 *
486		 */
487		krb5_cc_cursor	cursor;
488		krb5_creds	nextcred;
489		boolean_t	found = 0;
490
491		if ((retval = krb5_cc_start_seq_get(kmd->kcontext,
492				kmd->ccache, &cursor)) != 0)
493			goto cleanup_creds;
494
495		while ((krb5_cc_next_cred(kmd->kcontext, kmd->ccache,
496					&cursor, &nextcred) == 0)) {
497			/* if two creds match, we just update the first */
498			if ((!found) && (creds_match(kmd->kcontext,
499						&nextcred, &creds))) {
500				/*
501				 * Mark it as found, don't store it
502				 * in the list or else it will be
503				 * stored twice later.
504				 */
505				found = 1;
506			} else {
507				/*
508				 * Add a new node to the list
509				 * of creds that must be replaced
510				 * in the cache later.
511				 */
512				cred_node *newnode = (cred_node *)malloc(
513						sizeof (cred_node));
514				if (newnode == NULL) {
515					retval = ENOMEM;
516					goto cleanup_creds;
517				}
518				newnode->creds = NULL;
519				newnode->next = NULL;
520
521				if (cred_list_head == NULL) {
522					cred_list_head = newnode;
523					fetched = cred_list_head;
524				} else {
525					fetched->next = newnode;
526					fetched = fetched->next;
527				}
528				retval = krb5_copy_creds(kmd->kcontext,
529						&nextcred, &fetched->creds);
530				if (retval)
531					goto cleanup_creds;
532			}
533		}
534
535		if ((retval = krb5_cc_end_seq_get(kmd->kcontext,
536					kmd->ccache, &cursor)) != 0)
537			goto cleanup_creds;
538
539		/*
540		 * If we found a matching cred, renew it.
541		 * This destroys the credential cache, if and only
542		 * if it passes.
543		 */
544		if (found &&
545		    (retval = krb5_get_credentials_renew(kmd->kcontext,
546				0, kmd->ccache, &creds, &renewed_cred))) {
547			if (kmd->debug)
548			    __pam_log(LOG_AUTH | LOG_DEBUG,
549				"PAM-KRB5 (setcred): krb5_get_credentials"
550				"_renew(update) failed: %s",
551				error_message((errcode_t)retval));
552			/*
553			 * If we only wanted to refresh the creds but failed
554			 * due to expiration, lack of "R" flag, or other
555			 * problems, return an error.  If we were trying to
556			 * establish new creds, add them to the cache.
557			 */
558			if ((retval = krb5_cc_initialize(kmd->kcontext,
559					kmd->ccache, me)) != 0) {
560				goto cleanup_creds;
561			} else if ((retval = krb5_cc_store_cred(kmd->kcontext,
562					kmd->ccache, &my_creds)) != 0) {
563				goto cleanup_creds;
564			}
565		}
566		/*
567		 * If no matching creds were found, we must
568		 * initialize the cache before we can store stuff
569		 * in it.
570		 */
571		if (!found) {
572			if ((retval = krb5_cc_initialize(kmd->kcontext,
573					kmd->ccache, me)) != 0) {
574				goto cleanup_creds;
575			}
576		}
577
578		/* now store all the other tickets */
579		fetched = cred_list_head;
580		while (fetched != NULL) {
581			retval = krb5_cc_store_cred(kmd->kcontext,
582					kmd->ccache, fetched->creds);
583			fetched = fetched->next;
584			if (retval) {
585			    if (kmd->debug)
586				__pam_log(LOG_AUTH | LOG_DEBUG,
587				    "PAM-KRB5(setcred): krb5_cc_store_cred() "
588				    "failed: %s",
589				    error_message((errcode_t)retval));
590			    goto cleanup_creds;
591			}
592		}
593	}
594
595cleanup_creds:
596	/* Cleanup the list of creds read from the cache if necessary */
597	fetched = cred_list_head;
598	while (fetched != NULL) {
599		cred_node *old = fetched;
600		/* Free the contents and the cred structure itself */
601		krb5_free_creds(kmd->kcontext, fetched->creds);
602		fetched = fetched->next;
603		free(old);
604	}
605
606	if ((retval == 0) && (client_name != NULL)) {
607		/*
608		 * Credential update was successful!
609		 *
610		 * We now chown the ccache to the appropriate uid/gid
611		 * combination, if its a FILE based ccache.
612		 */
613		if (strstr(kmd->env, "FILE:")) {
614			uid_t uuid;
615			gid_t ugid;
616			char *username = NULL, *tmpname = NULL;
617			char *filepath = NULL;
618
619			username = strdup(client_name);
620			if ((tmpname = strchr(username, '@')))
621				*tmpname = '\0';
622
623			if (get_pw_uid(username, &uuid) == 0 ||
624			    get_pw_gid(username, &ugid) == 0) {
625				__pam_log(LOG_AUTH | LOG_ERR,
626				    "PAM-KRB5 (setcred): Unable to "
627				    "find matching uid/gid pair for user `%s'",
628				    username);
629				retval = KRB5KRB_ERR_GENERIC;
630				goto error;
631			}
632			if (!(filepath = strchr(kmd->env, ':')) ||
633			    !(filepath+1)) {
634				__pam_log(LOG_AUTH | LOG_ERR,
635					"PAM-KRB5 (setcred): Invalid pathname "
636					"for credential cache of user `%s'",
637					username);
638				retval = KRB5KRB_ERR_GENERIC;
639				goto error;
640			}
641			if (chown(filepath+1, uuid, ugid)) {
642				if (kmd->debug)
643					__pam_log(LOG_AUTH | LOG_DEBUG,
644					    "PAM-KRB5 (setcred): chown to user "
645					    "`%s' failed for FILE=%s",
646					    username, filepath);
647			}
648
649			free(username);
650		}
651	}
652
653error:
654	if (retval == 0) {
655		krb5_timestamp endtime;
656
657		if (renewed_cred && renewed_cred->times.endtime != 0)
658			endtime = renewed_cred->times.endtime;
659		else
660			endtime = my_creds.times.endtime;
661
662		if (kmd->debug)
663			__pam_log(LOG_AUTH | LOG_DEBUG,
664				"PAM-KRB5 (setcred): delete/add warning");
665
666		kwarn_del_warning(client_name);
667		if (kwarn_add_warning(client_name, endtime) != 0) {
668			__pam_log(LOG_AUTH | LOG_NOTICE,
669				"PAM-KRB5 (setcred): kwarn_add_warning"
670				" failed: ktkt_warnd(1M) down?");
671		}
672	}
673
674	if (renewed_cred != NULL)
675		krb5_free_creds(kmd->kcontext, renewed_cred);
676
677	if (client_name != NULL)
678		free(client_name);
679
680	krb5_free_cred_contents(kmd->kcontext, &creds);
681
682	return (retval);
683}
684
685static krb5_boolean
686creds_match(krb5_context ctx, const krb5_creds *mcreds,
687	const krb5_creds *creds)
688{
689	char *s1, *s2, *c1, *c2;
690	krb5_unparse_name(ctx, mcreds->client, &c1);
691	krb5_unparse_name(ctx, mcreds->server, &s1);
692	krb5_unparse_name(ctx, creds->client, &c2);
693	krb5_unparse_name(ctx, creds->server, &s2);
694
695	return (krb5_principal_compare(ctx, mcreds->client, creds->client) &&
696		krb5_principal_compare(ctx, mcreds->server, creds->server));
697}
698
699/*
700 * Delete the user's credentials for this session
701 */
702static int
703attempt_delete_initcred(krb5_module_data_t *kmd)
704{
705	if (kmd == NULL)
706		return (PAM_SUCCESS);
707
708	if (kmd->debug) {
709		__pam_log(LOG_AUTH | LOG_DEBUG,
710			"PAM-KRB5 (setcred): deleting user's "
711			"credentials (initcreds)");
712	}
713	krb5_free_cred_contents(kmd->kcontext, &kmd->initcreds);
714	(void) memset((char *)&kmd->initcreds, 0, sizeof (krb5_creds));
715	kmd->auth_status = PAM_AUTHINFO_UNAVAIL;
716	return (PAM_SUCCESS);
717}
718