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 2008 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): "
594 					    "krb5_cc_store_cred() "
595 					    "failed: %s",
596 					    error_message((errcode_t)retval));
597 				goto cleanup_creds;
598 			}
599 		}
600 	}
601 
602 cleanup_creds:
603 	/* Cleanup the list of creds read from the cache if necessary */
604 	fetched = cred_list_head;
605 	while (fetched != NULL) {
606 		cred_node *old = fetched;
607 		/* Free the contents and the cred structure itself */
608 		krb5_free_creds(kmd->kcontext, fetched->creds);
609 		fetched = fetched->next;
610 		free(old);
611 	}
612 
613 	if ((retval == 0) && (client_name != NULL)) {
614 		/*
615 		 * Credential update was successful!
616 		 *
617 		 * We now chown the ccache to the appropriate uid/gid
618 		 * combination, if its a FILE based ccache.
619 		 */
620 		if (strstr(kmd->env, "FILE:")) {
621 			uid_t uuid;
622 			gid_t ugid;
623 			char *username = NULL, *tmpname = NULL;
624 			char *filepath = NULL;
625 
626 			username = strdup(client_name);
627 			if (username == NULL) {
628 				__pam_log(LOG_AUTH | LOG_ERR,
629 				    "PAM-KRB5 (setcred): Out of memory");
630 				retval = KRB5KRB_ERR_GENERIC;
631 				goto error;
632 			}
633 			if ((tmpname = strchr(username, '@')))
634 				*tmpname = '\0';
635 
636 			if (get_pw_uid(username, &uuid) == 0 ||
637 			    get_pw_gid(username, &ugid) == 0) {
638 				__pam_log(LOG_AUTH | LOG_ERR,
639 				    "PAM-KRB5 (setcred): Unable to "
640 				    "find matching uid/gid pair for user `%s'",
641 				    username);
642 				retval = KRB5KRB_ERR_GENERIC;
643 				goto error;
644 			}
645 			if (!(filepath = strchr(kmd->env, ':')) ||
646 			    !(filepath+1)) {
647 				__pam_log(LOG_AUTH | LOG_ERR,
648 				    "PAM-KRB5 (setcred): Invalid pathname "
649 				    "for credential cache of user `%s'",
650 				    username);
651 				retval = KRB5KRB_ERR_GENERIC;
652 				goto error;
653 			}
654 			if (chown(filepath+1, uuid, ugid)) {
655 				if (kmd->debug)
656 					__pam_log(LOG_AUTH | LOG_DEBUG,
657 					    "PAM-KRB5 (setcred): chown to user "
658 					    "`%s' failed for FILE=%s",
659 					    username, filepath);
660 			}
661 
662 			free(username);
663 		}
664 	}
665 
666 error:
667 	if (retval == 0) {
668 		krb5_timestamp endtime;
669 
670 		if (renewed_cred && renewed_cred->times.endtime != 0)
671 			endtime = renewed_cred->times.endtime;
672 		else
673 			endtime = my_creds.times.endtime;
674 
675 		if (kmd->debug)
676 			__pam_log(LOG_AUTH | LOG_DEBUG,
677 			    "PAM-KRB5 (setcred): delete/add warning");
678 
679 		kwarn_del_warning(client_name);
680 		if (kwarn_add_warning(client_name, endtime) != 0) {
681 			__pam_log(LOG_AUTH | LOG_NOTICE,
682 			    "PAM-KRB5 (setcred): kwarn_add_warning"
683 			    " failed: ktkt_warnd(1M) down?");
684 		}
685 	}
686 
687 	if (renewed_cred != NULL)
688 		krb5_free_creds(kmd->kcontext, renewed_cred);
689 
690 	if (client_name != NULL)
691 		free(client_name);
692 
693 	krb5_free_cred_contents(kmd->kcontext, &creds);
694 
695 	return (retval);
696 }
697 
698 static krb5_boolean
699 creds_match(krb5_context ctx, const krb5_creds *mcreds,
700 	const krb5_creds *creds)
701 {
702 	char *s1, *s2, *c1, *c2;
703 	krb5_unparse_name(ctx, mcreds->client, &c1);
704 	krb5_unparse_name(ctx, mcreds->server, &s1);
705 	krb5_unparse_name(ctx, creds->client, &c2);
706 	krb5_unparse_name(ctx, creds->server, &s2);
707 
708 	return (krb5_principal_compare(ctx, mcreds->client, creds->client) &&
709 	    krb5_principal_compare(ctx, mcreds->server, creds->server));
710 }
711 
712 /*
713  * Delete the user's credentials for this session
714  */
715 static int
716 attempt_delete_initcred(krb5_module_data_t *kmd)
717 {
718 	if (kmd == NULL)
719 		return (PAM_SUCCESS);
720 
721 	if (kmd->debug) {
722 		__pam_log(LOG_AUTH | LOG_DEBUG,
723 		    "PAM-KRB5 (setcred): deleting user's "
724 		    "credentials (initcreds)");
725 	}
726 	krb5_free_cred_contents(kmd->kcontext, &kmd->initcreds);
727 	(void) memset((char *)&kmd->initcreds, 0, sizeof (krb5_creds));
728 	kmd->auth_status = PAM_AUTHINFO_UNAVAIL;
729 	return (PAM_SUCCESS);
730 }
731