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