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]
23 * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved.
26 #include <security/pam_appl.h>
27 #include <security/pam_modules.h>
28 #include <security/pam_impl.h>
32 #include <sys/types.h>
38 #include "profile/prof_int.h"
42 #include "krb5_repository.h"
44 #define KRB5_DEFAULT_OPTIONS 0
46 int forwardable_flag
= 0;
47 int renewable_flag
= 0;
48 int proxiable_flag
= 0;
49 int no_address_flag
= 0;
50 profile_options_boolean config_option
[] = {
51 { "forwardable", &forwardable_flag
, 0 },
52 { "renewable", &renewable_flag
, 0 },
53 { "proxiable", &proxiable_flag
, 0 },
54 { "no_addresses", &no_address_flag
, 0 },
59 profile_option_strings config_times
[] = {
60 { "max_life", &life_timeval
, 0 },
61 { "max_renewable_life", &renew_timeval
, 0 },
64 char *realmdef
[] = { "realms", NULL
, NULL
, NULL
};
65 char *appdef
[] = { "appdefaults", "kinit", NULL
};
67 #define krb_realm (*(realmdef + 1))
69 int attempt_krb5_auth(pam_handle_t
*, krb5_module_data_t
*, char *,
71 void krb5_cleanup(pam_handle_t
*, void *, int);
73 extern errcode_t
profile_get_options_boolean();
74 extern errcode_t
profile_get_options_string();
75 extern int krb5_verifypw(char *, char *, int);
76 extern krb5_error_code
krb5_verify_init_creds(krb5_context
,
77 krb5_creds
*, krb5_principal
, krb5_keytab
, krb5_ccache
*,
78 krb5_verify_init_creds_opt
*);
79 extern krb5_error_code
__krb5_get_init_creds_password(krb5_context
,
80 krb5_creds
*, krb5_principal
, char *, krb5_prompter_fct
, void *,
81 krb5_deltat
, char *, krb5_get_init_creds_opt
*,
85 * pam_sm_authenticate - Authenticate user
96 int result
= PAM_AUTH_ERR
;
97 /* pam.conf options */
100 /* return an error on password expire */
103 char *password
= NULL
;
105 krb5_module_data_t
*kmd
= NULL
;
106 krb5_repository_data_t
*krb5_data
= NULL
;
107 pam_repository_t
*rep_data
= NULL
;
108 boolean_t do_pkinit
= FALSE
;
110 for (i
= 0; i
< argc
; i
++) {
111 if (strcmp(argv
[i
], "debug") == 0) {
113 } else if (strcmp(argv
[i
], "nowarn") == 0) {
115 } else if (strcmp(argv
[i
], "err_on_exp") == 0) {
117 } else if (strcmp(argv
[i
], "pkinit") == 0) {
120 __pam_log(LOG_AUTH
| LOG_ERR
,
121 "PAM-KRB5 (auth) unrecognized option %s", argv
[i
]);
124 if (flags
& PAM_SILENT
) warn
= 0;
127 __pam_log(LOG_AUTH
| LOG_DEBUG
,
128 "PAM-KRB5 (auth): pam_sm_authenticate flags=%d",
132 * pam_get_data could fail if we are being called for the first time
133 * or if the module is not found, PAM_NO_MODULE_DATA is not an error
135 err
= pam_get_data(pamh
, KRB5_DATA
, (const void**)&kmd
);
136 if (!(err
== PAM_SUCCESS
|| err
== PAM_NO_MODULE_DATA
))
137 return (PAM_SYSTEM_ERR
);
140 * If pam_krb5 was stacked higher in the auth stack and did PKINIT
141 * preauth sucessfully then this instance is a fallback to password
142 * based preauth and should just return PAM_IGNORE.
144 * The else clause is handled further down.
147 if (++(kmd
->auth_calls
) > 2) {
149 * pam_krb5 has been stacked > 2 times in the auth
150 * stack. Clear out the current kmd and proceed as if
151 * this is the first time pam_krb5 auth has been called.
154 __pam_log(LOG_AUTH
| LOG_DEBUG
,
155 "PAM-KRB5 (auth): stacked more than"
156 " two times, clearing kmd");
158 /* clear out/free current kmd */
159 err
= pam_set_data(pamh
, KRB5_DATA
, NULL
, NULL
);
160 if (err
!= PAM_SUCCESS
) {
161 krb5_cleanup(pamh
, kmd
, err
);
166 } else if (kmd
->auth_calls
== 2 &&
167 kmd
->auth_status
== PAM_SUCCESS
) {
169 * The previous instance of pam_krb5 succeeded and this
170 * instance was a fall back in case it didn't succeed so
174 __pam_log(LOG_AUTH
| LOG_DEBUG
,
175 "PAM-KRB5 (auth): PKINIT succeeded "
176 "earlier so returning PAM_IGNORE");
182 (void) pam_get_item(pamh
, PAM_USER
, (void**) &user
);
184 if (user
== NULL
|| *user
== '\0') {
187 * If doing PKINIT it is okay to prompt for the user
190 if ((err
= pam_get_user(pamh
, &user
, NULL
)) !=
193 __pam_log(LOG_AUTH
| LOG_DEBUG
,
194 "PAM-KRB5 (auth): get user failed: "
195 "%s", pam_strerror(pamh
, err
));
201 __pam_log(LOG_AUTH
| LOG_DEBUG
,
202 "PAM-KRB5 (auth): user empty or null");
203 return (PAM_USER_UNKNOWN
);
207 /* make sure a password entry exists for this user */
208 if (!get_pw_uid(user
, &pw_uid
))
209 return (PAM_USER_UNKNOWN
);
212 kmd
= calloc(1, sizeof (krb5_module_data_t
));
214 result
= PAM_BUF_ERR
;
218 err
= pam_set_data(pamh
, KRB5_DATA
, kmd
, &krb5_cleanup
);
219 if (err
!= PAM_SUCCESS
) {
229 if (snprintf(buffer
, sizeof (buffer
),
230 "%s=FILE:/tmp/krb5cc_%d",
231 KRB5_ENV_CCNAME
, (int)pw_uid
) >= sizeof (buffer
)) {
232 result
= PAM_SYSTEM_ERR
;
236 /* we MUST copy this to the heap for the putenv to work! */
237 kmd
->env
= strdup(buffer
);
239 result
= PAM_BUF_ERR
;
242 if (putenv(kmd
->env
)) {
243 result
= PAM_SYSTEM_ERR
;
250 if ((kmd
->user
= strdup(user
)) == NULL
) {
251 result
= PAM_BUF_ERR
;
255 kmd
->auth_status
= PAM_AUTH_ERR
;
258 kmd
->err_on_exp
= err_on_exp
;
260 kmd
->kcontext
= NULL
;
261 kmd
->password
= NULL
;
262 kmd
->age_status
= PAM_SUCCESS
;
263 (void) memset((char *)&kmd
->initcreds
, 0, sizeof (krb5_creds
));
265 kmd
->preauth_type
= do_pkinit
? KRB_PKINIT
: KRB_PASSWD
;
268 * For apps that already did krb5 auth exchange...
269 * Now that we've created the kmd structure, we can
270 * return SUCCESS. 'kmd' may be needed later by other
271 * PAM functions, thats why we wait until this point to
274 (void) pam_get_item(pamh
, PAM_REPOSITORY
, (void **)&rep_data
);
276 if (rep_data
!= NULL
) {
277 if (strcmp(rep_data
->type
, KRB5_REPOSITORY_NAME
) != 0) {
279 __pam_log(LOG_AUTH
| LOG_DEBUG
,
280 "PAM-KRB5 (auth): wrong"
281 "repository found (%s), returning "
282 "PAM_IGNORE", rep_data
->type
);
285 if (rep_data
->scope_len
== sizeof (krb5_repository_data_t
)) {
286 krb5_data
= (krb5_repository_data_t
*)rep_data
->scope
;
288 if (krb5_data
->flags
==
289 SUNW_PAM_KRB5_ALREADY_AUTHENTICATED
&&
290 krb5_data
->principal
!= NULL
&&
291 strlen(krb5_data
->principal
)) {
293 __pam_log(LOG_AUTH
| LOG_DEBUG
,
294 "PAM-KRB5 (auth): Principal "
295 "%s already authenticated",
296 krb5_data
->principal
);
297 kmd
->auth_status
= PAM_SUCCESS
;
298 return (PAM_SUCCESS
);
304 * if root key exists in the keytab, it's a random key so no
305 * need to prompt for pw and we just return IGNORE.
307 * note we don't need to force a prompt for pw as authtok_get
308 * is required to be stacked above this module.
310 if ((strcmp(user
, ROOT_UNAME
) == 0) &&
311 key_in_keytab(user
, debug
)) {
313 __pam_log(LOG_AUTH
| LOG_DEBUG
,
315 "key for '%s' in keytab, returning IGNORE", user
);
320 (void) pam_get_item(pamh
, PAM_AUTHTOK
, (void **)&password
);
322 result
= attempt_krb5_auth(pamh
, kmd
, user
, &password
, 1);
327 __pam_log(LOG_AUTH
| LOG_DEBUG
,
328 "PAM-KRB5 (auth): pam_sm_auth finalize"
329 " ccname env, result =%d, env ='%s',"
330 " age = %d, status = %d",
331 result
, kmd
->env
? kmd
->env
: "<null>",
332 kmd
->age_status
, kmd
->auth_status
);
335 !(kmd
->age_status
== PAM_NEW_AUTHTOK_REQD
&&
336 kmd
->auth_status
== PAM_SUCCESS
)) {
339 if (result
== PAM_SUCCESS
) {
341 * Put ccname into the pamh so that login
342 * apps can pick this up when they run
345 if ((result
= pam_putenv(pamh
, kmd
->env
))
347 /* should not happen but... */
348 __pam_log(LOG_AUTH
| LOG_ERR
,
350 " pam_putenv failed: result: %d",
356 /* for lack of a Solaris unputenv() */
357 krb5_unsetenv(KRB5_ENV_CCNAME
);
362 kmd
->auth_status
= result
;
366 __pam_log(LOG_AUTH
| LOG_DEBUG
,
367 "PAM-KRB5 (auth): end: %s", pam_strerror(pamh
, result
));
372 static krb5_error_code
380 krb5_prompt prompts
[])
382 krb5_error_code rc
= KRB5_LIBOS_CANTREADPWD
;
383 pam_handle_t
*pamh
= (pam_handle_t
*)data
;
384 struct pam_conv
*pam_convp
;
385 struct pam_message
*msgs
= NULL
;
386 struct pam_response
*ret_respp
= NULL
;
388 krb5_prompt_type
*prompt_type
= krb5_get_prompt_types(ctx
);
389 char tmpbuf
[PAM_MAX_MSG_SIZE
];
392 assert(num_prompts
> 0);
395 * Because this function should never be used for password prompts,
396 * disallow password prompts.
398 for (i
= 0; i
< num_prompts
; i
++) {
399 switch (prompt_type
[i
]) {
400 case KRB5_PROMPT_TYPE_PASSWORD
:
401 case KRB5_PROMPT_TYPE_NEW_PASSWORD
:
402 case KRB5_PROMPT_TYPE_NEW_PASSWORD_AGAIN
:
407 if (pam_get_item(pamh
, PAM_CONV
, (void **)&pam_convp
) != PAM_SUCCESS
) {
410 if (pam_convp
== NULL
) {
414 msgs
= (struct pam_message
*)calloc(num_prompts
,
415 sizeof (struct pam_message
));
419 (void) memset(msgs
, 0, sizeof (struct pam_message
) * num_prompts
);
421 for (i
= 0; i
< num_prompts
; i
++) {
422 /* convert krb prompt style to PAM style */
423 if (prompts
[i
].hidden
) {
424 msgs
[i
].msg_style
= PAM_PROMPT_ECHO_OFF
;
426 msgs
[i
].msg_style
= PAM_PROMPT_ECHO_ON
;
429 * krb expects the prompting function to append ": " to the
432 if (snprintf(tmpbuf
, sizeof (tmpbuf
), "%s: ",
433 prompts
[i
].prompt
) < 0) {
436 msgs
[i
].msg
= strdup(tmpbuf
);
437 if (msgs
[i
].msg
== NULL
) {
443 * Call PAM conv function to display the prompt.
446 if ((pam_convp
->conv
)(num_prompts
, &msgs
, &ret_respp
,
447 pam_convp
->appdata_ptr
) == PAM_SUCCESS
) {
448 for (i
= 0; i
< num_prompts
; i
++) {
449 /* convert PAM response to krb prompt reply format */
450 assert(prompts
[i
].reply
->data
!= NULL
);
451 assert(ret_respp
[i
].resp
!= NULL
);
453 if (strlcpy(prompts
[i
].reply
->data
,
454 ret_respp
[i
].resp
, prompts
[i
].reply
->length
) >=
455 prompts
[i
].reply
->length
) {
456 char errmsg
[1][PAM_MAX_MSG_SIZE
];
458 (void) snprintf(errmsg
[0], PAM_MAX_MSG_SIZE
,
459 "%s", dgettext(TEXT_DOMAIN
,
460 "Reply too long: "));
461 (void) __pam_display_msg(pamh
, PAM_ERROR_MSG
,
468 * newline must be replaced with \0 terminator
470 retp
= strchr(prompts
[i
].reply
->data
, '\n');
473 /* NULL terminator should not be counted */
474 prompts
[i
].reply
->length
=
475 strlen(prompts
[i
].reply
->data
);
482 for (i
= 0; i
< num_prompts
; i
++) {
486 if (ret_respp
[i
].resp
) {
487 /* 0 out sensitive data before free() */
488 (void) memset(ret_respp
[i
].resp
, 0,
489 strlen(ret_respp
[i
].resp
));
490 free(ret_respp
[i
].resp
);
501 krb5_module_data_t
*kmd
,
504 boolean_t verify_tik
)
506 krb5_principal me
= NULL
, clientp
= NULL
;
507 krb5_principal server
= NULL
, serverp
= NULL
;
508 krb5_creds
*my_creds
;
510 krb5_error_code code
= 0;
511 char kuser
[2*MAXHOSTNAMELEN
];
512 krb5_deltat lifetime
;
514 krb5_deltat krb5_max_duration
;
515 int options
= KRB5_DEFAULT_OPTIONS
;
516 krb5_data tgtname
= {
521 krb5_get_init_creds_opt
*opts
= NULL
;
522 krb5_kdc_rep
*as_reply
= NULL
;
524 * "result" should not be assigned PAM_SUCCESS unless
525 * authentication has succeeded and there are no other errors.
527 * "code" is sometimes used for PAM codes, sometimes for krb5
530 int result
= PAM_AUTH_ERR
;
533 __pam_log(LOG_AUTH
| LOG_DEBUG
,
534 "PAM-KRB5 (auth): attempt_krb5_auth: start: user='%s'",
535 user
? user
: "<null>");
537 /* need to free context with krb5_free_context */
538 if (code
= krb5_init_secure_context(&kmd
->kcontext
)) {
539 __pam_log(LOG_AUTH
| LOG_ERR
,
540 "PAM-KRB5 (auth): Error initializing "
542 error_message(code
));
543 return (PAM_SYSTEM_ERR
);
546 if ((code
= get_kmd_kuser(kmd
->kcontext
, (const char *)user
, kuser
,
547 2*MAXHOSTNAMELEN
)) != 0) {
548 /* get_kmd_kuser returns proper PAM error statuses */
552 if ((code
= krb5_parse_name(kmd
->kcontext
, kuser
, &me
)) != 0) {
553 krb5_free_context(kmd
->kcontext
);
554 kmd
->kcontext
= NULL
;
555 return (PAM_SYSTEM_ERR
);
558 /* call krb5_free_cred_contents() on error */
559 my_creds
= &kmd
->initcreds
;
562 krb5_copy_principal(kmd
->kcontext
, me
, &my_creds
->client
))) {
563 result
= PAM_SYSTEM_ERR
;
566 clientp
= my_creds
->client
;
568 if (code
= krb5_build_principal_ext(kmd
->kcontext
, &server
,
569 krb5_princ_realm(kmd
->kcontext
, me
)->length
,
570 krb5_princ_realm(kmd
->kcontext
, me
)->data
,
571 tgtname
.length
, tgtname
.data
,
572 krb5_princ_realm(kmd
->kcontext
, me
)->length
,
573 krb5_princ_realm(kmd
->kcontext
, me
)->data
, 0)) {
574 __pam_log(LOG_AUTH
| LOG_ERR
,
575 "PAM-KRB5 (auth): attempt_krb5_auth: "
576 "krb5_build_princ_ext failed: %s",
577 error_message(code
));
578 result
= PAM_SYSTEM_ERR
;
582 if (code
= krb5_copy_principal(kmd
->kcontext
, server
,
583 &my_creds
->server
)) {
584 result
= PAM_SYSTEM_ERR
;
587 serverp
= my_creds
->server
;
589 if (code
= krb5_timeofday(kmd
->kcontext
, &now
)) {
590 __pam_log(LOG_AUTH
| LOG_ERR
,
591 "PAM-KRB5 (auth): attempt_krb5_auth: "
592 "krb5_timeofday failed: %s",
593 error_message(code
));
594 result
= PAM_SYSTEM_ERR
;
599 * set the values for lifetime and rlife to be the maximum
602 krb5_max_duration
= KRB5_KDB_EXPIRATION
- now
- 60*60;
603 lifetime
= krb5_max_duration
;
604 rlife
= krb5_max_duration
;
607 * Let us get the values for various options
608 * from Kerberos configuration file
611 krb_realm
= krb5_princ_realm(kmd
->kcontext
, me
)->data
;
612 profile_get_options_boolean(kmd
->kcontext
->profile
,
613 realmdef
, config_option
);
614 profile_get_options_boolean(kmd
->kcontext
->profile
,
615 appdef
, config_option
);
616 profile_get_options_string(kmd
->kcontext
->profile
,
617 realmdef
, config_times
);
618 profile_get_options_string(kmd
->kcontext
->profile
,
619 appdef
, config_times
);
622 code
= krb5_string_to_deltat(renew_timeval
, &rlife
);
623 if (code
!= 0 || rlife
== 0 || rlife
> krb5_max_duration
) {
624 __pam_log(LOG_AUTH
| LOG_ERR
,
625 "PAM-KRB5 (auth): Bad max_renewable_life "
626 " value '%s' in Kerberos config file",
628 result
= PAM_SYSTEM_ERR
;
633 code
= krb5_string_to_deltat(life_timeval
, &lifetime
);
634 if (code
!= 0 || lifetime
== 0 ||
635 lifetime
> krb5_max_duration
) {
636 __pam_log(LOG_AUTH
| LOG_ERR
,
637 "lifetime value '%s' in Kerberos config file",
639 result
= PAM_SYSTEM_ERR
;
643 /* start timer when request gets to KDC */
644 my_creds
->times
.starttime
= 0;
645 my_creds
->times
.endtime
= now
+ lifetime
;
647 if (options
& KDC_OPT_RENEWABLE
) {
648 my_creds
->times
.renew_till
= now
+ rlife
;
650 my_creds
->times
.renew_till
= 0;
652 code
= krb5_get_init_creds_opt_alloc(kmd
->kcontext
, &opts
);
654 __pam_log(LOG_AUTH
| LOG_ERR
,
655 "Error allocating gic opts: %s",
656 error_message(code
));
657 result
= PAM_SYSTEM_ERR
;
661 krb5_get_init_creds_opt_set_tkt_life(opts
, lifetime
);
663 if (proxiable_flag
) { /* Set in config file */
665 __pam_log(LOG_AUTH
| LOG_DEBUG
,
666 "PAM-KRB5 (auth): Proxiable tickets "
668 krb5_get_init_creds_opt_set_proxiable(opts
, TRUE
);
670 if (forwardable_flag
) {
672 __pam_log(LOG_AUTH
| LOG_DEBUG
,
673 "PAM-KRB5 (auth): Forwardable tickets "
675 krb5_get_init_creds_opt_set_forwardable(opts
, TRUE
);
677 if (renewable_flag
) {
679 __pam_log(LOG_AUTH
| LOG_DEBUG
,
680 "PAM-KRB5 (auth): Renewable tickets "
682 krb5_get_init_creds_opt_set_renew_life(opts
, rlife
);
684 if (no_address_flag
) {
686 __pam_log(LOG_AUTH
| LOG_DEBUG
,
687 "PAM-KRB5 (auth): Addressless tickets "
689 krb5_get_init_creds_opt_set_address_list(opts
, NULL
);
693 * mech_krb5 interprets empty passwords as NULL passwords and tries to
694 * read a password from stdin. Since we are in pam this is bad and
695 * should not be allowed.
697 * Note, the logic now is that if the preauth_type is PKINIT then
698 * provide a proper PAMcentric prompt function that the underlying
699 * PKINIT preauth plugin will use to prompt for the PIN.
701 if (kmd
->preauth_type
== KRB_PKINIT
) {
705 * Note: we want to limit preauth types to just those for PKINIT
706 * but krb5_get_init_creds() doesn't support that at this point.
707 * Instead we rely on pam_krb5_prompter() to limit prompts to
708 * non-password types. So all we can do here is set the preauth
709 * list so krb5_get_init_creds() will try that first.
711 krb5_preauthtype pk_pa_list
[] = {
712 KRB5_PADATA_PK_AS_REQ
,
713 KRB5_PADATA_PK_AS_REQ_OLD
715 krb5_get_init_creds_opt_set_preauth_list(opts
, pk_pa_list
, 2);
717 if (*krb5_pass
== NULL
|| strlen(*krb5_pass
) != 0) {
718 if (*krb5_pass
!= NULL
) {
719 /* treat the krb5_pass as a PIN */
720 code
= krb5_get_init_creds_opt_set_pa(
721 kmd
->kcontext
, opts
, "PIN", *krb5_pass
);
725 code
= __krb5_get_init_creds_password(
729 NULL
, /* clear text passwd */
730 pam_krb5_prompter
, /* prompter */
731 pamh
, /* prompter data */
733 NULL
, /* defaults to krbtgt@REALM */
739 code
= KRB5KRB_AP_ERR_BAD_INTEGRITY
;
743 * Do password based preauths
745 * See earlier PKINIT comment. We are doing something similar
746 * here but we do not pass in a prompter (we assume
747 * pam_authtok_get has already prompted for that).
749 if (*krb5_pass
== NULL
|| strlen(*krb5_pass
) == 0) {
750 code
= KRB5KRB_AP_ERR_BAD_INTEGRITY
;
752 krb5_preauthtype pk_pa_list
[] = {
753 KRB5_PADATA_ENC_TIMESTAMP
756 krb5_get_init_creds_opt_set_preauth_list(opts
,
760 * We call our own private version of gic_pwd, because
761 * we need more information, such as password/account
762 * expiration, that is found in the as_reply. The
763 * "prompter" interface is not granular enough for PAM
766 code
= __krb5_get_init_creds_password(kmd
->kcontext
,
769 *krb5_pass
, /* clear text passwd */
773 NULL
, /* defaults to krbtgt@REALM */
780 __pam_log(LOG_AUTH
| LOG_DEBUG
,
781 "PAM-KRB5 (auth): attempt_krb5_auth: "
782 "krb5_get_init_creds_password returns: %s",
783 code
== 0 ? "SUCCESS" : error_message(code
));
787 /* got a tgt, let's verify it */
789 krb5_verify_init_creds_opt vopts
;
791 krb5_principal sp
= NULL
;
792 char kt_name
[MAX_KEYTAB_NAME_LEN
];
795 krb5_verify_init_creds_opt_init(&vopts
);
797 code
= krb5_verify_init_creds(kmd
->kcontext
,
799 NULL
, /* defaults to host/localhost@REALM */
805 result
= PAM_SYSTEM_ERR
;
808 * Give a better error message when the
809 * keytable entry isn't found or the keytab
810 * file cannot be found.
812 if (krb5_sname_to_principal(kmd
->kcontext
, NULL
,
813 NULL
, KRB5_NT_SRV_HST
, &sp
))
816 fqdn
= sp
->data
[1].data
;
818 if (krb5_kt_default_name(kmd
->kcontext
, kt_name
,
820 (void) strlcpy(kt_name
,
825 case KRB5_KT_NOTFOUND
:
826 __pam_log(LOG_AUTH
| LOG_ERR
,
828 "krb5_verify_init_creds failed:"
829 " Key table entry \"host/%s\""
834 __pam_log(LOG_AUTH
| LOG_ERR
,
836 "krb5_verify_init_creds failed:"
837 " Keytab file \"%s\""
838 " does not exist.\n",
842 __pam_log(LOG_AUTH
| LOG_ERR
,
844 "krb5_verify_init_creds failed:"
846 error_message(code
));
851 krb5_free_principal(kmd
->kcontext
, sp
);
856 kmd
->expiration
= as_reply
->enc_part2
->key_exp
;
860 case KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN
:
862 * Since this principal is not part of the local
863 * Kerberos realm, we just return PAM_USER_UNKNOWN.
865 result
= PAM_USER_UNKNOWN
;
868 __pam_log(LOG_AUTH
| LOG_DEBUG
,
869 "PAM-KRB5 (auth): attempt_krb5_auth:"
870 " User is not part of the local Kerberos"
871 " realm: %s", error_message(code
));
874 case KRB5KDC_ERR_PREAUTH_FAILED
:
875 case KRB5KRB_AP_ERR_BAD_INTEGRITY
:
877 * We could be trying the password from a previous
878 * pam authentication module, but we don't want to
879 * generate an error if the unix password is different
880 * than the Kerberos password...
884 case KRB5KDC_ERR_KEY_EXP
:
885 if (!kmd
->err_on_exp
) {
887 * Request a tik for changepw service and it will tell
888 * us if pw is good or not. If PKINIT is being done it
889 * is possible that *krb5_pass may be NULL so check for
890 * that. If that is the case this function will return
893 if (*krb5_pass
!= NULL
) {
894 code
= krb5_verifypw(kuser
, *krb5_pass
,
897 __pam_log(LOG_AUTH
| LOG_DEBUG
,
899 "attempt_krb5_auth: "
900 "verifypw %d", code
);
904 * pw is good, set age status for
907 kmd
->age_status
= PAM_NEW_AUTHTOK_REQD
;
915 result
= PAM_SYSTEM_ERR
;
917 __pam_log(LOG_AUTH
| LOG_DEBUG
,
918 "PAM-KRB5 (auth): error %d - %s",
919 code
, error_message(code
));
925 * success for the entered pw or PKINIT succeeded.
927 * we can't rely on the pw in PAM_AUTHTOK
928 * to be the (correct) krb5 one so
929 * store krb5 pw in module data for
930 * use in acct_mgmt. Note that *krb5_pass may be NULL if we're
933 if (*krb5_pass
!= NULL
&&
934 !(kmd
->password
= strdup(*krb5_pass
))) {
935 __pam_log(LOG_AUTH
| LOG_ERR
,
936 "Cannot strdup password");
937 result
= PAM_BUF_ERR
;
941 result
= PAM_SUCCESS
;
946 /* jump (or reach) here if error and cred cache has been init */
949 __pam_log(LOG_AUTH
| LOG_DEBUG
,
950 "PAM-KRB5 (auth): clearing initcreds in "
951 "pam_authenticate()");
953 krb5_free_cred_contents(kmd
->kcontext
, &kmd
->initcreds
);
954 (void) memset((char *)&kmd
->initcreds
, 0, sizeof (krb5_creds
));
958 krb5_free_principal(kmd
->kcontext
, server
);
960 krb5_free_principal(kmd
->kcontext
, me
);
962 krb5_free_kdc_rep(kmd
->kcontext
, as_reply
);
965 * clientp or serverp could be NULL in certain error cases in this
966 * function. mycreds->[client|server] could also be NULL in case
967 * of error in this function, see out_err above. The pointers clientp
968 * and serverp reference the input argument in my_creds for
969 * get_init_creds and must be freed if the input argument does not
970 * match the output argument, which occurs during a successful call
973 if (clientp
&& my_creds
->client
&& clientp
!= my_creds
->client
)
974 krb5_free_principal(kmd
->kcontext
, clientp
);
975 if (serverp
&& my_creds
->server
&& serverp
!= my_creds
->server
)
976 krb5_free_principal(kmd
->kcontext
, serverp
);
979 krb5_free_context(kmd
->kcontext
);
980 kmd
->kcontext
= NULL
;
983 krb5_get_init_creds_opt_free(kmd
->kcontext
, opts
);
986 __pam_log(LOG_AUTH
| LOG_DEBUG
,
987 "PAM-KRB5 (auth): attempt_krb5_auth returning %d",
990 return (kmd
->auth_status
= result
);
995 krb5_cleanup(pam_handle_t
*pamh
, void *data
, int pam_status
)
997 krb5_module_data_t
*kmd
= (krb5_module_data_t
*)data
;
1003 __pam_log(LOG_AUTH
| LOG_DEBUG
,
1004 "PAM-KRB5 (auth): krb5_cleanup auth_status = %d",
1009 * Apps could be calling pam_end here, so we should always clean
1010 * up regardless of success or failure here.
1013 (void) krb5_cc_close(kmd
->kcontext
, kmd
->ccache
);
1015 if (kmd
->password
) {
1016 (void) memset(kmd
->password
, 0, strlen(kmd
->password
));
1017 free(kmd
->password
);
1024 krb5_free_cred_contents(kmd
->kcontext
, &kmd
->initcreds
);
1025 (void) memset((char *)&kmd
->initcreds
, 0, sizeof (krb5_creds
));