xref: /illumos-gate/usr/src/lib/pam_modules/krb5/krb5_password.c (revision a0e56b0eb1fdc159ff8348ca0e77d884bb7d126b)
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 2006 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 <kadm5/admin.h>
29 #include <krb5.h>
30 
31 #include <security/pam_appl.h>
32 #include <security/pam_modules.h>
33 #include <security/pam_impl.h>
34 #include <syslog.h>
35 #include <string.h>
36 #include <stdio.h>
37 #include <stdlib.h>
38 #include <sys/types.h>
39 #include <pwd.h>
40 #include <libintl.h>
41 #include <netdb.h>
42 #include "utils.h"
43 #include "krb5_repository.h"
44 
45 #define	MISC_EXIT_STATUS 6
46 #define	DONT_DISP_POLICY	0
47 #define	DISP_POLICY		1
48 
49 extern int attempt_krb5_auth(void *, krb5_module_data_t *, char *, char **,
50 			boolean_t);
51 extern int krb5_verifypw(pam_handle_t *, char *, char *, boolean_t, int);
52 
53 static char *get_passwd(pam_handle_t *, char *);
54 static void display_msg(pam_handle_t *, int, char *);
55 static void display_msgs(pam_handle_t *, int, int,
56 		char msgs[][PAM_MAX_MSG_SIZE]);
57 static int krb5_changepw(pam_handle_t *, char *, char *, char *, int);
58 
59 /*
60  * set_ccname()
61  *
62  * set KRB5CCNAME shell var
63  */
64 static void
65 set_ccname(
66 	pam_handle_t *pamh,
67 	krb5_module_data_t *kmd,
68 	int login_result,
69 	int debug)
70 {
71 	int result;
72 
73 	if (debug)
74 		syslog(LOG_DEBUG,
75 		    "PAM-KRB5 (password): password: finalize"
76 		    " ccname env, login_result =%d, env ='%s'",
77 		    login_result, kmd->env ? kmd->env : "<null>");
78 
79 	if (kmd->env) {
80 
81 		if (login_result == PAM_SUCCESS) {
82 				/*
83 				 * Put ccname into the pamh so that login
84 				 * apps can pick this up when they run
85 				 * pam_getenvlist().
86 				 */
87 			if ((result = pam_putenv(pamh, kmd->env))
88 			    != PAM_SUCCESS) {
89 				/* should not happen but... */
90 				syslog(LOG_ERR,
91 				    dgettext(TEXT_DOMAIN,
92 					    "PAM-KRB5 (password):"
93 					    " pam_putenv failed: result: %d"),
94 				    result);
95 				goto cleanupccname;
96 			}
97 		} else {
98 		cleanupccname:
99 				/* for lack of a Solaris unputenv() */
100 			krb5_unsetenv(KRB5_ENV_CCNAME);
101 			free(kmd->env);
102 			kmd->env = NULL;
103 		}
104 	}
105 }
106 
107 /*
108  * get_set_creds()
109  *
110  * do a krb5 login to get and set krb5 creds (needed after a pw change
111  * on pw expire on login)
112  */
113 static void
114 get_set_creds(
115 	pam_handle_t *pamh,
116 	krb5_module_data_t *kmd,
117 	char *user,
118 	char *newpass,
119 	int debug)
120 {
121 	int login_result;
122 
123 	if (!kmd || kmd->age_status != PAM_NEW_AUTHTOK_REQD)
124 		return;
125 
126 	/*
127 	 * if pw has expired, get/set krb5 creds ala auth mod
128 	 *
129 	 * pwchange verified user sufficiently, so don't request strict
130 	 * tgt verification (will cause rcache perm issues possibly anyways)
131 	 */
132 	login_result = attempt_krb5_auth(pamh, kmd, user, &newpass, 0);
133 	if (debug)
134 		syslog(LOG_DEBUG,
135 		    "PAM-KRB5 (password): get_set_creds: login_result= %d",
136 		    login_result);
137 	/*
138 	 * the krb5 login should not fail, but if so,
139 	 * warn the user they have to kinit(1)
140 	 */
141 	if (login_result != PAM_SUCCESS) {
142 		display_msg(pamh, PAM_TEXT_INFO,
143 			    dgettext(TEXT_DOMAIN,
144 				    "Warning: "
145 				    "Could not cache Kerberos"
146 				    " credentials, please run "
147 				    "kinit(1) or re-login\n"));
148 	}
149 	set_ccname(pamh, kmd, login_result, debug);
150 }
151 /*
152  * This is the PAM Kerberos Password Change module
153  *
154  */
155 
156 int
157 pam_sm_chauthtok(
158 	pam_handle_t		*pamh,
159 	int			flags,
160 	int			argc,
161 	const char		**argv)
162 {
163 
164 	char			*user;
165 	int			err, result = PAM_AUTH_ERR;
166 	char			*newpass = NULL, *vnewpass = NULL;
167 	char			*oldpass = NULL;
168 	int			i;
169 	int			debug = 0;
170 	uid_t			pw_uid;
171 	krb5_module_data_t	*kmd = NULL;
172 	char			*pam_service;
173 	int			promptforold = 0;
174 	int			promptfornew = 0;
175 	pam_repository_t	*rep_data = NULL;
176 
177 	for (i = 0; i < argc; i++) {
178 		if (strcmp(argv[i], "debug") == 0)
179 			debug = 1;
180 		else
181 			syslog(LOG_ERR,
182 			    dgettext(TEXT_DOMAIN,
183 				    "PAM-KRB5 (password): illegal option %s"),
184 			    argv[i]);
185 	}
186 
187 	if (debug)
188 		syslog(LOG_DEBUG,
189 		    "PAM-KRB5 (password): start: flags = %x",
190 		    flags);
191 
192 	err = pam_get_item(pamh, PAM_REPOSITORY, (void **)&rep_data);
193 	if (rep_data != NULL) {
194 		if (strcmp(rep_data->type, KRB5_REPOSITORY_NAME) != 0) {
195 			if (debug)
196 				syslog(LOG_DEBUG, "PAM-KRB5 (auth): wrong"
197 					"repository found (%s), returning "
198 					"PAM_IGNORE", rep_data->type);
199 			return (PAM_IGNORE);
200 		}
201 	}
202 
203 	if (flags & PAM_PRELIM_CHECK) {
204 		/* Nothing to do here */
205 		if (debug)
206 			syslog(LOG_DEBUG,
207 			    "PAM-KRB5 (password): prelim check");
208 		return (PAM_IGNORE);
209 	}
210 
211 	/* make sure PAM framework is telling us to update passwords */
212 	if (!(flags & PAM_UPDATE_AUTHTOK)) {
213 		syslog(LOG_ERR, dgettext(TEXT_DOMAIN,
214 			"PAM-KRB5 (password): bad flags: %d"),
215 			flags);
216 		return (PAM_SYSTEM_ERR);
217 	}
218 
219 
220 	if ((err = pam_get_data(pamh, KRB5_DATA, (const void **)&kmd))
221 	    != PAM_SUCCESS) {
222 		if (debug)
223 			syslog(LOG_DEBUG,
224 			    "PAM-KRB5 (password): get mod data failed %d",
225 			    err);
226 		kmd = NULL;
227 	}
228 
229 	if (flags & PAM_CHANGE_EXPIRED_AUTHTOK) {
230 		/* let's make sure we know the krb5 pw has expired */
231 
232 		if (debug)
233 			syslog(LOG_DEBUG,
234 			    "PAM-KRB5 (password): kmd age status %d",
235 			    kmd ? kmd->age_status : -99);
236 
237 		if (!kmd || kmd->age_status != PAM_NEW_AUTHTOK_REQD)
238 			return (PAM_IGNORE);
239 	}
240 
241 	err = pam_get_item(pamh, PAM_SERVICE, (void **)&pam_service);
242 	if (err != PAM_SUCCESS) {
243 		syslog(LOG_ERR,
244 		    "PAM-KRB5 (password): error getting SERVICE");
245 		return (PAM_SYSTEM_ERR);
246 	}
247 
248 	err = pam_get_item(pamh, PAM_USER, (void **)&user);
249 	if (err != PAM_SUCCESS) {
250 		syslog(LOG_ERR,
251 		    "PAM-KRB5 (password): error getting USER");
252 		return (PAM_SYSTEM_ERR);
253 	}
254 
255 	if (user == NULL || user == '\0') {
256 		syslog(LOG_ERR,
257 		    "PAM-KRB5 (password): username is empty");
258 		return (PAM_SYSTEM_ERR);
259 	}
260 
261 	if (!get_pw_uid(user, &pw_uid)) {
262 		syslog(LOG_ERR,
263 		    "PAM-KRB5 (password): can't get uid for %s",
264 		    user);
265 		return (PAM_AUTHTOK_ERR);
266 	}
267 
268 	/*
269 	 * if root key exists in the keytab, it's a random key so no
270 	 * need to prompt for pw and we just return IGNORE
271 	 */
272 	if ((strcmp(user, ROOT_UNAME) == 0) &&
273 	    key_in_keytab(user, debug)) {
274 		if (debug)
275 			syslog(LOG_DEBUG,
276 			    "PAM-KRB5 (password): "
277 			    "key for '%s' in keytab, returning IGNORE", user);
278 		result = PAM_IGNORE;
279 		goto out;
280 	}
281 
282 	if ((err = pam_get_item(pamh, PAM_AUTHTOK,
283 				(void **) &newpass)) < 0)
284 		return (err);
285 
286 	if ((err = pam_get_item(pamh, PAM_OLDAUTHTOK,
287 				(void **) &oldpass)) < 0)
288 		return (err);
289 
290 	if (!newpass && !oldpass) {
291 		promptforold = 1;
292 		promptfornew = 1;
293 	} else {
294 		/*
295 		 * OLDAUTHTOK not set, we're probably the first password
296 		 * module but the AUTHTOK is probably set from an auth mod
297 		 */
298 		if (newpass && !oldpass) {
299 			oldpass = newpass;
300 			newpass = NULL;
301 			promptfornew = 1;
302 		}
303 
304 		result = krb5_verifypw(pamh, user, oldpass,
305 				    DONT_DISP_POLICY, debug);
306 		if (debug)
307 			syslog(LOG_DEBUG,
308 			    "PAM-KRB5 (password): verifypw first %d",
309 			    result);
310 		/*
311 		 * If this fails and is not bad passwd, then it might
312 		 * be a non-rpcsec_gss KDC so drop thru.
313 		 *
314 		 * (note in S9 change pw should work on non-rpcsec_gss KDCs
315 		 *  such as MIT & MS)
316 		 */
317 		if (result != 0)
318 			promptforold = 1;
319 	}
320 
321 	if (promptforold) {
322 
323 		oldpass = get_passwd(pamh,
324 				    dgettext(TEXT_DOMAIN,
325 					    "Old Kerberos password: "));
326 
327 		if (oldpass == NULL || oldpass[0] == '\0') {
328 			/* Need a password to proceed */
329 			display_msg(pamh, PAM_ERROR_MSG,
330 				    dgettext(TEXT_DOMAIN,
331 					    "Need the old password"
332 					    " to proceed \n"));
333 			free(oldpass);
334 			return (PAM_AUTHTOK_ERR);
335 		}
336 
337 		result = krb5_verifypw(pamh, user, oldpass,
338 				    DISP_POLICY, debug);
339 		if (debug)
340 			syslog(LOG_DEBUG,
341 			    "PAM-KRB5 (password): verifypw prforold %d",
342 			    result);
343 		/*
344 		 * If it's a bad password, we are done.
345 		 * Else, continue and try the pwch with oldpass.
346 		 */
347 		if (result == 2) {
348 			display_msg(pamh, PAM_ERROR_MSG,
349 				    dgettext(TEXT_DOMAIN,
350 					    "Old Kerberos"
351 					    " password incorrect\n"));
352 			(void) memset(oldpass, 0, strlen(oldpass));
353 			free(oldpass);
354 			return (PAM_AUTHTOK_ERR);
355 		}
356 	}
357 
358 	if (promptfornew) {
359 		newpass = get_passwd(pamh, dgettext(TEXT_DOMAIN,
360 			"New Kerberos password: "));
361 
362 		if (newpass == NULL || newpass[0] == '\0') {
363 			/* Need a password to proceed */
364 			display_msg(pamh, PAM_ERROR_MSG,
365 			    dgettext(TEXT_DOMAIN,
366 			    "Need a password to proceed \n"));
367 			result = PAM_AUTHTOK_ERR;
368 			goto out;
369 		}
370 
371 		vnewpass = get_passwd(pamh,
372 				dgettext(TEXT_DOMAIN,
373 			"Re-enter new Kerberos password: "));
374 
375 		if (vnewpass == NULL || vnewpass[0] == '\0') {
376 			/* Need a password to proceed */
377 			display_msg(pamh, PAM_ERROR_MSG,
378 			    dgettext(TEXT_DOMAIN,
379 				"Need a password to proceed \n"));
380 			result = PAM_AUTHTOK_ERR;
381 			goto out;
382 		}
383 
384 		if (strcmp(newpass, vnewpass)) {
385 			display_msg(pamh, PAM_ERROR_MSG,
386 			    dgettext(TEXT_DOMAIN,
387 				"Passwords do not match \n"));
388 			result = PAM_AUTHTOK_ERR;
389 			goto out;
390 		}
391 	}
392 
393 	result = krb5_changepw(pamh, user, oldpass, newpass, debug);
394 	if (result == PAM_SUCCESS) {
395 		display_msg(pamh, PAM_TEXT_INFO,
396 			    dgettext(TEXT_DOMAIN,
397 				    "Kerberos password "
398 				    "successfully changed\n"));
399 
400 		get_set_creds(pamh, kmd, user, newpass, debug);
401 
402 		(void) pam_set_item(pamh, PAM_AUTHTOK, newpass);
403 		(void) pam_set_item(pamh, PAM_OLDAUTHTOK, oldpass);
404 	}
405 
406 out:
407 	if (promptforold && oldpass) {
408 		(void) memset(oldpass, 0, strlen(oldpass));
409 		free(oldpass);
410 	}
411 	if (newpass) {
412 		(void) memset(newpass, 0, strlen(newpass));
413 		free(newpass);
414 	}
415 
416 	if (vnewpass) {
417 		(void) memset(vnewpass, 0, strlen(vnewpass));
418 		free(vnewpass);
419 	}
420 
421 	if (debug)
422 		syslog(LOG_DEBUG,
423 		    "PAM-KRB5 (password): out: returns %d",
424 		    result);
425 
426 	return (result);
427 }
428 
429 
430 int
431 pam_sm_get_authtokattr(
432 	/*ARGSUSED*/
433 	pam_handle_t		*pamh,
434 	char			***ga_getattr,
435 	int			repository,
436 	const char		*nisdomain,
437 	int			argc,
438 	const char		**argv)
439 {
440 	return (PAM_SUCCESS);
441 }
442 
443 int
444 pam_sm_set_authtokattr(
445 	/*ARGSUSED*/
446 	pam_handle_t		*pamh,
447 	const char 		**pam_setattr,
448 	int			repository,
449 	const char		*nisdomain,
450 	int			argc,
451 	const char		**argv)
452 {
453 	return (PAM_SUCCESS);
454 }
455 
456 int
457 krb5_verifypw(
458 	pam_handle_t *pamh,
459 	char 	*princ_str,
460 	char	*old_password,
461 	boolean_t disp_flag,
462 	int debug)
463 {
464 	kadm5_ret_t		code;
465 	krb5_principal 		princ = 0;
466 	char 			admin_realm[1024];
467 	char			kprinc[2*MAXHOSTNAMELEN];
468 	char			*cpw_service;
469 	kadm5_principal_ent_rec principal_entry;
470 	kadm5_policy_ent_rec	 policy_entry;
471 	void 			*server_handle;
472 	krb5_context		context;
473 	kadm5_config_params	params;
474 #define	MSG_ROWS		5
475 	char			msgs[MSG_ROWS][PAM_MAX_MSG_SIZE];
476 
477 	(void) memset((char *)&params, 0, sizeof (params));
478 	(void) memset(&principal_entry, 0, sizeof (principal_entry));
479 	(void) memset(&policy_entry, 0, sizeof (policy_entry));
480 
481 	if (code = krb5_init_context(&context)) {
482 		return (6);
483 	}
484 
485 	if ((code = get_kmd_kuser(context, (const char *)princ_str, kprinc,
486 		2*MAXHOSTNAMELEN)) != 0) {
487 		return (code);
488 	}
489 
490 	/* Need to get a krb5_principal struct */
491 
492 	code = krb5_parse_name(context, kprinc, &princ);
493 
494 	if (code != 0) {
495 		return (MISC_EXIT_STATUS);
496 	}
497 
498 	if (strlen(old_password) == 0) {
499 		krb5_free_principal(context, princ);
500 		return (5);
501 	}
502 
503 	(void) strlcpy(admin_realm,
504 		    krb5_princ_realm(context, princ)->data,
505 		    sizeof (admin_realm));
506 
507 	params.mask |= KADM5_CONFIG_REALM;
508 	params.realm = admin_realm;
509 
510 
511 	if (kadm5_get_cpw_host_srv_name(context, admin_realm, &cpw_service)) {
512 		syslog(LOG_ERR,
513 		    dgettext(TEXT_DOMAIN,
514 			"PAM-KRB5 (password): unable to get host based "
515 			"service name for realm %s\n"),
516 			admin_realm);
517 		return (3);
518 	}
519 
520 	code = kadm5_init_with_password(kprinc, old_password, cpw_service,
521 					&params, KADM5_STRUCT_VERSION,
522 					KADM5_API_VERSION_2, &server_handle);
523 	if (code != 0) {
524 		if (debug)
525 			syslog(LOG_DEBUG,
526 			    "PAM-KRB5: krb5_verifypw: init_with_pw"
527 			    " failed: (%s)", error_message(code));
528 		krb5_free_principal(context, princ);
529 		return ((code == KADM5_BAD_PASSWORD) ? 2 : 3);
530 	}
531 
532 	if (disp_flag &&
533 	    _kadm5_get_kpasswd_protocol(server_handle) == KRB5_CHGPWD_RPCSEC) {
534 		/*
535 		 * Note: copy of this exists in login
536 		 * (kverify.c/get_verified_in_tkt).
537 		 */
538 
539 		code = kadm5_get_principal(server_handle, princ,
540 						&principal_entry,
541 						KADM5_PRINCIPAL_NORMAL_MASK);
542 		if (code != 0) {
543 			krb5_free_principal(context, princ);
544 			(void) kadm5_destroy(server_handle);
545 			return ((code == KADM5_UNK_PRINC) ? 1 :
546 				MISC_EXIT_STATUS);
547 		}
548 
549 		if ((principal_entry.aux_attributes & KADM5_POLICY) != 0) {
550 			code = kadm5_get_policy(server_handle,
551 						principal_entry.policy,
552 						&policy_entry);
553 			if (code != 0) {
554 				/*
555 				 * doesn't matter which error comes back,
556 				 * there's no nice recovery or need to
557 				 * differentiate to the user
558 				 */
559 				(void) kadm5_free_principal_ent(server_handle,
560 							&principal_entry);
561 				krb5_free_principal(context, princ);
562 				(void) kadm5_destroy(server_handle);
563 				return (MISC_EXIT_STATUS);
564 			}
565 
566 			(void) snprintf(msgs[0], PAM_MAX_MSG_SIZE,
567 				dgettext(TEXT_DOMAIN, "POLICY_EXPLANATION:"));
568 			(void) snprintf(msgs[1], PAM_MAX_MSG_SIZE,
569 				dgettext(TEXT_DOMAIN,
570 					"Principal string is %s"), princ_str);
571 			(void) snprintf(msgs[2], PAM_MAX_MSG_SIZE,
572 				dgettext(TEXT_DOMAIN, "Policy Name is  %s"),
573 				principal_entry.policy);
574 			(void) snprintf(msgs[3], PAM_MAX_MSG_SIZE,
575 				dgettext(TEXT_DOMAIN,
576 					"Minimum password length is %d"),
577 					policy_entry.pw_min_length);
578 			(void) snprintf(msgs[4], PAM_MAX_MSG_SIZE,
579 				dgettext(TEXT_DOMAIN,
580 					"Minimum password classes is %d"),
581 					policy_entry.pw_min_classes);
582 			display_msgs(pamh, PAM_TEXT_INFO, MSG_ROWS, msgs);
583 
584 			if (code = kadm5_free_principal_ent(server_handle,
585 							    &principal_entry)) {
586 				(void) kadm5_free_policy_ent(server_handle,
587 							&policy_entry);
588 				krb5_free_principal(context, princ);
589 				(void) kadm5_destroy(server_handle);
590 				return (MISC_EXIT_STATUS);
591 			}
592 			if (code = kadm5_free_policy_ent(server_handle,
593 							&policy_entry)) {
594 				krb5_free_principal(context, princ);
595 
596 				(void) kadm5_destroy(server_handle);
597 				return (MISC_EXIT_STATUS);
598 			}
599 		} else {
600 			/*
601 			 * kpasswd *COULD* output something here to encourage
602 			 * the choice of good passwords, in the absence of
603 			 * an enforced policy.
604 			 */
605 			if (code = kadm5_free_principal_ent(server_handle,
606 							    &principal_entry)) {
607 				krb5_free_principal(context, princ);
608 				(void) kadm5_destroy(server_handle);
609 				return (MISC_EXIT_STATUS);
610 			}
611 		}
612 	}
613 	krb5_free_principal(context, princ);
614 
615 	(void) kadm5_destroy(server_handle);
616 
617 	return (0);
618 }
619 
620 /*
621  * Function: krb5_changepw
622  *
623  * Purpose: Initialize and call lower level routines to change a password
624  *
625  * Arguments:
626  *
627  *	princ_str	principal name to use, optional
628  *	old_password 	old password
629  *	new_password  	new password
630  *
631  * Returns:
632  *                      exit status of PAM_SUCCESS for success
633  *			1 principal unknown
634  *			2 old password wrong
635  *			3 cannot initialize admin server session
636  *			4 new passwd mismatch or error trying to change pw
637  *                      5 password not typed
638  *                      6 misc error
639  *                      7 incorrect usage
640  *
641  * Requires:
642  *	Passwords cannot be more than 255 characters long.
643  *
644  * Modifies:
645  *
646  * Changes the principal's password.
647  *
648  */
649 static int
650 krb5_changepw(
651 	pam_handle_t *pamh,
652 	char *princ_str,
653 	char *old_password,
654 	char *new_password,
655 	int debug)
656 {
657 	kadm5_ret_t		code;
658 	krb5_principal 		princ = 0;
659 	char 			msg_ret[1024], admin_realm[1024];
660 	char			kprinc[2*MAXHOSTNAMELEN];
661 	char			*cpw_service;
662 	kadm5_principal_ent_rec principal_entry;
663 	kadm5_policy_ent_rec	policy_entry;
664 	void 			*server_handle;
665 	krb5_context		context;
666 	kadm5_config_params	params;
667 
668 	(void) memset((char *)&params, 0, sizeof (params));
669 	(void) memset(&principal_entry, 0, sizeof (principal_entry));
670 	(void) memset(&policy_entry, 0, sizeof (policy_entry));
671 
672 	if (code = krb5_init_context(&context)) {
673 		return (6);
674 	}
675 
676 	if ((code = get_kmd_kuser(context, (const char *)princ_str, kprinc,
677 		2*MAXHOSTNAMELEN)) != 0) {
678 		return (code);
679 	}
680 
681 	/* Need to get a krb5_principal struct */
682 
683 	code = krb5_parse_name(context, kprinc, &princ);
684 
685 	if (code != 0) {
686 		return (MISC_EXIT_STATUS);
687 	}
688 
689 	if (strlen(old_password) == 0) {
690 		krb5_free_principal(context, princ);
691 		return (5);
692 	}
693 
694 	(void) snprintf(admin_realm, sizeof (admin_realm), "%s",
695 		krb5_princ_realm(context, princ)->data);
696 	params.mask |= KADM5_CONFIG_REALM;
697 	params.realm = admin_realm;
698 
699 
700 	if (kadm5_get_cpw_host_srv_name(context, admin_realm, &cpw_service)) {
701 		syslog(LOG_ERR,
702 			dgettext(TEXT_DOMAIN,
703 				"PAM-KRB5 (password):unable to get host based "
704 				"service name for realm %s\n"),
705 			admin_realm);
706 		return (3);
707 	}
708 
709 	code = kadm5_init_with_password(kprinc, old_password, cpw_service,
710 					&params, KADM5_STRUCT_VERSION,
711 					KADM5_API_VERSION_2, &server_handle);
712 	free(cpw_service);
713 	if (code != 0) {
714 		if (debug)
715 			syslog(LOG_DEBUG,
716 			    "PAM-KRB5 (password): changepw: "
717 			    "init_with_pw failed:  (%s)", error_message(code));
718 		krb5_free_principal(context, princ);
719 		return ((code == KADM5_BAD_PASSWORD) ? 2 : 3);
720 	}
721 
722 	code = kadm5_chpass_principal_util(server_handle, princ,
723 					new_password,
724 					NULL /* don't need pw back */,
725 					msg_ret,
726 					sizeof (msg_ret));
727 
728 	if (code) {
729 		char msgs[2][PAM_MAX_MSG_SIZE];
730 
731 		(void) snprintf(msgs[0], PAM_MAX_MSG_SIZE, "%s",
732 			dgettext(TEXT_DOMAIN,
733 				"Kerberos password not changed: "));
734 		(void) snprintf(msgs[1], PAM_MAX_MSG_SIZE, "%s", msg_ret);
735 
736 		display_msgs(pamh, PAM_ERROR_MSG, 2, msgs);
737 	}
738 
739 	krb5_free_principal(context, princ);
740 
741 	(void) kadm5_destroy(server_handle);
742 
743 	if (debug)
744 		syslog(LOG_DEBUG,
745 		    "PAM-KRB5 (password): changepw: end %d", code);
746 
747 	if (code == KRB5_LIBOS_CANTREADPWD)
748 		return (5);
749 	else if (code)
750 		return (4);
751 	else
752 		return (PAM_SUCCESS);
753 }
754 
755 static char *
756 get_passwd(
757 	pam_handle_t *pamh,
758 	char *prompt)
759 {
760 	int		err;
761 	char		*p;
762 
763 	err = __pam_get_authtok(pamh, PAM_PROMPT, 0, prompt, &p);
764 
765 	if (err != PAM_SUCCESS) {
766 		return (NULL);
767 	}
768 
769 	return (p);
770 }
771 
772 
773 static void
774 display_msgs(pam_handle_t *pamh,
775 	int msg_style, int nmsg, char msgs[][PAM_MAX_MSG_SIZE])
776 {
777 	(void) __pam_display_msg(pamh, msg_style, nmsg, msgs, NULL);
778 }
779 
780 
781 static void
782 display_msg(pam_handle_t *pamh, int msg_style, char *msg)
783 {
784 	char pam_msg[1][PAM_MAX_MSG_SIZE];
785 
786 	(void) snprintf(pam_msg[0], PAM_MAX_MSG_SIZE, "%s", msg);
787 	display_msgs(pamh, msg_style, 1, pam_msg);
788 }
789