krb5_child.c revision a777a485bf73be24404fe3094c3688e604d8cbf8
640b2adac05bb7f5e9fba064434c91852c3a72e6nd/*
8e34905974b7a442a55adac3b3fdb196c389e807takashi SSSD
640b2adac05bb7f5e9fba064434c91852c3a72e6nd
640b2adac05bb7f5e9fba064434c91852c3a72e6nd Kerberos 5 Backend Module -- tgt_req and changepw child
8e34905974b7a442a55adac3b3fdb196c389e807takashi
640b2adac05bb7f5e9fba064434c91852c3a72e6nd Authors:
640b2adac05bb7f5e9fba064434c91852c3a72e6nd Sumit Bose <sbose@redhat.com>
6e89d4f6c259afc94f8806c74a33a8fe81392499sf
640b2adac05bb7f5e9fba064434c91852c3a72e6nd Copyright (C) 2009-2010 Red Hat
8e34905974b7a442a55adac3b3fdb196c389e807takashi
8e34905974b7a442a55adac3b3fdb196c389e807takashi This program is free software; you can redistribute it and/or modify
640b2adac05bb7f5e9fba064434c91852c3a72e6nd it under the terms of the GNU General Public License as published by
640b2adac05bb7f5e9fba064434c91852c3a72e6nd the Free Software Foundation; either version 3 of the License, or
640b2adac05bb7f5e9fba064434c91852c3a72e6nd (at your option) any later version.
8e34905974b7a442a55adac3b3fdb196c389e807takashi
8e34905974b7a442a55adac3b3fdb196c389e807takashi This program is distributed in the hope that it will be useful,
640b2adac05bb7f5e9fba064434c91852c3a72e6nd but WITHOUT ANY WARRANTY; without even the implied warranty of
640b2adac05bb7f5e9fba064434c91852c3a72e6nd MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
640b2adac05bb7f5e9fba064434c91852c3a72e6nd GNU General Public License for more details.
640b2adac05bb7f5e9fba064434c91852c3a72e6nd
640b2adac05bb7f5e9fba064434c91852c3a72e6nd You should have received a copy of the GNU General Public License
640b2adac05bb7f5e9fba064434c91852c3a72e6nd along with this program. If not, see <http://www.gnu.org/licenses/>.
a27e9e05958bc51ea09edb8d8d862fe8b125313bslive*/
8e34905974b7a442a55adac3b3fdb196c389e807takashi
a27e9e05958bc51ea09edb8d8d862fe8b125313bslive#include <sys/types.h>
ef685e00a47967e27d89709461728a229d762172nd#include <unistd.h>
ef685e00a47967e27d89709461728a229d762172nd#include <sys/stat.h>
ef685e00a47967e27d89709461728a229d762172nd#include <popt.h>
a27e9e05958bc51ea09edb8d8d862fe8b125313bslive
6e89d4f6c259afc94f8806c74a33a8fe81392499sf#include <security/pam_modules.h>
a27e9e05958bc51ea09edb8d8d862fe8b125313bslive
51853aa2ebfdf9903a094467e1d02099f143639daaron#include "util/util.h"
a27e9e05958bc51ea09edb8d8d862fe8b125313bslive#include "util/user_info_msg.h"
51853aa2ebfdf9903a094467e1d02099f143639daaron#include "providers/child_common.h"
a27e9e05958bc51ea09edb8d8d862fe8b125313bslive#include "providers/dp_backend.h"
a27e9e05958bc51ea09edb8d8d862fe8b125313bslive#include "providers/krb5/krb5_auth.h"
a27e9e05958bc51ea09edb8d8d862fe8b125313bslive#include "providers/krb5/krb5_utils.h"
51853aa2ebfdf9903a094467e1d02099f143639daaron
a27e9e05958bc51ea09edb8d8d862fe8b125313bslivestruct krb5_child_ctx {
51853aa2ebfdf9903a094467e1d02099f143639daaron /* opts taken from kinit */
a27e9e05958bc51ea09edb8d8d862fe8b125313bslive /* in seconds */
a27e9e05958bc51ea09edb8d8d862fe8b125313bslive krb5_deltat starttime;
a27e9e05958bc51ea09edb8d8d862fe8b125313bslive krb5_deltat lifetime;
a27e9e05958bc51ea09edb8d8d862fe8b125313bslive krb5_deltat rlife;
a27e9e05958bc51ea09edb8d8d862fe8b125313bslive
a27e9e05958bc51ea09edb8d8d862fe8b125313bslive int forwardable;
a27e9e05958bc51ea09edb8d8d862fe8b125313bslive int proxiable;
8e34905974b7a442a55adac3b3fdb196c389e807takashi int addresses;
a27e9e05958bc51ea09edb8d8d862fe8b125313bslive
ef685e00a47967e27d89709461728a229d762172nd int not_forwardable;
ef685e00a47967e27d89709461728a229d762172nd int not_proxiable;
a27e9e05958bc51ea09edb8d8d862fe8b125313bslive int no_addresses;
6e89d4f6c259afc94f8806c74a33a8fe81392499sf
222f0f03c2f9ee6343c18f80f0cb6e9aad21bc58slive int verbose;
222f0f03c2f9ee6343c18f80f0cb6e9aad21bc58slive
222f0f03c2f9ee6343c18f80f0cb6e9aad21bc58slive char* principal_name;
a27e9e05958bc51ea09edb8d8d862fe8b125313bslive char* service_name;
a27e9e05958bc51ea09edb8d8d862fe8b125313bslive char* keytab_name;
a27e9e05958bc51ea09edb8d8d862fe8b125313bslive char* k5_cache_name;
222f0f03c2f9ee6343c18f80f0cb6e9aad21bc58slive char* k4_cache_name;
222f0f03c2f9ee6343c18f80f0cb6e9aad21bc58slive
a27e9e05958bc51ea09edb8d8d862fe8b125313bslive action_type action;
a27e9e05958bc51ea09edb8d8d862fe8b125313bslive
a27e9e05958bc51ea09edb8d8d862fe8b125313bslive char *kdcip;
a27e9e05958bc51ea09edb8d8d862fe8b125313bslive char *realm;
a27e9e05958bc51ea09edb8d8d862fe8b125313bslive char *changepw_principle;
a27e9e05958bc51ea09edb8d8d862fe8b125313bslive char *ccache_dir;
a27e9e05958bc51ea09edb8d8d862fe8b125313bslive char *ccname_template;
069aec3ea15c5ad699f98f50091aad653b1c6021rbowen int auth_timeout;
a27e9e05958bc51ea09edb8d8d862fe8b125313bslive
17ca00f92106c825382359ebf0a754f8df21e316rbowen int child_debug_fd;
069aec3ea15c5ad699f98f50091aad653b1c6021rbowen};
a27e9e05958bc51ea09edb8d8d862fe8b125313bslive
6e89d4f6c259afc94f8806c74a33a8fe81392499sfstruct krb5_req {
a27e9e05958bc51ea09edb8d8d862fe8b125313bslive krb5_context ctx;
17ca00f92106c825382359ebf0a754f8df21e316rbowen krb5_principal princ;
069aec3ea15c5ad699f98f50091aad653b1c6021rbowen char* name;
069aec3ea15c5ad699f98f50091aad653b1c6021rbowen krb5_creds *creds;
a27e9e05958bc51ea09edb8d8d862fe8b125313bslive krb5_get_init_creds_opt *options;
a27e9e05958bc51ea09edb8d8d862fe8b125313bslive pid_t child_pid;
a27e9e05958bc51ea09edb8d8d862fe8b125313bslive int read_from_child_fd;
17ca00f92106c825382359ebf0a754f8df21e316rbowen int write_to_child_fd;
17ca00f92106c825382359ebf0a754f8df21e316rbowen
17ca00f92106c825382359ebf0a754f8df21e316rbowen struct be_req *req;
a27e9e05958bc51ea09edb8d8d862fe8b125313bslive struct pam_data *pd;
a27e9e05958bc51ea09edb8d8d862fe8b125313bslive struct krb5_child_ctx *krb5_ctx;
a27e9e05958bc51ea09edb8d8d862fe8b125313bslive errno_t (*child_req)(int fd, struct krb5_req *kr);
a27e9e05958bc51ea09edb8d8d862fe8b125313bslive
a27e9e05958bc51ea09edb8d8d862fe8b125313bslive char *ccname;
a27e9e05958bc51ea09edb8d8d862fe8b125313bslive char *keytab;
a27e9e05958bc51ea09edb8d8d862fe8b125313bslive bool validate;
8e34905974b7a442a55adac3b3fdb196c389e807takashi
a27e9e05958bc51ea09edb8d8d862fe8b125313bslive const char *upn;
ef685e00a47967e27d89709461728a229d762172nd uid_t uid;
ef685e00a47967e27d89709461728a229d762172nd gid_t gid;
ef685e00a47967e27d89709461728a229d762172nd};
222f0f03c2f9ee6343c18f80f0cb6e9aad21bc58slive
6e89d4f6c259afc94f8806c74a33a8fe81392499sfstatic krb5_context krb5_error_ctx;
222f0f03c2f9ee6343c18f80f0cb6e9aad21bc58slivestatic const char *__krb5_error_msg;
222f0f03c2f9ee6343c18f80f0cb6e9aad21bc58slive#define KRB5_DEBUG(level, krb5_error) do { \
222f0f03c2f9ee6343c18f80f0cb6e9aad21bc58slive __krb5_error_msg = sss_krb5_get_error_message(krb5_error_ctx, krb5_error); \
222f0f03c2f9ee6343c18f80f0cb6e9aad21bc58slive DEBUG(level, ("%d: [%d][%s]\n", __LINE__, krb5_error, __krb5_error_msg)); \
222f0f03c2f9ee6343c18f80f0cb6e9aad21bc58slive sss_krb5_free_error_message(krb5_error_ctx, __krb5_error_msg); \
222f0f03c2f9ee6343c18f80f0cb6e9aad21bc58slive} while(0);
222f0f03c2f9ee6343c18f80f0cb6e9aad21bc58slive
222f0f03c2f9ee6343c18f80f0cb6e9aad21bc58slive
222f0f03c2f9ee6343c18f80f0cb6e9aad21bc58slivestatic krb5_error_code sss_krb5_prompter(krb5_context context, void *data,
222f0f03c2f9ee6343c18f80f0cb6e9aad21bc58slive const char *name, const char *banner,
222f0f03c2f9ee6343c18f80f0cb6e9aad21bc58slive int num_prompts, krb5_prompt prompts[])
222f0f03c2f9ee6343c18f80f0cb6e9aad21bc58slive{
a27e9e05958bc51ea09edb8d8d862fe8b125313bslive int ret;
a27e9e05958bc51ea09edb8d8d862fe8b125313bslive struct krb5_req *kr = talloc_get_type(data, struct krb5_req);
a27e9e05958bc51ea09edb8d8d862fe8b125313bslive
222f0f03c2f9ee6343c18f80f0cb6e9aad21bc58slive if (num_prompts != 0) {
95e8cab14596a61826fa52477dcaebc07bfbad00colm DEBUG(1, ("Cannot handle password prompts.\n"));
8e34905974b7a442a55adac3b3fdb196c389e807takashi return KRB5_LIBOS_CANTREADPWD;
95e8cab14596a61826fa52477dcaebc07bfbad00colm }
95e8cab14596a61826fa52477dcaebc07bfbad00colm
95e8cab14596a61826fa52477dcaebc07bfbad00colm if (banner == NULL || *banner == '\0') {
95e8cab14596a61826fa52477dcaebc07bfbad00colm DEBUG(5, ("Prompter called with empty banner, nothing to do.\n"));
6e89d4f6c259afc94f8806c74a33a8fe81392499sf return EOK;
95e8cab14596a61826fa52477dcaebc07bfbad00colm }
95e8cab14596a61826fa52477dcaebc07bfbad00colm
95e8cab14596a61826fa52477dcaebc07bfbad00colm DEBUG(9, ("Prompter called with [%s].\n", banner));
95e8cab14596a61826fa52477dcaebc07bfbad00colm
95e8cab14596a61826fa52477dcaebc07bfbad00colm ret = pam_add_response(kr->pd, SSS_PAM_TEXT_MSG, strlen(banner)+1,
95e8cab14596a61826fa52477dcaebc07bfbad00colm (const uint8_t *) banner);
95e8cab14596a61826fa52477dcaebc07bfbad00colm if (ret != EOK) {
95e8cab14596a61826fa52477dcaebc07bfbad00colm DEBUG(1, ("pam_add_response failed.\n"));
95e8cab14596a61826fa52477dcaebc07bfbad00colm }
95e8cab14596a61826fa52477dcaebc07bfbad00colm
95e8cab14596a61826fa52477dcaebc07bfbad00colm return EOK;
95e8cab14596a61826fa52477dcaebc07bfbad00colm}
95e8cab14596a61826fa52477dcaebc07bfbad00colm
95e8cab14596a61826fa52477dcaebc07bfbad00colm
95e8cab14596a61826fa52477dcaebc07bfbad00colmstatic krb5_error_code create_empty_cred(struct krb5_req *kr, krb5_creds **_cred)
95e8cab14596a61826fa52477dcaebc07bfbad00colm{
17efa6b5344b7574597eec03f02ef28103e19265nd krb5_error_code kerr;
8e34905974b7a442a55adac3b3fdb196c389e807takashi krb5_creds *cred = NULL;
17efa6b5344b7574597eec03f02ef28103e19265nd krb5_data *krb5_realm;
17efa6b5344b7574597eec03f02ef28103e19265nd
17efa6b5344b7574597eec03f02ef28103e19265nd cred = calloc(sizeof(krb5_creds), 1);
ef685e00a47967e27d89709461728a229d762172nd if (cred == NULL) {
b2a8f444bd02b73948298f92036f693afcfee95eerikabele DEBUG(1, ("calloc failed.\n"));
6e89d4f6c259afc94f8806c74a33a8fe81392499sf return ENOMEM;
cd51960ffc0f49d7a9e702162ed49b3eb0909276dirkx }
17efa6b5344b7574597eec03f02ef28103e19265nd
17efa6b5344b7574597eec03f02ef28103e19265nd kerr = krb5_copy_principal(kr->ctx, kr->princ, &cred->client);
cd51960ffc0f49d7a9e702162ed49b3eb0909276dirkx if (kerr != 0) {
cd51960ffc0f49d7a9e702162ed49b3eb0909276dirkx DEBUG(1, ("krb5_copy_principal failed.\n"));
cd51960ffc0f49d7a9e702162ed49b3eb0909276dirkx goto done;
17efa6b5344b7574597eec03f02ef28103e19265nd }
17efa6b5344b7574597eec03f02ef28103e19265nd
cd51960ffc0f49d7a9e702162ed49b3eb0909276dirkx krb5_realm = krb5_princ_realm(kr->ctx, kr->princ);
cd51960ffc0f49d7a9e702162ed49b3eb0909276dirkx
cd51960ffc0f49d7a9e702162ed49b3eb0909276dirkx kerr = krb5_build_principal_ext(kr->ctx, &cred->server,
cd51960ffc0f49d7a9e702162ed49b3eb0909276dirkx krb5_realm->length, krb5_realm->data,
17efa6b5344b7574597eec03f02ef28103e19265nd KRB5_TGS_NAME_SIZE, KRB5_TGS_NAME,
cd51960ffc0f49d7a9e702162ed49b3eb0909276dirkx krb5_realm->length, krb5_realm->data, 0);
86bb9693d63dfc1be14519a5b444467e4b0cdaf8kawai if (kerr != 0) {
8e34905974b7a442a55adac3b3fdb196c389e807takashi DEBUG(1, ("krb5_build_principal_ext failed.\n"));
86bb9693d63dfc1be14519a5b444467e4b0cdaf8kawai goto done;
86bb9693d63dfc1be14519a5b444467e4b0cdaf8kawai }
86bb9693d63dfc1be14519a5b444467e4b0cdaf8kawai
86bb9693d63dfc1be14519a5b444467e4b0cdaf8kawaidone:
86bb9693d63dfc1be14519a5b444467e4b0cdaf8kawai if (kerr != 0) {
6e89d4f6c259afc94f8806c74a33a8fe81392499sf if (cred != NULL && cred->client != NULL) {
86bb9693d63dfc1be14519a5b444467e4b0cdaf8kawai krb5_free_principal(kr->ctx, cred->client);
8e34905974b7a442a55adac3b3fdb196c389e807takashi }
8e34905974b7a442a55adac3b3fdb196c389e807takashi
8e34905974b7a442a55adac3b3fdb196c389e807takashi free(cred);
86bb9693d63dfc1be14519a5b444467e4b0cdaf8kawai } else {
86bb9693d63dfc1be14519a5b444467e4b0cdaf8kawai *_cred = cred;
86bb9693d63dfc1be14519a5b444467e4b0cdaf8kawai }
8e34905974b7a442a55adac3b3fdb196c389e807takashi
8e34905974b7a442a55adac3b3fdb196c389e807takashi return kerr;
8e34905974b7a442a55adac3b3fdb196c389e807takashi}
86bb9693d63dfc1be14519a5b444467e4b0cdaf8kawai
86bb9693d63dfc1be14519a5b444467e4b0cdaf8kawaistatic krb5_error_code create_ccache_file(struct krb5_req *kr, krb5_creds *creds)
86bb9693d63dfc1be14519a5b444467e4b0cdaf8kawai{
86bb9693d63dfc1be14519a5b444467e4b0cdaf8kawai krb5_error_code kerr;
86bb9693d63dfc1be14519a5b444467e4b0cdaf8kawai krb5_ccache tmp_cc = NULL;
86bb9693d63dfc1be14519a5b444467e4b0cdaf8kawai char *cc_file_name;
17efa6b5344b7574597eec03f02ef28103e19265nd int fd = -1;
8e34905974b7a442a55adac3b3fdb196c389e807takashi size_t ccname_len;
17efa6b5344b7574597eec03f02ef28103e19265nd char *dummy;
17efa6b5344b7574597eec03f02ef28103e19265nd char *tmp_ccname;
8e34905974b7a442a55adac3b3fdb196c389e807takashi krb5_creds *l_cred;
b5468eddc0cb1691af19ddc70a6e205daf00a94ctrawick
b5468eddc0cb1691af19ddc70a6e205daf00a94ctrawick if (strncmp(kr->ccname, "FILE:", 5) == 0) {
6e89d4f6c259afc94f8806c74a33a8fe81392499sf cc_file_name = kr->ccname + 5;
b5468eddc0cb1691af19ddc70a6e205daf00a94ctrawick } else {
8e34905974b7a442a55adac3b3fdb196c389e807takashi cc_file_name = kr->ccname;
8e34905974b7a442a55adac3b3fdb196c389e807takashi }
b5468eddc0cb1691af19ddc70a6e205daf00a94ctrawick
b5468eddc0cb1691af19ddc70a6e205daf00a94ctrawick if (cc_file_name[0] != '/') {
b5468eddc0cb1691af19ddc70a6e205daf00a94ctrawick DEBUG(1, ("Ccache filename is not an absolute path.\n"));
8e34905974b7a442a55adac3b3fdb196c389e807takashi return EINVAL;
8e34905974b7a442a55adac3b3fdb196c389e807takashi }
b5468eddc0cb1691af19ddc70a6e205daf00a94ctrawick
b5468eddc0cb1691af19ddc70a6e205daf00a94ctrawick dummy = strrchr(cc_file_name, '/');
b5468eddc0cb1691af19ddc70a6e205daf00a94ctrawick tmp_ccname = talloc_strndup(kr, cc_file_name, (size_t) (dummy-cc_file_name));
b5468eddc0cb1691af19ddc70a6e205daf00a94ctrawick if (tmp_ccname == NULL) {
17efa6b5344b7574597eec03f02ef28103e19265nd DEBUG(1, ("talloc_strdup failed.\n"));
b5468eddc0cb1691af19ddc70a6e205daf00a94ctrawick return ENOMEM;
17efa6b5344b7574597eec03f02ef28103e19265nd }
8e34905974b7a442a55adac3b3fdb196c389e807takashi tmp_ccname = talloc_asprintf_append(tmp_ccname, "/.krb5cc_dummy_XXXXXX");
17efa6b5344b7574597eec03f02ef28103e19265nd
17efa6b5344b7574597eec03f02ef28103e19265nd fd = mkstemp(tmp_ccname);
17efa6b5344b7574597eec03f02ef28103e19265nd if (fd == -1) {
ef685e00a47967e27d89709461728a229d762172nd DEBUG(1, ("mkstemp failed [%d][%s].\n", errno, strerror(errno)));
222f0f03c2f9ee6343c18f80f0cb6e9aad21bc58slive return errno;
6e89d4f6c259afc94f8806c74a33a8fe81392499sf }
222f0f03c2f9ee6343c18f80f0cb6e9aad21bc58slive
17efa6b5344b7574597eec03f02ef28103e19265nd kerr = krb5_cc_resolve(kr->ctx, tmp_ccname, &tmp_cc);
17efa6b5344b7574597eec03f02ef28103e19265nd if (kerr != 0) {
222f0f03c2f9ee6343c18f80f0cb6e9aad21bc58slive KRB5_DEBUG(1, kerr);
222f0f03c2f9ee6343c18f80f0cb6e9aad21bc58slive goto done;
222f0f03c2f9ee6343c18f80f0cb6e9aad21bc58slive }
17efa6b5344b7574597eec03f02ef28103e19265nd
17efa6b5344b7574597eec03f02ef28103e19265nd kerr = krb5_cc_initialize(kr->ctx, tmp_cc, kr->princ);
222f0f03c2f9ee6343c18f80f0cb6e9aad21bc58slive if (kerr != 0) {
222f0f03c2f9ee6343c18f80f0cb6e9aad21bc58slive KRB5_DEBUG(1, kerr);
222f0f03c2f9ee6343c18f80f0cb6e9aad21bc58slive goto done;
222f0f03c2f9ee6343c18f80f0cb6e9aad21bc58slive }
17efa6b5344b7574597eec03f02ef28103e19265nd if (fd != -1) {
1f2a7403f1389cbf2da0a53a2b2fb425dea75506erikabele close(fd);
94abf1cc80072ea31506946ac6595631ca6d2c14rbowen fd = -1;
94abf1cc80072ea31506946ac6595631ca6d2c14rbowen }
94abf1cc80072ea31506946ac6595631ca6d2c14rbowen
94abf1cc80072ea31506946ac6595631ca6d2c14rbowen if (creds == NULL) {
94abf1cc80072ea31506946ac6595631ca6d2c14rbowen kerr = create_empty_cred(kr, &l_cred);
94abf1cc80072ea31506946ac6595631ca6d2c14rbowen if (kerr != 0) {
94abf1cc80072ea31506946ac6595631ca6d2c14rbowen KRB5_DEBUG(1, kerr);
94abf1cc80072ea31506946ac6595631ca6d2c14rbowen goto done;
94abf1cc80072ea31506946ac6595631ca6d2c14rbowen }
94abf1cc80072ea31506946ac6595631ca6d2c14rbowen } else {
94abf1cc80072ea31506946ac6595631ca6d2c14rbowen l_cred = creds;
94abf1cc80072ea31506946ac6595631ca6d2c14rbowen }
94abf1cc80072ea31506946ac6595631ca6d2c14rbowen
94abf1cc80072ea31506946ac6595631ca6d2c14rbowen kerr = krb5_cc_store_cred(kr->ctx, tmp_cc, l_cred);
94abf1cc80072ea31506946ac6595631ca6d2c14rbowen if (kerr != 0) {
94abf1cc80072ea31506946ac6595631ca6d2c14rbowen KRB5_DEBUG(1, kerr);
94abf1cc80072ea31506946ac6595631ca6d2c14rbowen goto done;
94abf1cc80072ea31506946ac6595631ca6d2c14rbowen }
94abf1cc80072ea31506946ac6595631ca6d2c14rbowen
94abf1cc80072ea31506946ac6595631ca6d2c14rbowen kerr = krb5_cc_close(kr->ctx, tmp_cc);
94abf1cc80072ea31506946ac6595631ca6d2c14rbowen if (kerr != 0) {
94abf1cc80072ea31506946ac6595631ca6d2c14rbowen KRB5_DEBUG(1, kerr);
94abf1cc80072ea31506946ac6595631ca6d2c14rbowen goto done;
94abf1cc80072ea31506946ac6595631ca6d2c14rbowen }
17efa6b5344b7574597eec03f02ef28103e19265nd tmp_cc = NULL;
8e34905974b7a442a55adac3b3fdb196c389e807takashi
17efa6b5344b7574597eec03f02ef28103e19265nd ccname_len = strlen(cc_file_name);
17efa6b5344b7574597eec03f02ef28103e19265nd if (ccname_len >= 6 && strcmp(cc_file_name + (ccname_len-6), "XXXXXX")==0 ) {
1f666f93c91dbb492dc7706573b369cd03b84265poirier fd = mkstemp(cc_file_name);
ef685e00a47967e27d89709461728a229d762172nd if (fd == -1) {
1f2a7403f1389cbf2da0a53a2b2fb425dea75506erikabele DEBUG(1, ("mkstemp failed [%d][%s].\n", errno, strerror(errno)));
6e89d4f6c259afc94f8806c74a33a8fe81392499sf kerr = errno;
1f2a7403f1389cbf2da0a53a2b2fb425dea75506erikabele goto done;
1f666f93c91dbb492dc7706573b369cd03b84265poirier }
17efa6b5344b7574597eec03f02ef28103e19265nd }
1f2a7403f1389cbf2da0a53a2b2fb425dea75506erikabele
1f2a7403f1389cbf2da0a53a2b2fb425dea75506erikabele kerr = rename(tmp_ccname, cc_file_name);
1f2a7403f1389cbf2da0a53a2b2fb425dea75506erikabele if (kerr == -1) {
1f666f93c91dbb492dc7706573b369cd03b84265poirier DEBUG(1, ("rename failed [%d][%s].\n", errno, strerror(errno)));
1f666f93c91dbb492dc7706573b369cd03b84265poirier }
1f2a7403f1389cbf2da0a53a2b2fb425dea75506erikabele
1f2a7403f1389cbf2da0a53a2b2fb425dea75506erikabeledone:
1f2a7403f1389cbf2da0a53a2b2fb425dea75506erikabele if (fd != -1) {
1f2a7403f1389cbf2da0a53a2b2fb425dea75506erikabele close(fd);
17efa6b5344b7574597eec03f02ef28103e19265nd fd = -1;
81622596373177e079337e956f7a5800895443b3erikabele }
81622596373177e079337e956f7a5800895443b3erikabele if (kerr != 0 && tmp_cc != NULL) {
8e34905974b7a442a55adac3b3fdb196c389e807takashi krb5_cc_destroy(kr->ctx, tmp_cc);
81622596373177e079337e956f7a5800895443b3erikabele }
ef685e00a47967e27d89709461728a229d762172nd return kerr;
ef685e00a47967e27d89709461728a229d762172nd}
ef685e00a47967e27d89709461728a229d762172nd
81622596373177e079337e956f7a5800895443b3erikabelestatic errno_t pack_response_packet(struct response *resp, int status,
6e89d4f6c259afc94f8806c74a33a8fe81392499sf struct pam_data *pd)
81622596373177e079337e956f7a5800895443b3erikabele{
81622596373177e079337e956f7a5800895443b3erikabele size_t size = 0;
81622596373177e079337e956f7a5800895443b3erikabele size_t p = 0;
81622596373177e079337e956f7a5800895443b3erikabele struct response_data *pdr;
81622596373177e079337e956f7a5800895443b3erikabele
81622596373177e079337e956f7a5800895443b3erikabele /* A buffer with the following structure must be created:
81622596373177e079337e956f7a5800895443b3erikabele * int32_t status of the request (required)
81622596373177e079337e956f7a5800895443b3erikabele * message (zero or more)
8e48ffd77ffe2edea5cae0dc77ae44df5e904fdfrbowen *
81622596373177e079337e956f7a5800895443b3erikabele * A message consists of:
81622596373177e079337e956f7a5800895443b3erikabele * int32_t type of the message
81622596373177e079337e956f7a5800895443b3erikabele * int32_t length of the following data
81622596373177e079337e956f7a5800895443b3erikabele * uint8_t[len] data
81622596373177e079337e956f7a5800895443b3erikabele */
81622596373177e079337e956f7a5800895443b3erikabele
81622596373177e079337e956f7a5800895443b3erikabele size = sizeof(int32_t);
ef685e00a47967e27d89709461728a229d762172nd
e554dd2dae4ba2c32dbd05fc0d4e0a42ef4ba902rbowen pdr = pd->resp_list;
e554dd2dae4ba2c32dbd05fc0d4e0a42ef4ba902rbowen while (pdr != NULL) {
e554dd2dae4ba2c32dbd05fc0d4e0a42ef4ba902rbowen size += 2*sizeof(int32_t) + pdr->len;
e554dd2dae4ba2c32dbd05fc0d4e0a42ef4ba902rbowen pdr = pdr->next;
e554dd2dae4ba2c32dbd05fc0d4e0a42ef4ba902rbowen }
e554dd2dae4ba2c32dbd05fc0d4e0a42ef4ba902rbowen
6e89d4f6c259afc94f8806c74a33a8fe81392499sf
e554dd2dae4ba2c32dbd05fc0d4e0a42ef4ba902rbowen resp->buf = talloc_array(resp, uint8_t, size);
e554dd2dae4ba2c32dbd05fc0d4e0a42ef4ba902rbowen if (!resp->buf) {
e554dd2dae4ba2c32dbd05fc0d4e0a42ef4ba902rbowen DEBUG(1, ("Insufficient memory to create message.\n"));
e554dd2dae4ba2c32dbd05fc0d4e0a42ef4ba902rbowen return ENOMEM;
e554dd2dae4ba2c32dbd05fc0d4e0a42ef4ba902rbowen }
e554dd2dae4ba2c32dbd05fc0d4e0a42ef4ba902rbowen
e554dd2dae4ba2c32dbd05fc0d4e0a42ef4ba902rbowen SAFEALIGN_SET_INT32(&resp->buf[p], status, &p);
e554dd2dae4ba2c32dbd05fc0d4e0a42ef4ba902rbowen
e554dd2dae4ba2c32dbd05fc0d4e0a42ef4ba902rbowen pdr = pd->resp_list;
e554dd2dae4ba2c32dbd05fc0d4e0a42ef4ba902rbowen while(pdr != NULL) {
e554dd2dae4ba2c32dbd05fc0d4e0a42ef4ba902rbowen SAFEALIGN_SET_INT32(&resp->buf[p], pdr->type, &p);
e554dd2dae4ba2c32dbd05fc0d4e0a42ef4ba902rbowen SAFEALIGN_SET_INT32(&resp->buf[p], pdr->len, &p);
e554dd2dae4ba2c32dbd05fc0d4e0a42ef4ba902rbowen safealign_memcpy(&resp->buf[p], pdr->data, pdr->len, &p);
e554dd2dae4ba2c32dbd05fc0d4e0a42ef4ba902rbowen
e554dd2dae4ba2c32dbd05fc0d4e0a42ef4ba902rbowen pdr = pdr->next;
e554dd2dae4ba2c32dbd05fc0d4e0a42ef4ba902rbowen }
f0eae6f6191f5730fa8db049f7391e93b4ff41b9erikabele
8e34905974b7a442a55adac3b3fdb196c389e807takashi
f0eae6f6191f5730fa8db049f7391e93b4ff41b9erikabele resp->size = p;
f0eae6f6191f5730fa8db049f7391e93b4ff41b9erikabele
f0eae6f6191f5730fa8db049f7391e93b4ff41b9erikabele return EOK;
f0eae6f6191f5730fa8db049f7391e93b4ff41b9erikabele}
f0eae6f6191f5730fa8db049f7391e93b4ff41b9erikabele
6e89d4f6c259afc94f8806c74a33a8fe81392499sfstatic struct response *prepare_response_message(struct krb5_req *kr,
f0eae6f6191f5730fa8db049f7391e93b4ff41b9erikabele krb5_error_code kerr,
f0eae6f6191f5730fa8db049f7391e93b4ff41b9erikabele int pam_status)
f0eae6f6191f5730fa8db049f7391e93b4ff41b9erikabele{
f0eae6f6191f5730fa8db049f7391e93b4ff41b9erikabele char *msg = NULL;
f0eae6f6191f5730fa8db049f7391e93b4ff41b9erikabele const char *krb5_msg = NULL;
f0eae6f6191f5730fa8db049f7391e93b4ff41b9erikabele int ret;
f0eae6f6191f5730fa8db049f7391e93b4ff41b9erikabele struct response *resp;
f0eae6f6191f5730fa8db049f7391e93b4ff41b9erikabele
f0eae6f6191f5730fa8db049f7391e93b4ff41b9erikabele resp = talloc_zero(kr, struct response);
f0eae6f6191f5730fa8db049f7391e93b4ff41b9erikabele if (resp == NULL) {
f0eae6f6191f5730fa8db049f7391e93b4ff41b9erikabele DEBUG(1, ("Initializing response failed.\n"));
f0eae6f6191f5730fa8db049f7391e93b4ff41b9erikabele return NULL;
f0eae6f6191f5730fa8db049f7391e93b4ff41b9erikabele }
4a67c5c0053e1c1c2202122e46a42987f6fd28dfyoshiki
f4166cb2bf5e48c1b0f18b0d1f6757fce82230a8nd if (kerr == 0) {
8e34905974b7a442a55adac3b3fdb196c389e807takashi if(kr->pd->cmd == SSS_PAM_CHAUTHTOK_PRELIM) {
f4166cb2bf5e48c1b0f18b0d1f6757fce82230a8nd pam_status = PAM_SUCCESS;
f4166cb2bf5e48c1b0f18b0d1f6757fce82230a8nd ret = EOK;
8e34905974b7a442a55adac3b3fdb196c389e807takashi } else {
f4166cb2bf5e48c1b0f18b0d1f6757fce82230a8nd if (kr->ccname == NULL) {
f4166cb2bf5e48c1b0f18b0d1f6757fce82230a8nd DEBUG(1, ("Error obtaining ccname.\n"));
6e89d4f6c259afc94f8806c74a33a8fe81392499sf return NULL;
f4166cb2bf5e48c1b0f18b0d1f6757fce82230a8nd }
8e34905974b7a442a55adac3b3fdb196c389e807takashi
8e34905974b7a442a55adac3b3fdb196c389e807takashi msg = talloc_asprintf(kr, "%s=%s",CCACHE_ENV_NAME, kr->ccname);
f4166cb2bf5e48c1b0f18b0d1f6757fce82230a8nd if (msg == NULL) {
f4166cb2bf5e48c1b0f18b0d1f6757fce82230a8nd DEBUG(1, ("talloc_asprintf failed.\n"));
f4166cb2bf5e48c1b0f18b0d1f6757fce82230a8nd return NULL;
8e34905974b7a442a55adac3b3fdb196c389e807takashi }
8e34905974b7a442a55adac3b3fdb196c389e807takashi
f4166cb2bf5e48c1b0f18b0d1f6757fce82230a8nd pam_status = PAM_SUCCESS;
f4166cb2bf5e48c1b0f18b0d1f6757fce82230a8nd ret = pam_add_response(kr->pd, SSS_PAM_ENV_ITEM, strlen(msg) + 1,
f4166cb2bf5e48c1b0f18b0d1f6757fce82230a8nd (uint8_t *) msg);
f4166cb2bf5e48c1b0f18b0d1f6757fce82230a8nd talloc_zfree(msg);
f4166cb2bf5e48c1b0f18b0d1f6757fce82230a8nd }
f4166cb2bf5e48c1b0f18b0d1f6757fce82230a8nd } else {
17efa6b5344b7574597eec03f02ef28103e19265nd krb5_msg = sss_krb5_get_error_message(krb5_error_ctx, kerr);
8e34905974b7a442a55adac3b3fdb196c389e807takashi if (krb5_msg == NULL) {
17efa6b5344b7574597eec03f02ef28103e19265nd DEBUG(1, ("sss_krb5_get_error_message failed.\n"));
17efa6b5344b7574597eec03f02ef28103e19265nd return NULL;
17efa6b5344b7574597eec03f02ef28103e19265nd }
4a67c5c0053e1c1c2202122e46a42987f6fd28dfyoshiki
4a67c5c0053e1c1c2202122e46a42987f6fd28dfyoshiki ret = pam_add_response(kr->pd, SSS_PAM_SYSTEM_INFO,
6e89d4f6c259afc94f8806c74a33a8fe81392499sf strlen(krb5_msg) + 1,
4a67c5c0053e1c1c2202122e46a42987f6fd28dfyoshiki (const uint8_t *) krb5_msg);
17efa6b5344b7574597eec03f02ef28103e19265nd sss_krb5_free_error_message(krb5_error_ctx, krb5_msg);
17efa6b5344b7574597eec03f02ef28103e19265nd }
17efa6b5344b7574597eec03f02ef28103e19265nd if (ret != EOK) {
4a67c5c0053e1c1c2202122e46a42987f6fd28dfyoshiki DEBUG(1, ("pam_add_response failed.\n"));
4a67c5c0053e1c1c2202122e46a42987f6fd28dfyoshiki }
4a67c5c0053e1c1c2202122e46a42987f6fd28dfyoshiki
17efa6b5344b7574597eec03f02ef28103e19265nd ret = pack_response_packet(resp, pam_status, kr->pd);
17efa6b5344b7574597eec03f02ef28103e19265nd if (ret != EOK) {
17efa6b5344b7574597eec03f02ef28103e19265nd DEBUG(1, ("pack_response_packet failed.\n"));
4a67c5c0053e1c1c2202122e46a42987f6fd28dfyoshiki return NULL;
4a67c5c0053e1c1c2202122e46a42987f6fd28dfyoshiki }
4a67c5c0053e1c1c2202122e46a42987f6fd28dfyoshiki
4a67c5c0053e1c1c2202122e46a42987f6fd28dfyoshiki return resp;
17efa6b5344b7574597eec03f02ef28103e19265nd}
e9e8e471353eaa5576e1e96530968d02f208e39fnd
e9e8e471353eaa5576e1e96530968d02f208e39fndstatic errno_t sendresponse(int fd, krb5_error_code kerr, int pam_status,
8e34905974b7a442a55adac3b3fdb196c389e807takashi struct krb5_req *kr)
e9e8e471353eaa5576e1e96530968d02f208e39fnd{
e9e8e471353eaa5576e1e96530968d02f208e39fnd struct response *resp;
1e1be8a0871405df3c1ec4d6d33aab71996ad0c9nilgun size_t written;
e9e8e471353eaa5576e1e96530968d02f208e39fnd int ret;
e9e8e471353eaa5576e1e96530968d02f208e39fnd
6e89d4f6c259afc94f8806c74a33a8fe81392499sf resp = prepare_response_message(kr, kerr, pam_status);
e9e8e471353eaa5576e1e96530968d02f208e39fnd if (resp == NULL) {
1e1be8a0871405df3c1ec4d6d33aab71996ad0c9nilgun DEBUG(1, ("prepare_response_message failed.\n"));
1e1be8a0871405df3c1ec4d6d33aab71996ad0c9nilgun return ENOMEM;
e9e8e471353eaa5576e1e96530968d02f208e39fnd }
e9e8e471353eaa5576e1e96530968d02f208e39fnd
e9e8e471353eaa5576e1e96530968d02f208e39fnd written = 0;
1e1be8a0871405df3c1ec4d6d33aab71996ad0c9nilgun while (written < resp->size) {
1e1be8a0871405df3c1ec4d6d33aab71996ad0c9nilgun ret = write(fd, resp->buf + written, resp->size - written);
1e1be8a0871405df3c1ec4d6d33aab71996ad0c9nilgun if (ret == -1) {
e9e8e471353eaa5576e1e96530968d02f208e39fnd if (errno == EAGAIN || errno == EINTR) {
e9e8e471353eaa5576e1e96530968d02f208e39fnd continue;
e9e8e471353eaa5576e1e96530968d02f208e39fnd }
e9e8e471353eaa5576e1e96530968d02f208e39fnd ret = errno;
e9e8e471353eaa5576e1e96530968d02f208e39fnd DEBUG(1, ("write failed [%d][%s].\n", ret, strerror(ret)));
return ret;
}
written += ret;
}
return EOK;
}
static krb5_error_code validate_tgt(struct krb5_req *kr)
{
krb5_error_code kerr;
krb5_error_code kt_err;
char *principal;
krb5_keytab keytab;
krb5_kt_cursor cursor;
krb5_keytab_entry entry;
krb5_verify_init_creds_opt opt;
memset(&keytab, 0, sizeof(keytab));
kerr = krb5_kt_resolve(kr->ctx, kr->keytab, &keytab);
if (kerr != 0) {
DEBUG(1, ("error resolving keytab [%s], not verifying TGT.\n",
kr->keytab));
return kerr;
}
memset(&cursor, 0, sizeof(cursor));
kerr = krb5_kt_start_seq_get(kr->ctx, keytab, &cursor);
if (kerr != 0) {
DEBUG(1, ("error reading keytab [%s], not verifying TGT.\n",
kr->keytab));
return kerr;
}
/* We look for the first entry from our realm or take the last one */
memset(&entry, 0, sizeof(entry));
while ((kt_err = krb5_kt_next_entry(kr->ctx, keytab, &entry, &cursor)) == 0) {
if (krb5_realm_compare(kr->ctx, entry.principal, kr->princ)) {
DEBUG(9, ("Found keytab entry with the realm of the credential.\n"));
break;
}
kerr = krb5_free_keytab_entry_contents(kr->ctx, &entry);
if (kerr != 0) {
DEBUG(1, ("Failed to free keytab entry.\n"));
}
memset(&entry, 0, sizeof(entry));
}
/* 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. */
kerr = krb5_kt_end_seq_get(kr->ctx, keytab, &cursor);
if (kerr != 0) {
DEBUG(1, ("krb5_kt_end_seq_get failed, not verifying TGT.\n"));
goto done;
}
/* check if we got any errors from krb5_kt_next_entry */
if (kt_err != 0 && kt_err != KRB5_KT_END) {
DEBUG(1, ("error reading keytab [%s], not verifying TGT.\n",
kr->keytab));
goto done;
}
/* Get the principal to which the key belongs, for logging purposes. */
principal = NULL;
kerr = krb5_unparse_name(kr->ctx, entry.principal, &principal);
if (kerr != 0) {
DEBUG(1, ("internal error parsing principal name, "
"not verifying TGT.\n"));
goto done;
}
krb5_verify_init_creds_opt_init(&opt);
kerr = krb5_verify_init_creds(kr->ctx, kr->creds, entry.principal, keytab,
NULL, &opt);
if (kerr == 0) {
DEBUG(5, ("TGT verified using key for [%s].\n", principal));
} else {
DEBUG(1 ,("TGT failed verification using key for [%s].\n", principal));
}
done:
if (krb5_kt_close(kr->ctx, keytab) != 0) {
DEBUG(1, ("krb5_kt_close failed"));
}
if (krb5_free_keytab_entry_contents(kr->ctx, &entry) != 0) {
DEBUG(1, ("Failed to free keytab entry.\n"));
}
if (principal != NULL) {
sss_krb5_free_unparsed_name(kr->ctx, principal);
}
return kerr;
}
static krb5_error_code get_and_save_tgt(struct krb5_req *kr,
char *password)
{
krb5_error_code kerr = 0;
int ret;
kerr = krb5_get_init_creds_password(kr->ctx, kr->creds, kr->princ,
password, sss_krb5_prompter, kr, 0,
NULL, kr->options);
if (kerr != 0) {
KRB5_DEBUG(1, kerr);
return kerr;
}
if (kr->validate) {
kerr = validate_tgt(kr);
if (kerr != 0) {
KRB5_DEBUG(1, kerr);
return kerr;
}
/* We drop root privileges which were needed to read the keytab file
* for the validation validation of the credentials here to run the
* ccache I/O operations with user privileges. */
ret = become_user(kr->uid, kr->gid);
if (ret != EOK) {
DEBUG(1, ("become_user failed.\n"));
return ret;
}
} else {
DEBUG(9, ("TGT validation is disabled.\n"));
}
kerr = create_ccache_file(kr, kr->creds);
if (kerr != 0) {
KRB5_DEBUG(1, kerr);
goto done;
}
kerr = 0;
done:
krb5_free_cred_contents(kr->ctx, kr->creds);
return kerr;
}
static errno_t changepw_child(int fd, struct krb5_req *kr)
{
int ret;
krb5_error_code kerr = 0;
char *pass_str = NULL;
char *newpass_str = NULL;
int pam_status = PAM_SYSTEM_ERR;
int result_code = -1;
krb5_data result_code_string;
krb5_data result_string;
char *user_error_message = NULL;
size_t user_resp_len;
uint8_t *user_resp;
krb5_prompter_fct prompter = sss_krb5_prompter;
pass_str = talloc_strndup(kr, (const char *) kr->pd->authtok,
kr->pd->authtok_size);
if (pass_str == NULL) {
DEBUG(1, ("talloc_strndup failed.\n"));
kerr = KRB5KRB_ERR_GENERIC;
goto sendresponse;
}
if (kr->pd->cmd == SSS_PAM_CHAUTHTOK_PRELIM) {
/* We do not need a password expiration warning here. */
prompter = NULL;
}
kerr = krb5_get_init_creds_password(kr->ctx, kr->creds, kr->princ,
pass_str, prompter, kr, 0,
kr->krb5_ctx->changepw_principle,
kr->options);
if (kerr != 0) {
KRB5_DEBUG(1, kerr);
if (kerr == KRB5_KDC_UNREACH) {
pam_status = PAM_AUTHINFO_UNAVAIL;
}
goto sendresponse;
}
memset(pass_str, 0, kr->pd->authtok_size);
talloc_zfree(pass_str);
memset(kr->pd->authtok, 0, kr->pd->authtok_size);
if (kr->pd->cmd == SSS_PAM_CHAUTHTOK_PRELIM) {
DEBUG(9, ("Initial authentication for change password operation "
"successfull.\n"));
krb5_free_cred_contents(kr->ctx, kr->creds);
pam_status = PAM_SUCCESS;
goto sendresponse;
}
newpass_str = talloc_strndup(kr, (const char *) kr->pd->newauthtok,
kr->pd->newauthtok_size);
if (newpass_str == NULL) {
DEBUG(1, ("talloc_strndup failed.\n"));
kerr = KRB5KRB_ERR_GENERIC;
goto sendresponse;
}
memset(&result_code_string, 0, sizeof(krb5_data));
memset(&result_string, 0, sizeof(krb5_data));
kerr = krb5_change_password(kr->ctx, kr->creds, newpass_str, &result_code,
&result_code_string, &result_string);
if (kerr == KRB5_KDC_UNREACH) {
pam_status = PAM_AUTHTOK_LOCK_BUSY;
goto sendresponse;
}
if (kerr != 0 || result_code != 0) {
if (kerr != 0) {
KRB5_DEBUG(1, kerr);
} else {
kerr = KRB5KRB_ERR_GENERIC;
}
if (result_code_string.length > 0) {
DEBUG(1, ("krb5_change_password failed [%d][%.*s].\n", result_code,
result_code_string.length, result_code_string.data));
user_error_message = talloc_strndup(kr->pd, result_code_string.data,
result_code_string.length);
if (user_error_message == NULL) {
DEBUG(1, ("talloc_strndup failed.\n"));
}
}
if (result_string.length > 0) {
DEBUG(1, ("krb5_change_password failed [%d][%.*s].\n", result_code,
result_string.length, result_string.data));
talloc_free(user_error_message);
user_error_message = talloc_strndup(kr->pd, result_string.data,
result_string.length);
if (user_error_message == NULL) {
DEBUG(1, ("talloc_strndup failed.\n"));
}
}
if (user_error_message != NULL) {
ret = pack_user_info_chpass_error(kr->pd, user_error_message,
&user_resp_len, &user_resp);
if (ret != EOK) {
DEBUG(1, ("pack_user_info_chpass_error failed.\n"));
} else {
ret = pam_add_response(kr->pd, SSS_PAM_USER_INFO, user_resp_len,
user_resp);
if (ret != EOK) {
DEBUG(1, ("pack_response_packet failed.\n"));
}
}
}
pam_status = PAM_AUTHTOK_ERR;
goto sendresponse;
}
krb5_free_cred_contents(kr->ctx, kr->creds);
kerr = get_and_save_tgt(kr, newpass_str);
memset(newpass_str, 0, kr->pd->newauthtok_size);
talloc_zfree(newpass_str);
memset(kr->pd->newauthtok, 0, kr->pd->newauthtok_size);
if (kerr != 0) {
KRB5_DEBUG(1, kerr);
if (kerr == KRB5_KDC_UNREACH) {
pam_status = PAM_AUTHINFO_UNAVAIL;
}
}
sendresponse:
ret = sendresponse(fd, kerr, pam_status, kr);
if (ret != EOK) {
DEBUG(1, ("sendresponse failed.\n"));
}
return ret;
}
static errno_t tgt_req_child(int fd, struct krb5_req *kr)
{
int ret;
krb5_error_code kerr = 0;
char *pass_str = NULL;
int pam_status = PAM_SYSTEM_ERR;
pass_str = talloc_strndup(kr, (const char *) kr->pd->authtok,
kr->pd->authtok_size);
if (pass_str == NULL) {
DEBUG(1, ("talloc_strndup failed.\n"));
kerr = KRB5KRB_ERR_GENERIC;
goto sendresponse;
}
kerr = get_and_save_tgt(kr, pass_str);
/* 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) {
kerr = krb5_get_init_creds_password(kr->ctx, kr->creds, kr->princ,
pass_str, sss_krb5_prompter, kr, 0,
kr->krb5_ctx->changepw_principle,
kr->options);
krb5_free_cred_contents(kr->ctx, kr->creds);
if (kerr == 0) {
kerr = KRB5KDC_ERR_KEY_EXP;
}
}
memset(pass_str, 0, kr->pd->authtok_size);
talloc_zfree(pass_str);
memset(kr->pd->authtok, 0, kr->pd->authtok_size);
if (kerr != 0) {
KRB5_DEBUG(1, kerr);
switch (kerr) {
case KRB5_KDC_UNREACH:
pam_status = PAM_AUTHINFO_UNAVAIL;
break;
case KRB5KDC_ERR_KEY_EXP:
pam_status = PAM_NEW_AUTHTOK_REQD;
break;
case KRB5KDC_ERR_PREAUTH_FAILED:
pam_status = PAM_CRED_ERR;
break;
default:
pam_status = PAM_SYSTEM_ERR;
}
}
sendresponse:
ret = sendresponse(fd, kerr, pam_status, kr);
if (ret != EOK) {
DEBUG(1, ("sendresponse failed.\n"));
}
return ret;
}
static errno_t create_empty_ccache(int fd, struct krb5_req *kr)
{
int ret;
int pam_status = PAM_SUCCESS;
ret = create_ccache_file(kr, NULL);
if (ret != 0) {
KRB5_DEBUG(1, ret);
pam_status = PAM_SYSTEM_ERR;
}
ret = sendresponse(fd, ret, pam_status, kr);
if (ret != EOK) {
DEBUG(1, ("sendresponse failed.\n"));
}
return ret;
}
static errno_t unpack_buffer(uint8_t *buf, size_t size, struct pam_data *pd,
struct krb5_req *kr, uint32_t *offline)
{
size_t p = 0;
uint32_t len;
uint32_t validate;
SAFEALIGN_COPY_UINT32_CHECK(&pd->cmd, buf + p, size, &p);
SAFEALIGN_COPY_UINT32_CHECK(&kr->uid, buf + p, size, &p);
SAFEALIGN_COPY_UINT32_CHECK(&kr->gid, buf + p, size, &p);
SAFEALIGN_COPY_UINT32_CHECK(&validate, buf + p, size, &p);
kr->validate = (validate == 0) ? false : true;
SAFEALIGN_COPY_UINT32_CHECK(offline, buf + p, size, &p);
SAFEALIGN_COPY_UINT32_CHECK(&len, buf + p, size, &p);
if ((p + len ) > size) return EINVAL;
kr->upn = talloc_strndup(pd, (char *)(buf + p), len);
if (kr->upn == NULL) return ENOMEM;
p += len;
SAFEALIGN_COPY_UINT32_CHECK(&len, buf + p, size, &p);
if ((p + len ) > size) return EINVAL;
kr->ccname = talloc_strndup(pd, (char *)(buf + p), len);
if (kr->ccname == NULL) return ENOMEM;
p += len;
SAFEALIGN_COPY_UINT32_CHECK(&len, buf + p, size, &p);
if ((p + len ) > size) return EINVAL;
kr->keytab = talloc_strndup(pd, (char *)(buf + p), len);
if (kr->keytab == NULL) return ENOMEM;
p += len;
SAFEALIGN_COPY_UINT32_CHECK(&len, buf + p, size, &p);
if ((p + len) > size) return EINVAL;
pd->authtok = (uint8_t *)talloc_strndup(pd, (char *)(buf + p), len);
if (pd->authtok == NULL) return ENOMEM;
pd->authtok_size = len + 1;
p += len;
if (pd->cmd == SSS_PAM_CHAUTHTOK) {
SAFEALIGN_COPY_UINT32_CHECK(&len, buf + p, size, &p);
if ((p + len) > size) return EINVAL;
pd->newauthtok = (uint8_t *)talloc_strndup(pd, (char *)(buf + p), len);
if (pd->newauthtok == NULL) return ENOMEM;
pd->newauthtok_size = len + 1;
p += len;
} else {
pd->newauthtok = NULL;
pd->newauthtok_size = 0;
}
return EOK;
}
static int krb5_cleanup(void *ptr)
{
struct krb5_req *kr = talloc_get_type(ptr, struct krb5_req);
if (kr == NULL) return EOK;
if (kr->options != NULL) {
sss_krb5_get_init_creds_opt_free(kr->ctx, kr->options);
}
if (kr->creds != NULL) {
krb5_free_cred_contents(kr->ctx, kr->creds);
krb5_free_creds(kr->ctx, kr->creds);
}
if (kr->name != NULL)
sss_krb5_free_unparsed_name(kr->ctx, kr->name);
if (kr->princ != NULL)
krb5_free_principal(kr->ctx, kr->princ);
if (kr->ctx != NULL)
krb5_free_context(kr->ctx);
if (kr->krb5_ctx != NULL) {
memset(kr->krb5_ctx, 0, sizeof(struct krb5_child_ctx));
}
memset(kr, 0, sizeof(struct krb5_req));
return EOK;
}
static int krb5_setup(struct krb5_req *kr, uint32_t offline)
{
krb5_error_code kerr = 0;
kr->krb5_ctx = talloc_zero(kr, struct krb5_child_ctx);
if (kr->krb5_ctx == NULL) {
DEBUG(1, ("talloc failed.\n"));
kerr = ENOMEM;
goto failed;
}
kr->krb5_ctx->changepw_principle = getenv(SSSD_KRB5_CHANGEPW_PRINCIPLE);
if (kr->krb5_ctx->changepw_principle == NULL) {
DEBUG(1, ("Cannot read [%s] from environment.\n",
SSSD_KRB5_CHANGEPW_PRINCIPLE));
if (kr->pd->cmd == SSS_PAM_CHAUTHTOK) {
goto failed;
}
}
kr->krb5_ctx->realm = getenv(SSSD_KRB5_REALM);
if (kr->krb5_ctx->realm == NULL) {
DEBUG(2, ("Cannot read [%s] from environment.\n", SSSD_KRB5_REALM));
}
switch(kr->pd->cmd) {
case SSS_PAM_AUTHENTICATE:
/* If we are offline, we need to create an empty ccache file */
if (offline) {
kr->child_req = create_empty_ccache;
} else {
kr->child_req = tgt_req_child;
}
break;
case SSS_PAM_CHAUTHTOK:
case SSS_PAM_CHAUTHTOK_PRELIM:
kr->child_req = changepw_child;
break;
default:
DEBUG(1, ("PAM command [%d] not supported.\n", kr->pd->cmd));
kerr = EINVAL;
goto failed;
}
kerr = krb5_init_context(&kr->ctx);
if (kerr != 0) {
KRB5_DEBUG(1, kerr);
goto failed;
}
kerr = krb5_parse_name(kr->ctx, kr->upn, &kr->princ);
if (kerr != 0) {
KRB5_DEBUG(1, kerr);
goto failed;
}
kerr = krb5_unparse_name(kr->ctx, kr->princ, &kr->name);
if (kerr != 0) {
KRB5_DEBUG(1, kerr);
goto failed;
}
kr->creds = calloc(1, sizeof(krb5_creds));
if (kr->creds == NULL) {
DEBUG(1, ("talloc_zero failed.\n"));
kerr = ENOMEM;
goto failed;
}
kerr = sss_krb5_get_init_creds_opt_alloc(kr->ctx, &kr->options);
if (kerr != 0) {
KRB5_DEBUG(1, kerr);
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. */
krb5_get_init_creds_opt_set_change_password_prompt(kr->options, 0);
if (kerr != 0) {
KRB5_DEBUG(1, kerr);
goto failed;
}
/* TODO: set options, e.g.
* krb5_get_init_creds_opt_set_tkt_life
* krb5_get_init_creds_opt_set_renew_life
* 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;
failed:
return kerr;
}
int main(int argc, const char *argv[])
{
uint8_t *buf = NULL;
int ret;
ssize_t len = 0;
struct pam_data *pd = NULL;
struct krb5_req *kr = NULL;
uint32_t offline;
int opt;
poptContext pc;
int debug_fd = -1;
struct poptOption long_options[] = {
POPT_AUTOHELP
{"debug-level", 'd', POPT_ARG_INT, &debug_level, 0,
_("Debug level"), NULL},
{"debug-timestamps", 0, POPT_ARG_INT, &debug_timestamps, 0,
_("Add debug timestamps"), NULL},
{"debug-fd", 0, POPT_ARG_INT, &debug_fd, 0,
_("An open file descriptor for the debug logs"), NULL},
POPT_TABLEEND
};
pc = poptGetContext(argv[0], argc, argv, long_options, 0);
while((opt = poptGetNextOpt(pc)) != -1) {
switch(opt) {
default:
fprintf(stderr, "\nInvalid option %s: %s\n\n",
poptBadOption(pc, 0), poptStrerror(opt));
poptPrintUsage(pc, stderr, 0);
_exit(-1);
}
}
poptFreeContext(pc);
DEBUG(7, ("krb5_child started.\n"));
pd = talloc_zero(NULL, struct pam_data);
if (pd == NULL) {
DEBUG(1, ("malloc failed.\n"));
_exit(-1);
}
debug_prg_name = talloc_asprintf(pd, "[sssd[krb5_child[%d]]]", getpid());
if (debug_fd != -1) {
ret = set_debug_file_from_fd(debug_fd);
if (ret != EOK) {
DEBUG(1, ("set_debug_file_from_fd failed.\n"));
}
}
buf = talloc_size(pd, sizeof(uint8_t)*IN_BUF_SIZE);
if (buf == NULL) {
DEBUG(1, ("malloc failed.\n"));
_exit(-1);
}
while ((ret = read(STDIN_FILENO, buf + len, IN_BUF_SIZE - len)) != 0) {
if (ret == -1) {
if (errno == EINTR || errno == EAGAIN) {
continue;
}
DEBUG(1, ("read failed [%d][%s].\n", errno, strerror(errno)));
goto fail;
} else if (ret > 0) {
len += ret;
if (len > IN_BUF_SIZE) {
DEBUG(1, ("read too much, this should never happen.\n"));
goto fail;
}
continue;
} else {
DEBUG(1, ("unexpected return code of read [%d].\n", ret));
goto fail;
}
}
close(STDIN_FILENO);
kr = talloc_zero(pd, struct krb5_req);
if (kr == NULL) {
DEBUG(1, ("talloc failed.\n"));
goto fail;
}
talloc_set_destructor((TALLOC_CTX *) kr, krb5_cleanup);
kr->pd = pd;
ret = unpack_buffer(buf, len, pd, kr, &offline);
if (ret != EOK) {
DEBUG(1, ("unpack_buffer failed.\n"));
goto fail;
}
ret = krb5_setup(kr, offline);
if (ret != EOK) {
DEBUG(1, ("krb5_setup failed.\n"));
goto fail;
}
ret = kr->child_req(STDOUT_FILENO, kr);
if (ret != EOK) {
DEBUG(1, ("Child request failed.\n"));
goto fail;
}
close(STDOUT_FILENO);
talloc_free(pd);
return 0;
fail:
close(STDOUT_FILENO);
talloc_free(pd);
exit(-1);
}