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.
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.
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]
22 * Copyright 2010 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
26 #include <kadm5/admin.h>
29 #include <security/pam_appl.h>
30 #include <security/pam_modules.h>
31 #include <security/pam_impl.h>
36 #include <sys/types.h>
41 #include "krb5_repository.h"
43 extern int attempt_krb5_auth(pam_handle_t
*, krb5_module_data_t
*, char *,
45 extern int krb5_verifypw(char *, char *, int);
47 static void display_msg(pam_handle_t
*, int, char *);
48 static void display_msgs(pam_handle_t
*, int, int,
49 char msgs
[][PAM_MAX_MSG_SIZE
]);
50 static int krb5_changepw(pam_handle_t
*, char *, char *, char *, int);
55 * set KRB5CCNAME shell var
60 krb5_module_data_t
*kmd
,
67 __pam_log(LOG_AUTH
| LOG_DEBUG
,
68 "PAM-KRB5 (password): password: finalize"
69 " ccname env, login_result =%d, env ='%s'",
70 login_result
, kmd
->env
? kmd
->env
: "<null>");
74 if (login_result
== PAM_SUCCESS
) {
76 * Put ccname into the pamh so that login
77 * apps can pick this up when they run
80 if ((result
= pam_putenv(pamh
, kmd
->env
))
82 /* should not happen but... */
83 __pam_log(LOG_AUTH
| LOG_ERR
,
84 "PAM-KRB5 (password):"
85 " pam_putenv failed: result: %d",
91 /* for lack of a Solaris unputenv() */
92 krb5_unsetenv(KRB5_ENV_CCNAME
);
102 * do a krb5 login to get and set krb5 creds (needed after a pw change
103 * on pw expire on login)
108 krb5_module_data_t
*kmd
,
115 if (!kmd
|| kmd
->age_status
!= PAM_NEW_AUTHTOK_REQD
)
119 * if pw has expired, get/set krb5 creds ala auth mod
121 * pwchange verified user sufficiently, so don't request strict
122 * tgt verification (will cause rcache perm issues possibly anyways)
124 login_result
= attempt_krb5_auth(pamh
, kmd
, user
, &newpass
, 0);
126 __pam_log(LOG_AUTH
| LOG_DEBUG
,
127 "PAM-KRB5 (password): get_set_creds: login_result= %d",
130 * the krb5 login should not fail, but if so,
131 * warn the user they have to kinit(1)
133 if (login_result
!= PAM_SUCCESS
) {
134 display_msg(pamh
, PAM_TEXT_INFO
,
135 dgettext(TEXT_DOMAIN
,
137 "Could not cache Kerberos"
138 " credentials, please run "
139 "kinit(1) or re-login\n"));
141 set_ccname(pamh
, kmd
, login_result
, debug
);
144 * This is the PAM Kerberos Password Change module
157 int err
, result
= PAM_AUTHTOK_ERR
;
158 char *newpass
= NULL
;
159 char *oldpass
= NULL
;
163 krb5_module_data_t
*kmd
= NULL
;
164 pam_repository_t
*rep_data
= NULL
;
166 for (i
= 0; i
< argc
; i
++) {
167 if (strcmp(argv
[i
], "debug") == 0)
170 __pam_log(LOG_AUTH
| LOG_ERR
,
171 "PAM-KRB5 (password): illegal option %s",
176 __pam_log(LOG_AUTH
| LOG_DEBUG
,
177 "PAM-KRB5 (password): start: flags = %x",
180 (void) pam_get_item(pamh
, PAM_REPOSITORY
, (void **)&rep_data
);
182 if (rep_data
!= NULL
) {
183 if (strcmp(rep_data
->type
, KRB5_REPOSITORY_NAME
) != 0) {
185 __pam_log(LOG_AUTH
| LOG_DEBUG
,
186 "PAM-KRB5 (auth): wrong"
187 "repository found (%s), returning "
188 "PAM_IGNORE", rep_data
->type
);
193 if (flags
& PAM_PRELIM_CHECK
) {
194 /* Nothing to do here */
196 __pam_log(LOG_AUTH
| LOG_DEBUG
,
197 "PAM-KRB5 (password): prelim check");
201 /* make sure PAM framework is telling us to update passwords */
202 if (!(flags
& PAM_UPDATE_AUTHTOK
)) {
203 __pam_log(LOG_AUTH
| LOG_ERR
,
204 "PAM-KRB5 (password): bad flags: %d",
206 return (PAM_SYSTEM_ERR
);
210 if ((err
= pam_get_data(pamh
, KRB5_DATA
, (const void **)&kmd
))
213 __pam_log(LOG_AUTH
| LOG_DEBUG
,
214 "PAM-KRB5 (password): get mod data failed %d",
219 if (flags
& PAM_CHANGE_EXPIRED_AUTHTOK
) {
220 /* let's make sure we know the krb5 pw has expired */
223 __pam_log(LOG_AUTH
| LOG_DEBUG
,
224 "PAM-KRB5 (password): kmd age status %d",
225 kmd
? kmd
->age_status
: -99);
227 if (!kmd
|| kmd
->age_status
!= PAM_NEW_AUTHTOK_REQD
)
231 (void) pam_get_item(pamh
, PAM_USER
, (void **)&user
);
233 if (user
== NULL
|| *user
== '\0') {
234 __pam_log(LOG_AUTH
| LOG_ERR
,
235 "PAM-KRB5 (password): username is empty");
236 return (PAM_USER_UNKNOWN
);
239 if (!get_pw_uid(user
, &pw_uid
)) {
240 __pam_log(LOG_AUTH
| LOG_ERR
,
241 "PAM-KRB5 (password): can't get uid for %s", user
);
242 return (PAM_USER_UNKNOWN
);
246 * if root key exists in the keytab, it's a random key so no
247 * need to prompt for pw and we just return IGNORE
249 if ((strcmp(user
, ROOT_UNAME
) == 0) &&
250 key_in_keytab(user
, debug
)) {
252 __pam_log(LOG_AUTH
| LOG_DEBUG
,
253 "PAM-KRB5 (password): "
254 "key for '%s' in keytab, returning IGNORE", user
);
259 (void) pam_get_item(pamh
, PAM_AUTHTOK
, (void **)&newpass
);
262 * If the preauth type done didn't use a passwd just ignore the error.
265 if (kmd
&& kmd
->preauth_type
== KRB_PKINIT
)
268 return (PAM_SYSTEM_ERR
);
270 (void) pam_get_item(pamh
, PAM_OLDAUTHTOK
, (void **)&oldpass
);
273 if (kmd
&& kmd
->preauth_type
== KRB_PKINIT
)
276 return (PAM_SYSTEM_ERR
);
278 result
= krb5_verifypw(user
, oldpass
, debug
);
280 __pam_log(LOG_AUTH
| LOG_DEBUG
,
281 "PAM-KRB5 (password): verifypw %d", result
);
284 * If it's a bad password or general failure, we are done.
288 * if the preauth type done didn't use a passwd just ignore the
291 if (kmd
&& kmd
->preauth_type
== KRB_PKINIT
)
295 display_msg(pamh
, PAM_ERROR_MSG
, dgettext(TEXT_DOMAIN
,
296 "Old Kerberos password incorrect\n"));
297 return (PAM_AUTHTOK_ERR
);
301 * If the old password verifies try to change it regardless of the
302 * preauth type and do not ignore the error.
304 result
= krb5_changepw(pamh
, user
, oldpass
, newpass
, debug
);
305 if (result
== PAM_SUCCESS
) {
306 display_msg(pamh
, PAM_TEXT_INFO
, dgettext(TEXT_DOMAIN
,
307 "Kerberos password successfully changed\n"));
309 get_set_creds(pamh
, kmd
, user
, newpass
, debug
);
314 __pam_log(LOG_AUTH
| LOG_DEBUG
,
315 "PAM-KRB5 (password): out: returns %d",
328 krb5_principal princ
= 0;
329 char admin_realm
[1024];
330 char kprinc
[2*MAXHOSTNAMELEN
];
333 krb5_context context
;
334 kadm5_config_params params
;
336 (void) memset((char *)¶ms
, 0, sizeof (params
));
338 if (code
= krb5_init_secure_context(&context
)) {
342 if ((code
= get_kmd_kuser(context
, (const char *)princ_str
, kprinc
,
343 2*MAXHOSTNAMELEN
)) != 0) {
347 /* Need to get a krb5_principal struct */
349 code
= krb5_parse_name(context
, kprinc
, &princ
);
354 if (strlen(old_password
) == 0) {
355 krb5_free_principal(context
, princ
);
359 (void) strlcpy(admin_realm
,
360 krb5_princ_realm(context
, princ
)->data
,
361 sizeof (admin_realm
));
363 params
.mask
|= KADM5_CONFIG_REALM
;
364 params
.realm
= admin_realm
;
367 if (kadm5_get_cpw_host_srv_name(context
, admin_realm
, &cpw_service
)) {
368 __pam_log(LOG_AUTH
| LOG_ERR
,
369 "PAM-KRB5 (password): unable to get host based "
370 "service name for realm %s\n",
372 krb5_free_principal(context
, princ
);
376 code
= kadm5_init_with_password(kprinc
, old_password
, cpw_service
,
377 ¶ms
, KADM5_STRUCT_VERSION
,
378 KADM5_API_VERSION_2
, NULL
,
382 __pam_log(LOG_AUTH
| LOG_DEBUG
,
383 "PAM-KRB5: krb5_verifypw: init_with_pw"
384 " failed: (%s)", error_message(code
));
385 krb5_free_principal(context
, princ
);
386 return ((code
== KADM5_BAD_PASSWORD
) ? 2 : 3);
389 krb5_free_principal(context
, princ
);
391 (void) kadm5_destroy(server_handle
);
397 * Function: krb5_changepw
399 * Purpose: Initialize and call lower level routines to change a password
403 * princ_str principal name to use, optional
404 * old_password old password
405 * new_password new password
408 * exit status of PAM_SUCCESS for success
409 * else returns PAM failure
412 * Passwords cannot be more than 255 characters long.
416 * Changes the principal's password.
428 krb5_principal princ
= 0;
429 char msg_ret
[1024], admin_realm
[1024];
430 char kprinc
[2*MAXHOSTNAMELEN
];
433 krb5_context context
;
434 kadm5_config_params params
;
436 (void) memset((char *)¶ms
, 0, sizeof (params
));
438 if (krb5_init_secure_context(&context
) != 0)
439 return (PAM_SYSTEM_ERR
);
441 if ((code
= get_kmd_kuser(context
, (const char *)princ_str
, kprinc
,
442 2*MAXHOSTNAMELEN
)) != 0) {
446 /* Need to get a krb5_principal struct */
448 code
= krb5_parse_name(context
, kprinc
, &princ
);
450 return (PAM_SYSTEM_ERR
);
452 if (strlen(old_password
) == 0) {
453 krb5_free_principal(context
, princ
);
454 return (PAM_AUTHTOK_ERR
);
457 (void) snprintf(admin_realm
, sizeof (admin_realm
), "%s",
458 krb5_princ_realm(context
, princ
)->data
);
459 params
.mask
|= KADM5_CONFIG_REALM
;
460 params
.realm
= admin_realm
;
463 if (kadm5_get_cpw_host_srv_name(context
, admin_realm
, &cpw_service
)) {
464 __pam_log(LOG_AUTH
| LOG_ERR
,
465 "PAM-KRB5 (password):unable to get host based "
466 "service name for realm %s\n",
468 return (PAM_SYSTEM_ERR
);
471 code
= kadm5_init_with_password(kprinc
, old_password
, cpw_service
,
472 ¶ms
, KADM5_STRUCT_VERSION
,
473 KADM5_API_VERSION_2
, NULL
,
478 __pam_log(LOG_AUTH
| LOG_DEBUG
,
479 "PAM-KRB5 (password): changepw: "
480 "init_with_pw failed: (%s)", error_message(code
));
481 krb5_free_principal(context
, princ
);
482 return ((code
== KADM5_BAD_PASSWORD
) ?
483 PAM_AUTHTOK_ERR
: PAM_SYSTEM_ERR
);
486 code
= kadm5_chpass_principal_util(server_handle
, princ
,
488 NULL
/* don't need pw back */,
493 char msgs
[2][PAM_MAX_MSG_SIZE
];
495 (void) snprintf(msgs
[0], PAM_MAX_MSG_SIZE
, "%s",
496 dgettext(TEXT_DOMAIN
,
497 "Kerberos password not changed: "));
498 (void) snprintf(msgs
[1], PAM_MAX_MSG_SIZE
, "%s", msg_ret
);
500 display_msgs(pamh
, PAM_ERROR_MSG
, 2, msgs
);
503 krb5_free_principal(context
, princ
);
505 (void) kadm5_destroy(server_handle
);
508 __pam_log(LOG_AUTH
| LOG_DEBUG
,
509 "PAM-KRB5 (password): changepw: end %d", code
);
512 return (PAM_AUTHTOK_ERR
);
514 return (PAM_SUCCESS
);
518 display_msgs(pam_handle_t
*pamh
,
519 int msg_style
, int nmsg
, char msgs
[][PAM_MAX_MSG_SIZE
])
521 (void) __pam_display_msg(pamh
, msg_style
, nmsg
, msgs
, NULL
);
526 display_msg(pam_handle_t
*pamh
, int msg_style
, char *msg
)
528 char pam_msg
[1][PAM_MAX_MSG_SIZE
];
530 (void) snprintf(pam_msg
[0], PAM_MAX_MSG_SIZE
, "%s", msg
);
531 display_msgs(pamh
, msg_style
, 1, pam_msg
);