krb5_child.c revision 714446cfe8f7f577e8c546cfc1b4cf7d425b5f7a
a6098862048d4bb469130b9ff21be3020d6f2c54Sumit Bose Kerberos 5 Backend Module -- tgt_req and changepw child
a6098862048d4bb469130b9ff21be3020d6f2c54Sumit Bose Sumit Bose <sbose@redhat.com>
a6098862048d4bb469130b9ff21be3020d6f2c54Sumit Bose Copyright (C) 2009-2010 Red Hat
a6098862048d4bb469130b9ff21be3020d6f2c54Sumit Bose This program is free software; you can redistribute it and/or modify
a6098862048d4bb469130b9ff21be3020d6f2c54Sumit Bose it under the terms of the GNU General Public License as published by
a6098862048d4bb469130b9ff21be3020d6f2c54Sumit Bose the Free Software Foundation; either version 3 of the License, or
a6098862048d4bb469130b9ff21be3020d6f2c54Sumit Bose (at your option) any later version.
a6098862048d4bb469130b9ff21be3020d6f2c54Sumit Bose This program is distributed in the hope that it will be useful,
a6098862048d4bb469130b9ff21be3020d6f2c54Sumit Bose but WITHOUT ANY WARRANTY; without even the implied warranty of
a6098862048d4bb469130b9ff21be3020d6f2c54Sumit Bose MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
a6098862048d4bb469130b9ff21be3020d6f2c54Sumit Bose GNU General Public License for more details.
a6098862048d4bb469130b9ff21be3020d6f2c54Sumit Bose You should have received a copy of the GNU General Public License
a6098862048d4bb469130b9ff21be3020d6f2c54Sumit Bose along with this program. If not, see <http://www.gnu.org/licenses/>.
a6098862048d4bb469130b9ff21be3020d6f2c54Sumit Bose#define SSSD_KRB5_CHANGEPW_PRINCIPAL "kadmin/changepw"
a6098862048d4bb469130b9ff21be3020d6f2c54Sumit Bose const char *upn;
a6098862048d4bb469130b9ff21be3020d6f2c54Sumit Bose#define KRB5_CHILD_DEBUG(level, error) KRB5_DEBUG(level, krb5_error_ctx, error)
a6098862048d4bb469130b9ff21be3020d6f2c54Sumit Bosestatic krb5_error_code set_lifetime_options(krb5_get_init_creds_opt *options)
a6098862048d4bb469130b9ff21be3020d6f2c54Sumit Bose lifetime_str = getenv(SSSD_KRB5_RENEWABLE_LIFETIME);
a6098862048d4bb469130b9ff21be3020d6f2c54Sumit Bose DEBUG(SSSDBG_CONF_SETTINGS, "Cannot read [%s] from environment.\n",
a6098862048d4bb469130b9ff21be3020d6f2c54Sumit Bose /* Unset option flag to make sure defaults from krb5.conf are used. */
a6098862048d4bb469130b9ff21be3020d6f2c54Sumit Bose options->flags &= ~(KRB5_GET_INIT_CREDS_OPT_RENEW_LIFE);
a6098862048d4bb469130b9ff21be3020d6f2c54Sumit Bose kerr = krb5_string_to_deltat(lifetime_str, &lifetime);
a6098862048d4bb469130b9ff21be3020d6f2c54Sumit Bose "krb5_string_to_deltat failed for [%s].\n",
a6098862048d4bb469130b9ff21be3020d6f2c54Sumit Bose DEBUG(SSSDBG_CONF_SETTINGS, "%s is set to [%s]\n",
a6098862048d4bb469130b9ff21be3020d6f2c54Sumit Bose krb5_get_init_creds_opt_set_renew_life(options, lifetime);
a6098862048d4bb469130b9ff21be3020d6f2c54Sumit Bose DEBUG(SSSDBG_CONF_SETTINGS, "Cannot read [%s] from environment.\n",
a6098862048d4bb469130b9ff21be3020d6f2c54Sumit Bose /* Unset option flag to make sure defaults from krb5.conf are used. */
a6098862048d4bb469130b9ff21be3020d6f2c54Sumit Bose options->flags &= ~(KRB5_GET_INIT_CREDS_OPT_TKT_LIFE);
a6098862048d4bb469130b9ff21be3020d6f2c54Sumit Bose kerr = krb5_string_to_deltat(lifetime_str, &lifetime);
a6098862048d4bb469130b9ff21be3020d6f2c54Sumit Bose "krb5_string_to_deltat failed for [%s].\n",
a6098862048d4bb469130b9ff21be3020d6f2c54Sumit Bose "%s is set to [%s]\n", SSSD_KRB5_LIFETIME, lifetime_str);
a6098862048d4bb469130b9ff21be3020d6f2c54Sumit Bose krb5_get_init_creds_opt_set_tkt_life(options, lifetime);
a6098862048d4bb469130b9ff21be3020d6f2c54Sumit Bosestatic void set_canonicalize_option(krb5_get_init_creds_opt *opts)
a6098862048d4bb469130b9ff21be3020d6f2c54Sumit Bose if (tmp_str != NULL && strcasecmp(tmp_str, "true") == 0) {
a6098862048d4bb469130b9ff21be3020d6f2c54Sumit Bose DEBUG(SSSDBG_CONF_SETTINGS, "%s is set to [%s]\n",
a6098862048d4bb469130b9ff21be3020d6f2c54Sumit Bose SSSD_KRB5_CANONICALIZE, tmp_str ? tmp_str : "not set");
a6098862048d4bb469130b9ff21be3020d6f2c54Sumit Bose sss_krb5_get_init_creds_opt_set_canonicalize(opts, canonicalize);
a6098862048d4bb469130b9ff21be3020d6f2c54Sumit Bosestatic void set_changepw_options(krb5_get_init_creds_opt *options)
a6098862048d4bb469130b9ff21be3020d6f2c54Sumit Bose sss_krb5_get_init_creds_opt_set_canonicalize(options, 0);
a6098862048d4bb469130b9ff21be3020d6f2c54Sumit Bose krb5_get_init_creds_opt_set_forwardable(options, 0);
a6098862048d4bb469130b9ff21be3020d6f2c54Sumit Bose krb5_get_init_creds_opt_set_proxiable(options, 0);
a6098862048d4bb469130b9ff21be3020d6f2c54Sumit Bose krb5_get_init_creds_opt_set_renew_life(options, 0);
a6098862048d4bb469130b9ff21be3020d6f2c54Sumit Bose krb5_get_init_creds_opt_set_tkt_life(options, 5*60);
a6098862048d4bb469130b9ff21be3020d6f2c54Sumit Bosestatic void revert_changepw_options(krb5_get_init_creds_opt *options)
b6dfbf81c61d4431aaa81687ec53e892f8b71edbSumit Bose /* Currently we do not set forwardable and proxiable explicitly, the flags
b6dfbf81c61d4431aaa81687ec53e892f8b71edbSumit Bose * must be removed so that libkrb5 can take the defaults from krb5.conf */
b6dfbf81c61d4431aaa81687ec53e892f8b71edbSumit Bose options->flags &= ~(KRB5_GET_INIT_CREDS_OPT_FORWARDABLE);
b6dfbf81c61d4431aaa81687ec53e892f8b71edbSumit Bose options->flags &= ~(KRB5_GET_INIT_CREDS_OPT_PROXIABLE);
b6dfbf81c61d4431aaa81687ec53e892f8b71edbSumit Bose DEBUG(SSSDBG_OP_FAILURE, ("set_lifetime_options failed.\n"));
b6dfbf81c61d4431aaa81687ec53e892f8b71edbSumit Bosestatic errno_t sss_send_pac(krb5_authdata **pac_authdata)
b6dfbf81c61d4431aaa81687ec53e892f8b71edbSumit Bose ret = sss_pac_make_request(SSS_PAC_ADD_PAC_USER, &sss_data,
b6dfbf81c61d4431aaa81687ec53e892f8b71edbSumit Bose DEBUG(SSSDBG_OP_FAILURE, "sss_pac_make_request failed [%d][%d].\n",
b6dfbf81c61d4431aaa81687ec53e892f8b71edbSumit Bosestatic void sss_krb5_expire_callback_func(krb5_context context, void *data,
a6098862048d4bb469130b9ff21be3020d6f2c54Sumit Bose struct krb5_req *kr = talloc_get_type(data, struct krb5_req);
a6098862048d4bb469130b9ff21be3020d6f2c54Sumit Bose DEBUG(SSSDBG_CRIT_FAILURE, "Time to expire out of range.\n");
a6098862048d4bb469130b9ff21be3020d6f2c54Sumit Bose DEBUG(SSSDBG_TRACE_INTERNAL, "exp_time: [%ld]\n", exp_time);
a6098862048d4bb469130b9ff21be3020d6f2c54Sumit Bose DEBUG(SSSDBG_CRIT_FAILURE, "talloc_size failed.\n");
b6dfbf81c61d4431aaa81687ec53e892f8b71edbSumit Bose ret = pam_add_response(kr->pd, SSS_PAM_USER_INFO, 2 * sizeof(uint32_t),
b6dfbf81c61d4431aaa81687ec53e892f8b71edbSumit Bose DEBUG(SSSDBG_CRIT_FAILURE, "pam_add_response failed.\n");
b6dfbf81c61d4431aaa81687ec53e892f8b71edbSumit Bose * TODO: These features generally would requires a significant refactoring
b6dfbf81c61d4431aaa81687ec53e892f8b71edbSumit Bose * of SSSD and MIT krb5 doesn't support them anyway. They are listed here
b6dfbf81c61d4431aaa81687ec53e892f8b71edbSumit Bose * simply as a reminder of things that might become future feature potential.
b6dfbf81c61d4431aaa81687ec53e892f8b71edbSumit Bose * 1. tokeninfo selection
b6dfbf81c61d4431aaa81687ec53e892f8b71edbSumit Bose * 2. challenge
b6dfbf81c61d4431aaa81687ec53e892f8b71edbSumit Bose * 3. discreet token/pin prompting
b6dfbf81c61d4431aaa81687ec53e892f8b71edbSumit Bose * 4. interactive otp format correction
b6dfbf81c61d4431aaa81687ec53e892f8b71edbSumit Bose * 5. nextOTP
b6dfbf81c61d4431aaa81687ec53e892f8b71edbSumit Bosetypedef int (*checker)(int c);
b6dfbf81c61d4431aaa81687ec53e892f8b71edbSumit Bosestatic krb5_error_code tokeninfo_matches(TALLOC_CTX *mem_ctx,
b6dfbf81c61d4431aaa81687ec53e892f8b71edbSumit Bose if (ti->flags & KRB5_RESPONDER_OTP_FLAGS_NEXTOTP) {
c51a204a40b8f85f7f525edb3e24520916d8b9c7Sumit Bose /* This is a non-sensical value. */
b6dfbf81c61d4431aaa81687ec53e892f8b71edbSumit Bose if (ti->flags & KRB5_RESPONDER_OTP_FLAGS_COLLECT_TOKEN) {
b6dfbf81c61d4431aaa81687ec53e892f8b71edbSumit Bose /* ASSUMPTION: authtok has one of the following formats:
b6dfbf81c61d4431aaa81687ec53e892f8b71edbSumit Bose * 1. TokenValue
b6dfbf81c61d4431aaa81687ec53e892f8b71edbSumit Bose * 2. PIN+TokenValue
c51a204a40b8f85f7f525edb3e24520916d8b9c7Sumit Bose talloc_set_destructor(token, token_pin_destructor);
b6dfbf81c61d4431aaa81687ec53e892f8b71edbSumit Bose if (ti->flags & KRB5_RESPONDER_OTP_FLAGS_COLLECT_PIN) {
b6dfbf81c61d4431aaa81687ec53e892f8b71edbSumit Bose /* If the server desires a separate pin, we will split it.
b6dfbf81c61d4431aaa81687ec53e892f8b71edbSumit Bose * ASSUMPTION: Format of authtok is PIN+TokenValue. */
b6dfbf81c61d4431aaa81687ec53e892f8b71edbSumit Bose if (ti->flags & KRB5_RESPONDER_OTP_FLAGS_SEPARATE_PIN) {
b6dfbf81c61d4431aaa81687ec53e892f8b71edbSumit Bose /* Copy the PIN from the front of the value. */
b6dfbf81c61d4431aaa81687ec53e892f8b71edbSumit Bose pin = talloc_strndup(NULL, pwd, len - ti->length);
b6dfbf81c61d4431aaa81687ec53e892f8b71edbSumit Bose talloc_set_destructor(pin, token_pin_destructor);
b6dfbf81c61d4431aaa81687ec53e892f8b71edbSumit Bose /* Remove the PIN from the front of the token value. */
b6dfbf81c61d4431aaa81687ec53e892f8b71edbSumit Bose memmove(token, token + len - ti->length, ti->length + 1);
b6dfbf81c61d4431aaa81687ec53e892f8b71edbSumit Bose talloc_set_destructor(pin, token_pin_destructor);
b6dfbf81c61d4431aaa81687ec53e892f8b71edbSumit Bose /* If check is set, we need to verify the contents of the token. */
b6dfbf81c61d4431aaa81687ec53e892f8b71edbSumit Bose for (i = 0; check != NULL && token[i] != '\0'; i++) {
2998435fcc95857b73049b3955af9889ab595f24Sumit Bosestatic krb5_error_code answer_otp(krb5_context ctx,
2998435fcc95857b73049b3955af9889ab595f24Sumit Bose ret = krb5_responder_otp_get_challenge(ctx, rctx, &chl);
2998435fcc95857b73049b3955af9889ab595f24Sumit Bose /* Either an error, or nothing to do. */
2998435fcc95857b73049b3955af9889ab595f24Sumit Bose if (chl->tokeninfo == NULL || chl->tokeninfo[0] == NULL) {
2998435fcc95857b73049b3955af9889ab595f24Sumit Bose /* No tokeninfos? Absurd! */
2998435fcc95857b73049b3955af9889ab595f24Sumit Bose /* Validate our assumptions about the contents of authtok. */
2998435fcc95857b73049b3955af9889ab595f24Sumit Bose ret = sss_authtok_get_password(kr->pd->authtok, &pwd, &len);
2998435fcc95857b73049b3955af9889ab595f24Sumit Bose /* Find the first supported tokeninfo which matches our authtoken. */
2998435fcc95857b73049b3955af9889ab595f24Sumit Bose ret = tokeninfo_matches(kr, chl->tokeninfo[i], pwd, len, &token, &pin);
2998435fcc95857b73049b3955af9889ab595f24Sumit Bose "No tokeninfos found which match our credentials.\n");
2998435fcc95857b73049b3955af9889ab595f24Sumit Bose if (chl->tokeninfo[i]->flags & KRB5_RESPONDER_OTP_FLAGS_COLLECT_TOKEN) {
2998435fcc95857b73049b3955af9889ab595f24Sumit Bose /* Don't let SSSD cache the OTP authtok since it is single-use. */
2998435fcc95857b73049b3955af9889ab595f24Sumit Bose ret = pam_add_response(kr->pd, SSS_OTP, 0, NULL);
2998435fcc95857b73049b3955af9889ab595f24Sumit Bose DEBUG(SSSDBG_CRIT_FAILURE, "pam_add_response failed.\n");
2998435fcc95857b73049b3955af9889ab595f24Sumit Bose /* Respond with the appropriate answer. */
2998435fcc95857b73049b3955af9889ab595f24Sumit Bose ret = krb5_responder_otp_set_answer(ctx, rctx, i, token, pin);
2998435fcc95857b73049b3955af9889ab595f24Sumit Bose krb5_responder_otp_challenge_free(ctx, rctx, chl);
2998435fcc95857b73049b3955af9889ab595f24Sumit Bosestatic krb5_error_code sss_krb5_responder(krb5_context ctx,
2998435fcc95857b73049b3955af9889ab595f24Sumit Bose struct krb5_req *kr = talloc_get_type(data, struct krb5_req);
2998435fcc95857b73049b3955af9889ab595f24Sumit Bosestatic krb5_error_code sss_krb5_prompter(krb5_context context, void *data,
a6098862048d4bb469130b9ff21be3020d6f2c54Sumit Bose struct krb5_req *kr = talloc_get_type(data, struct krb5_req);
a6098862048d4bb469130b9ff21be3020d6f2c54Sumit Bose DEBUG(SSSDBG_CRIT_FAILURE, "Cannot handle password prompts.\n");
a6098862048d4bb469130b9ff21be3020d6f2c54Sumit Bose "Prompter called with empty banner, nothing to do.\n");
a6098862048d4bb469130b9ff21be3020d6f2c54Sumit Bose DEBUG(SSSDBG_FUNC_DATA, "Prompter called with [%s].\n", banner);
a6098862048d4bb469130b9ff21be3020d6f2c54Sumit Bose ret = pam_add_response(kr->pd, SSS_PAM_TEXT_MSG, strlen(banner)+1,
a6098862048d4bb469130b9ff21be3020d6f2c54Sumit Bose DEBUG(SSSDBG_CRIT_FAILURE, "pam_add_response failed.\n");
b6dfbf81c61d4431aaa81687ec53e892f8b71edbSumit Bosestatic krb5_error_code create_empty_cred(krb5_context ctx, krb5_principal princ,
2998435fcc95857b73049b3955af9889ab595f24Sumit Bose kerr = krb5_copy_principal(ctx, princ, &cred->client);
b6dfbf81c61d4431aaa81687ec53e892f8b71edbSumit Bose DEBUG(SSSDBG_CRIT_FAILURE, "krb5_copy_principal failed.\n");
a6098862048d4bb469130b9ff21be3020d6f2c54Sumit Bose kerr = krb5_build_principal_ext(ctx, &cred->server,
a6098862048d4bb469130b9ff21be3020d6f2c54Sumit Bose DEBUG(SSSDBG_CRIT_FAILURE, "krb5_build_principal_ext failed.\n");
b6dfbf81c61d4431aaa81687ec53e892f8b71edbSumit Bose DEBUG(SSSDBG_TRACE_INTERNAL, "Created empty krb5_creds.\n");
a6098862048d4bb469130b9ff21be3020d6f2c54Sumit Bose /* We only treat the FILE type case in a special way due to the history
a6098862048d4bb469130b9ff21be3020d6f2c54Sumit Bose * of storing FILE type ccache in /tmp and associated security issues */
return EOK;
return ret;
return EOK;
const char *type;
#ifdef HAVE_KRB5_CC_COLLECTION
bool switch_to_cc = false;
if (kerr) {
return ERR_INTERNAL;
#ifdef HAVE_KRB5_CC_COLLECTION
switch_to_cc = true;
#ifdef HAVE_KRB5_CC_COLLECTION
if (switch_to_cc) {
done:
if (kcc) {
return kerr;
size_t p = 0;
if (!buf) {
return ENOMEM;
*_len = p;
return EOK;
int ret;
return ERR_INTERNAL;
return ENOMEM;
return ret;
int ret;
return ret;
errno = 0;
return ret;
return EOK;
return EOK;
int ret;
unsigned int upn_len = 0;
goto done;
if (kerr != 0) {
goto done;
goto done;
done:
return ret;
bool realm_entry_found = false;
if (kerr != 0) {
return kerr;
if (kerr != 0) {
return kerr;
if (kerr != 0) {
goto done;
if (kerr != 0) {
realm_entry_found = true;
if (!realm_entry_found) {
if (kerr != 0) {
goto done;
goto done;
if (kerr != 0) {
goto done;
if (kerr == 0) {
goto done;
if (kerr != 0) {
kerr = 0;
goto done;
if (kerr != 0) {
kerr = 0;
done:
return kerr;
char *ccname)
&options);
if (kerr != 0) {
return kerr;
if (kerr != 0) {
goto done;
kerr = 0;
done:
return kerr;
const char *password)
const char *realm_name;
int realm_length;
char *cc_name;
kr);
if (kerr != 0) {
if (realm_length == 0) {
return KRB5KRB_ERR_GENERIC;
if (kerr != 0) {
return kerr;
if (kerr != 0) {
return kerr;
if (kerr != 0) {
goto done;
if (kerr) {
goto done;
if (kerr != 0) {
kerr = 0;
done:
return kerr;
if (kerr != 0) {
switch (kerr) {
return ERR_OK;
case KRB5_LIBOS_CANTREADPWD:
return ERR_NO_CREDS;
case KRB5KRB_ERR_GENERIC:
case KRB5KRB_AP_ERR_SKEW:
case KRB5_KDC_UNREACH:
case KRB5_REALM_CANT_RESOLVE:
return ERR_NETWORK_IO;
return ERR_ACCOUNT_EXPIRED;
case KRB5KDC_ERR_KEY_EXP:
return ERR_CREDS_EXPIRED;
return ERR_AUTH_FAILED;
case KRB5_PROG_ETYPE_NOSUPP:
case KRB5_PREAUTH_FAILED:
return ERR_CREDS_INVALID;
return ERR_INTERNAL;
int ret;
const char *realm_name;
int realm_length;
return ERR_NO_CREDS;
if (!prelim) {
if (realm_length == 0) {
return ERR_INTERNAL;
if (kerr != 0) {
msg);
return kerr;
if (prelim) {
return EOK;
return ERR_NO_CREDS;
return ERR_NETWORK_IO;
if (kerr != 0) {
return ERR_CHPASS_FAILED;
if (kerr == 0) {
int ret;
switch (ret) {
case EOK:
case EACCES:
return ERR_INVALID_CRED_TYPE;
return ERR_NO_CREDS;
if (kerr == 0) {
goto done;
if (kerr != 0) {
if (kerr == 0) {
done:
return ret;
if (kerr != 0) {
if (access_allowed) {
return EOK;
return ERR_AUTH_DENIED;
const char *ccname;
int ret;
return ERR_INVALID_CRED_TYPE;
if (kerr != 0) {
goto done;
if (kerr != 0) {
goto done;
if (kerr != 0) {
goto done;
if (kerr != 0) {
goto done;
if (kerr != 0) {
goto done;
if (kerr != 0) {
done:
if (kerr == 0) {
kerr = 0;
if (kerr != 0) {
return EINVAL;
switch (auth_token_type) {
case SSS_AUTHTOK_TYPE_EMPTY:
case SSS_AUTHTOK_TYPE_CCFILE:
return EINVAL;
*p += auth_token_length;
return ret;
size_t p = 0;
return ENOMEM;
p += len;
p += len;
if (len > 0) {
p += len;
p += len;
if (ret) {
return ret;
if (ret) {
return ret;
p += len;
return EOK;
return EOK;
if (krberr != 0) {
goto done;
} else if (krberr != 0) {
krberr = 0;
goto done;
krberr = 0;
done:
return krberr;
const char *primary,
const char *realm,
const char *keytab_name,
char **fast_ccname)
char *ccname;
char *server_name;
int status;
return ENOMEM;
goto done;
if (kerr) {
goto done;
if (kerr != 0) {
goto done;
goto done;
if (kerr != 0) {
goto done;
if (kerr == 0) {
goto done;
switch (fchild_pid) {
goto done;
if (kerr != 0) {
if (kerr != 0) {
exit(0);
errno = 0;
if (kerr > 0) {
if (kerr != 0) {
if (kerr == 0) {
goto done;
goto done;
kerr = 0;
done:
if (kerr == 0) {
return kerr;
errno = 0;
return ret;
return ret;
char *fast_principal_realm;
char *fast_principal;
char *tmp_str;
char *new_ccname;
if (tmp_str) {
if (kerr) {
return kerr;
&tmp_str);
if (kerr) {
return kerr;
if (!fast_principal) {
return KRB5KRB_ERR_GENERIC;
if (!fast_principal_realm) {
return ENOMEM;
if (kerr != 0) {
return kerr;
if (kerr != 0) {
return kerr;
if (kerr != 0) {
return kerr;
if (demand) {
if (kerr != 0) {
return kerr;
return EOK;
char *use_fast_str;
return EINVAL;
return EOK;
bool valid;
valid = false;
switch (ret) {
case ERR_NOT_FOUND:
case ENOENT:
case EINVAL:
case EOK:
valid = true;
return ret;
return EOK;
return ret;
return ret;
return EOK;
return ret;
return EOK;
return EOK;
if (ret != 0) {
if (ret != 0) {
return ret;
return EOK;
int parse_flags;
if (kerr != 0) {
return kerr;
if (kerr != 0) {
return EIO;
* missing in krb5.conf or to allow SSSD to work with multiple unconnected
if (kerr != 0) {
if (kerr != 0) {
return kerr;
if (kerr != 0) {
return kerr;
return ENOMEM;
if (kerr != 0) {
return kerr;
if (kerr != 0) {
return kerr;
if (!offline) {
return kerr;
int ret;
char *mem_keytab;
if (kerr != 0) {
return kerr;
if (kerr != 0) {
return kerr;
return ret;;
return ret;
if (!(offline ||
NULL);
if (kerr != 0) {
return kerr;
return kerr;
int opt;
&debug_to_stderr, 0,
switch(opt) {
if (!debug_prg_name) {
goto done;
goto done;
goto done;
if (kerr != 0) {
goto done;
if (kerr != 0) {
goto done;
goto done;
case SSS_PAM_AUTHENTICATE:
if (offline) {
case SSS_PAM_CHAUTHTOK:
case SSS_PAM_CHAUTHTOK_PRELIM:
case SSS_PAM_ACCT_MGMT:
case SSS_CMD_RENEW:
if (offline) {
goto done;
goto done;
done:
ret = 0;