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