krb5_child.c revision 0f76569b4cecc048974e837c92d4ca806ca3bbac
/*
SSSD
Kerberos 5 Backend Module -- tgt_req and changepw child
Authors:
Sumit Bose <sbose@redhat.com>
Copyright (C) 2009-2010 Red Hat
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <unistd.h>
#include <popt.h>
#include <security/pam_modules.h>
#include "util/sss_krb5.h"
#include "util/user_info_msg.h"
#include "util/child_common.h"
#include "providers/dp_backend.h"
#include "providers/krb5/krb5_auth.h"
#include "providers/krb5/krb5_utils.h"
#include "sss_cli.h"
#define SSSD_KRB5_CHANGEPW_PRINCIPAL "kadmin/changepw"
struct krb5_child_ctx {
/* opts taken from kinit */
/* in seconds */
int forwardable;
int proxiable;
int addresses;
int not_forwardable;
int not_proxiable;
int no_addresses;
int verbose;
char* principal_name;
char* service_name;
char* keytab_name;
char* k5_cache_name;
char* k4_cache_name;
char *kdcip;
char *realm;
char *ccache_dir;
char *ccname_template;
int auth_timeout;
};
struct krb5_req {
char* name;
int read_from_child_fd;
int write_to_child_fd;
struct krb5_child_ctx *krb5_ctx;
char *ccname;
char *keytab;
bool validate;
bool upn_from_different_realm;
char *fast_ccname;
const char *upn;
};
static krb5_context krb5_error_ctx;
{
struct sss_cli_req_data sss_data;
int ret;
int errnop;
return EIO;
}
return EOK;
}
{
int ret;
long exp_time;
if (password_expiration == 0) {
return;
}
return;
}
return;
}
}
return;
}
{
int ret;
if (num_prompts != 0) {
return KRB5_LIBOS_CANTREADPWD;
}
return EOK;
}
}
return EOK;
}
krb5_creds **_cred)
{
return ENOMEM;
}
if (kerr != 0) {
goto done;
}
if (kerr != 0) {
goto done;
}
done:
if (kerr != 0) {
}
} else {
}
return kerr;
}
static krb5_error_code
{
if (kerr != 0) {
goto done;
}
if (kerr != 0) {
goto done;
}
} else {
}
if (kerr != 0) {
goto done;
}
#ifdef HAVE_KRB5_DIRCACHE
if (kerr != 0) {
goto done;
}
#endif /* HAVE_KRB5_DIRCACHE */
if (kerr != 0) {
goto done;
}
done:
return kerr;
}
{
char *cc_file_name;
int fd = -1;
char *dummy;
char *tmp_ccname;
} else {
}
if (cc_file_name[0] != '/') {
return EINVAL;
}
return ENOMEM;
}
if (tmp_ccname == NULL) {
goto done;
}
if (tmp_ccname == NULL) {
goto done;
}
if (fd == -1) {
goto done;
}
if (kerr != 0) {
goto done;
}
if (fd != -1) {
fd = -1;
}
if (kerr != 0) {
goto done;
}
if (fd == -1) {
goto done;
}
}
if (kerr == -1) {
}
done:
}
if (fd != -1) {
}
return kerr;
}
#ifdef HAVE_KRB5_DIRCACHE
static errno_t
{
if (ret == -1) {
/* Failing the mkdir is only OK if the directory already
* exists AND it is owned by the same user and group and
* has the correct permissions.
*/
errno = 0;
if (ret == -1) {
return EIO;
}
("The directory %s is owned by %d/%d, expected %d/%d\n",
return EACCES;
}
("The directory %s has wrong permissions %o, expected 0700\n",
return EACCES;
}
} else {
return ret;
}
}
return EOK;
}
static krb5_error_code
{
const char *dirname;
return EIO;
}
goto done;
}
if (dirname[0] == ':') {
/* Cache name in the form of DIR::filepath represents a single
* ccache in a collection that we are trying to reuse.
*/
if (kerr != 0) {
goto done;
}
} else if (dirname[0] == '/') {
/* An absolute path denotes that krb5_child should create a new
* ccache. We can afford to just call mkdir(dirname) because we
* only want the last component to be created.
*/
if (kerr) {
("Cannot create directory %s\n", dirname));
goto done;
}
if (kerr != 0) {
goto done;
}
if (kerr != 0) {
goto done;
}
} else {
("Wrong residual format for DIR in ccache %s\n", ccname));
return EIO;
}
if (kerr != 0) {
goto done;
}
done:
}
return kerr;
}
#endif /* HAVE_KRB5_DIRCACHE */
static krb5_error_code
{
enum sss_krb5_cc_type cctype;
switch (cctype) {
case SSS_KRB5_TYPE_FILE:
#ifdef HAVE_KRB5_DIRCACHE
case SSS_KRB5_TYPE_DIR:
#endif /* HAVE_KRB5_DIRCACHE */
default:
return EINVAL;
}
return EINVAL; /* Should never get here */
}
{
size_t p = 0;
struct response_data *pdr;
/* A buffer with the following structure must be created:
* int32_t status of the request (required)
* message (zero or more)
*
* A message consists of:
* int32_t type of the message
* int32_t length of the following data
* uint8_t[len] data
*/
}
return ENOMEM;
}
}
return EOK;
}
int pam_status)
{
int ret;
return NULL;
}
if (kerr == 0) {
} else {
return NULL;
}
return NULL;
}
}
} else {
return NULL;
}
}
}
return NULL;
}
return resp;
}
{
int ret;
return ENOMEM;
}
errno = 0;
if (written == -1) {
return ret;
}
("Write error, wrote [%d] bytes, expected [%d]\n",
return EOK;
}
return EOK;
}
{
int ret;
int64_t t[4];
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;
}
/* We look for the first entry from our realm or take the last one */
if (validation_princ != NULL) {
}
if (kerr != 0) {
goto done;
}
if (kerr != 0) {
}
("Found keytab entry with the realm of the credential.\n"));
realm_entry_found = true;
break;
}
}
if (!realm_entry_found) {
("Keytab entry with the realm of the credential not found "
"in keytab. Using the last entry.\n"));
}
/* Close the keytab here. Even though we're using cursors, the file
* handle is stored in the krb5_keytab structure, and it gets
* overwritten when the verify_init_creds() call below creates its own
* cursor, creating a leak. */
if (kerr != 0) {
"not verifying TGT.\n"));
goto done;
}
/* check if we got any errors from krb5_kt_next_entry */
goto done;
}
/* Get the principal to which the key belongs, for logging purposes. */
if (kerr != 0) {
"not verifying TGT.\n"));
goto done;
}
&validation_ccache, &opt);
if (kerr == 0) {
principal));
} else {
"for [%s].\n", principal));
goto done;
}
/* Try to find and send the PAC to the PAC responder for principals which
* do not belong to our realm. Failures are not critical. */
if (kr->upn_from_different_realm) {
if (kerr != 0) {
"membership for user with principal [%s] " \
kerr = 0;
goto done;
}
if (kerr != 0) {
"membership for user with principal [%s] " \
kerr = 0;
}
}
done:
if (validation_ccache != NULL) {
}
}
if (validation_princ != NULL) {
}
}
return kerr;
}
{
int canonicalize = 0;
char *tmp_str;
canonicalize = 1;
}
}
char *ccname)
{
krb5_error_code kerr = 0;
&options);
if (kerr != 0) {
return kerr;
}
/* Use the updated principal in the creds in case canonicalized */
if (kerr != 0) {
goto done;
}
kerr = 0;
done:
return kerr;
}
char *password)
{
krb5_error_code kerr = 0;
int ret;
const char *realm_name;
int realm_length;
kr);
if (kerr != 0) {
}
("Attempting kinit for realm [%s]\n",realm_name));
if (kerr != 0) {
return kerr;
}
if (kerr != 0) {
return kerr;
}
} else {
}
/* We drop root privileges which were needed to read the keytab file
* for the validation of the credentials or for FAST here to run the
* ccache I/O operations with user privileges. */
return ret;
}
}
/* Use the updated principal in the creds in case canonicalized */
if (kerr != 0) {
goto done;
}
}
kerr = 0;
done:
return kerr;
}
{
int pam_status;
switch (kerr) {
case KRB5_LIBOS_CANTREADPWD:
break;
case KRB5_KDC_UNREACH:
break;
case KRB5KDC_ERR_KEY_EXP:
break;
break;
case KRB5_PREAUTH_FAILED:
break;
default:
break;
}
return pam_status;
}
{
if (kerr == 0) {
return PAM_SUCCESS;
}
return kerr_handle_error(kerr);
}
{
int ret;
krb5_error_code kerr = 0;
char *newpass_str = NULL;
int pam_status = PAM_SYSTEM_ERR;
int result_code = -1;
char *user_error_message = NULL;
char *changepw_princ = NULL;
const char *realm_name;
int realm_length;
goto sendresponse;
}
goto sendresponse;
}
if (changepw_princ == NULL) {
goto sendresponse;
}
("Created a changepw principal [%s]\n", changepw_princ));
/* We do not need a password expiration warning here. */
}
("Attempting kinit for realm [%s]\n",realm_name));
if (kerr != 0) {
goto sendresponse;
}
("Initial authentication for change password operation "
"successful.\n"));
goto sendresponse;
}
if (newpass_str == NULL) {
goto sendresponse;
}
if (kerr == KRB5_KDC_UNREACH) {
goto sendresponse;
}
if (kerr != 0 || result_code != 0) {
if (kerr != 0) {
} else {
}
if (result_code_string.length > 0) {
if (user_error_message == NULL) {
}
}
if (result_string.length > 0) {
if (user_error_message == NULL) {
}
}
if (user_error_message != NULL) {
&user_resp_len, &user_resp);
} else {
}
}
}
goto sendresponse;
}
}
return ret;
}
{
int ret;
krb5_error_code kerr = 0;
char *changepw_princ = NULL;
int pam_status = PAM_SYSTEM_ERR;
goto sendresponse;
}
goto sendresponse;
}
if (changepw_princ == NULL) {
goto sendresponse;
}
("Created a changepw principal [%s]\n", changepw_princ));
/* If the password is expired the KDC will always return
KRB5KDC_ERR_KEY_EXP regardless if the supplied password is correct or
not. In general the password can still be used to get a changepw ticket.
So we validate the password by trying to get a changepw ticket. */
if (kerr == KRB5KDC_ERR_KEY_EXP) {
if (kerr != 0) {
}
if (kerr == 0) {
}
}
}
return ret;
}
{
int status;
int ret;
/* krb5_kuserok tries to verify that kr->pd->user is a locally known
* account, so we have to unset _SSS_LOOPS to make getpwnam() work. */
"krb5_kuserok will most certainly fail.\n"));
}
if (kerr != 0) {
"krb5_kuserok may fail.\n"));
}
}
return ret;
}
{
int ret;
int status = PAM_AUTHTOK_ERR;
int kerr;
char *ccname;
goto done;
}
goto done;
}
if (kerr != 0) {
goto done;
}
if (kerr != 0) {
goto done;
}
if (kerr != 0) {
goto done;
}
} else {
}
/* We drop root privileges which were needed to read the keytab file
* for the validation of the credentials or for FAST here to run the
* ccache I/O operations with user privileges. */
goto done;
}
}
if (kerr != 0) {
goto done;
}
if (kerr != 0) {
goto done;
}
}
kerr = 0;
done:
}
}
return ret;
}
{
int ret;
int pam_status = PAM_SUCCESS;
if (ret != 0) {
}
}
return ret;
}
{
size_t p = 0;
p += len;
("cmd [%d] uid [%llu] gid [%llu] validate [%s] offline [%s] "
p += len;
p += len;
p += len;
} else {
pd->authtok_size = 0;
}
p += len;
} else {
pd->newauthtok_size = 0;
}
p += len;
} else {
}
return EOK;
}
static int krb5_cleanup(void *ptr)
{
}
}
}
return EOK;
}
{
if (krberr != 0) {
goto done;
}
if (krberr != 0) {
krberr = 0;
goto done;
}
krberr = 0;
done:
}
return krberr;
}
const char *realm,
const char *keytab_name,
char **fast_ccname)
{
char *ccname;
char *server_name;
goto done;
}
goto done;
}
if (keytab_name != NULL) {
} else {
}
if (kerr) {
("Failed to read keytab file [%s]: %s\n",
goto done;
}
if (kerr != 0) {
("find_principal_in_keytab failed for principal %s@%s.\n",
goto done;
}
if (server_name == NULL) {
goto done;
}
if (kerr != 0) {
goto done;
}
if (kerr == 0) {
goto done;
}
}
if (kerr != 0) {
goto done;
}
kerr = 0;
done:
if (client_princ != NULL) {
}
if (server_princ != NULL) {
}
if (kerr == 0) {
}
}
return kerr;
}
static errno_t
{
/* Set the global error context */
if (debug_level & SSSDBG_TRACE_ALL) {
if (kerr) {
return EIO;
}
}
return EOK;
}
{
krb5_error_code kerr = 0;
char *lifetime_str;
char *use_fast_str;
char *tmp_str;
char *fast_principal = NULL;
const char *fast_principal_realm = NULL;
goto failed;
}
("Cannot read [%s] from environment.\n", SSSD_KRB5_REALM));
}
case SSS_PAM_AUTHENTICATE:
/* If we are offline, we need to create an empty ccache file */
if (offline) {
} else {
}
break;
case SSS_PAM_CHAUTHTOK:
case SSS_PAM_CHAUTHTOK_PRELIM:
break;
case SSS_PAM_ACCT_MGMT:
break;
case SSS_CMD_RENEW:
if (!offline) {
} else {
goto failed;
}
break;
default:
goto failed;
}
if (kerr != 0) {
goto failed;
}
}
if (kerr != 0) {
goto failed;
}
if (kerr != 0) {
goto failed;
}
goto failed;
}
if (kerr != 0) {
goto failed;
}
/* A prompter is used to catch messages about when a password will
* expired. The library shall not use the prompter to ask for a new password
* but shall return KRB5KDC_ERR_KEY_EXP. */
if (kerr != 0) {
goto failed;
}
#endif
if (lifetime_str == NULL) {
} else {
if (kerr != 0) {
lifetime_str));
goto failed;
}
}
if (lifetime_str == NULL) {
} else {
if (kerr != 0) {
lifetime_str));
goto failed;
}
}
if (!offline) {
if (!tmp_str) {
} else {
if (kerr) {
goto failed;
}
&tmp_str);
if (kerr) {
goto failed;
}
if (!fast_principal) {
goto failed;
}
if (!fast_principal_realm) {
goto failed;
}
}
if (kerr != 0) {
goto failed;
}
kr->fast_ccname);
if (kerr != 0) {
"failed.\n"));
goto failed;
}
if (kerr != 0) {
"failed.\n"));
goto failed;
}
}
} else {
goto failed;
}
}
/* TODO: set options, e.g.
* krb5_get_init_creds_opt_set_forwardable
* krb5_get_init_creds_opt_set_proxiable
* krb5_get_init_creds_opt_set_etype_list
* krb5_get_init_creds_opt_set_address_list
* krb5_get_init_creds_opt_set_preauth_list
* krb5_get_init_creds_opt_set_salt
* krb5_get_init_creds_opt_set_change_password_prompt
* krb5_get_init_creds_opt_set_pa
*/
return EOK;
return kerr;
}
{
int ret;
int opt;
int debug_fd = -1;
struct poptOption long_options[] = {
_("Debug level"), NULL},
_("Add debug timestamps"), NULL},
_("Show timestamps with microseconds"), NULL},
_("An open file descriptor for the debug logs"), NULL},
};
/* Set debug level to invalid value so we can decide if -d 0 was used. */
switch(opt) {
default:
_exit(-1);
}
}
if (!debug_prg_name) {
goto fail;
}
if (debug_fd != -1) {
}
}
goto fail;
}
goto fail;
}
errno = 0;
if (len == -1) {
goto fail;
}
goto fail;
}
goto fail;
}
goto fail;
}
goto fail;
}
return 0;
fail:
exit(-1);
}