xref: /illumos-gate/usr/src/lib/pam_modules/krb5/krb5_setcred.c (revision 4fceebdf03eeac0d7c58a4f70cc19b00a8c40a73)
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 
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 = 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 
255 out:
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 
276 static int
277 attempt_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  */
357 static krb5_error_code
358 krb5_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	*credsp = &creds;
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((char *)credsp, 0, sizeof (krb5_creds));
387 	if ((retval = krb5_copy_principal(kmd->kcontext,
388 				server, &credsp->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, &credsp->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, &credsp))) {
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, &credsp))) {
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 
595 cleanup_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 				return (KRB5KRB_ERR_GENERIC);
630 			}
631 			if (!(filepath = strchr(kmd->env, ':')) ||
632 			    !(filepath+1)) {
633 				__pam_log(LOG_AUTH | LOG_ERR,
634 					"PAM-KRB5 (setcred): Invalid pathname "
635 					"for credential cache of user `%s'",
636 					username);
637 				return (KRB5KRB_ERR_GENERIC);
638 			}
639 			if (chown(filepath+1, uuid, ugid)) {
640 				if (kmd->debug)
641 					__pam_log(LOG_AUTH | 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 				__pam_log(LOG_AUTH | LOG_NOTICE,
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 (PAM_SUCCESS);
690 
691 	if (kmd->debug) {
692 		__pam_log(LOG_AUTH | 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 (PAM_SUCCESS);
700 }
701