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