From cc275f011ea8ca17d270de6946eb54015a4f7055 Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Thu, 12 Apr 2007 11:23:58 +0000 Subject: r22191: Add a samba4kinit binary to the build, so I can test using an existing ccache, as well as PKINIT. Andrew Bartlett (This used to be commit 440b8d9e4b09d5e8c943504ade85c17f752fe705) --- source4/heimdal/kuser/kinit.c | 930 ++++++++++++++++++++++++++++++ source4/heimdal/kuser/kuser_locl.h | 90 +++ source4/heimdal/lib/krb5/convert_creds.c | 179 ++++++ source4/heimdal/lib/krb5/prompter_posix.c | 74 +++ source4/heimdal_build/config.mk | 18 +- source4/heimdal_build/kafs.h | 19 + 6 files changed, 1308 insertions(+), 2 deletions(-) create mode 100644 source4/heimdal/kuser/kinit.c create mode 100644 source4/heimdal/kuser/kuser_locl.h create mode 100644 source4/heimdal/lib/krb5/convert_creds.c create mode 100644 source4/heimdal/lib/krb5/prompter_posix.c create mode 100644 source4/heimdal_build/kafs.h (limited to 'source4') diff --git a/source4/heimdal/kuser/kinit.c b/source4/heimdal/kuser/kinit.c new file mode 100644 index 0000000000..667e0963b0 --- /dev/null +++ b/source4/heimdal/kuser/kinit.c @@ -0,0 +1,930 @@ +/* + * Copyright (c) 1997-2006 Kungliga Tekniska Högskolan + * (Royal Institute of Technology, Stockholm, Sweden). + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "kuser_locl.h" +RCSID("$Id: kinit.c,v 1.141 2006/12/12 16:35:41 lha Exp $"); + +#ifndef KRB4 +#include "krb5-v4compat.h" +#endif + +struct krb5_pk_identity; +struct krb5_pk_cert; +struct ContentInfo; +struct _krb5_krb_auth_data; +struct krb5_dh_moduli; +struct krb5_plugin; +enum plugin_type; +#include "krb5-private.h" + +int forwardable_flag = -1; +int proxiable_flag = -1; +int renewable_flag = -1; +int renew_flag = 0; +int pac_flag = -1; +int validate_flag = 0; +int version_flag = 0; +int help_flag = 0; +int addrs_flag = -1; +struct getarg_strings extra_addresses; +int anonymous_flag = 0; +char *lifetime = NULL; +char *renew_life = NULL; +char *server_str = NULL; +char *cred_cache = NULL; +char *start_str = NULL; +struct getarg_strings etype_str; +int use_keytab = 0; +char *keytab_str = NULL; +int do_afslog = -1; +int get_v4_tgt = -1; +int convert_524 = 0; +int fcache_version; +char *password_file = NULL; +char *pk_user_id = NULL; +char *pk_x509_anchors = NULL; +int pk_use_enckey = 0; + +static char *krb4_cc_name; + +static struct getargs args[] = { + /* + * used by MIT + * a: ~A + * V: verbose + * F: ~f + * P: ~p + * C: v4 cache name? + * 5: + */ + { "524init", '4', arg_flag, &get_v4_tgt, + "obtain version 4 TGT" }, + + { "524convert", '9', arg_flag, &convert_524, + "only convert ticket to version 4" }, + + { "afslog", 0 , arg_flag, &do_afslog, + "obtain afs tokens" }, + + { "cache", 'c', arg_string, &cred_cache, + "credentials cache", "cachename" }, + + { "forwardable", 'f', arg_flag, &forwardable_flag, + "get forwardable tickets"}, + + { "keytab", 't', arg_string, &keytab_str, + "keytab to use", "keytabname" }, + + { "lifetime", 'l', arg_string, &lifetime, + "lifetime of tickets", "time"}, + + { "proxiable", 'p', arg_flag, &proxiable_flag, + "get proxiable tickets" }, + + { "renew", 'R', arg_flag, &renew_flag, + "renew TGT" }, + + { "renewable", 0, arg_flag, &renewable_flag, + "get renewable tickets" }, + + { "renewable-life", 'r', arg_string, &renew_life, + "renewable lifetime of tickets", "time" }, + + { "server", 'S', arg_string, &server_str, + "server to get ticket for", "principal" }, + + { "start-time", 's', arg_string, &start_str, + "when ticket gets valid", "time" }, + + { "use-keytab", 'k', arg_flag, &use_keytab, + "get key from keytab" }, + + { "validate", 'v', arg_flag, &validate_flag, + "validate TGT" }, + + { "enctypes", 'e', arg_strings, &etype_str, + "encryption types to use", "enctypes" }, + + { "fcache-version", 0, arg_integer, &fcache_version, + "file cache version to create" }, + + { "addresses", 'A', arg_negative_flag, &addrs_flag, + "request a ticket with no addresses" }, + + { "extra-addresses",'a', arg_strings, &extra_addresses, + "include these extra addresses", "addresses" }, + + { "anonymous", 0, arg_flag, &anonymous_flag, + "request an anonymous ticket" }, + + { "request-pac", 0, arg_flag, &pac_flag, + "request a Windows PAC" }, + + { "password-file", 0, arg_string, &password_file, + "read the password from a file" }, + +#ifdef PKINIT + { "pk-user", 'C', arg_string, &pk_user_id, + "principal's public/private/certificate identifier", + "id" }, + + { "x509-anchors", 'D', arg_string, &pk_x509_anchors, + "directory with CA certificates", "directory" }, + + { "pk-use-enckey", 0, arg_flag, &pk_use_enckey, + "Use RSA encrypted reply (instead of DH)" }, + +#endif + { "version", 0, arg_flag, &version_flag }, + { "help", 0, arg_flag, &help_flag } +}; + +static void +usage (int ret) +{ + arg_printusage (args, + sizeof(args)/sizeof(*args), + NULL, + "[principal [command]]"); + exit (ret); +} + +#ifdef KRB4 +/* for when the KDC tells us it's a v4 one, we try to talk that */ + +static int +key_to_key(const char *user, + char *instance, + const char *realm, + const void *arg, + des_cblock *key) +{ + memcpy(key, arg, sizeof(des_cblock)); + return 0; +} + +static int +do_v4_fallback (krb5_context context, + const krb5_principal principal, + int lifetime, + int use_srvtab, const char *srvtab_str, + const char *passwd) +{ + int ret; + krb_principal princ; + des_cblock key; + krb5_error_code kret; + + if (lifetime == 0) + lifetime = DEFAULT_TKT_LIFE; + else + lifetime = krb_time_to_life (0, lifetime); + + kret = krb5_524_conv_principal (context, principal, + princ.name, + princ.instance, + princ.realm); + if (kret) { + krb5_warn (context, kret, "krb5_524_conv_principal"); + return 1; + } + + if (use_srvtab || srvtab_str) { + if (srvtab_str == NULL) + srvtab_str = KEYFILE; + + ret = read_service_key (princ.name, princ.instance, princ.realm, + 0, srvtab_str, (char *)&key); + if (ret) { + warnx ("read_service_key %s: %s", srvtab_str, + krb_get_err_text (ret)); + return 1; + } + ret = krb_get_in_tkt (princ.name, princ.instance, princ.realm, + KRB_TICKET_GRANTING_TICKET, princ.realm, + lifetime, key_to_key, NULL, key); + } else { + ret = krb_get_pw_in_tkt(princ.name, princ.instance, princ.realm, + KRB_TICKET_GRANTING_TICKET, princ.realm, + lifetime, passwd); + } + memset (key, 0, sizeof(key)); + if (ret) { + warnx ("%s", krb_get_err_text(ret)); + return 1; + } + if (do_afslog && k_hasafs()) { + if ((ret = krb_afslog(NULL, NULL)) != 0 && ret != KDC_PR_UNKNOWN) { + if(ret > 0) + warnx ("%s", krb_get_err_text(ret)); + else + warnx ("failed to store AFS token"); + } + } + return 0; +} + + +/* + * the special version of get_default_principal that takes v4 into account + */ + +static krb5_error_code +kinit_get_default_principal (krb5_context context, + krb5_principal *princ) +{ + krb5_error_code ret; + krb5_ccache id; + krb_principal v4_princ; + int kret; + + ret = krb5_cc_default (context, &id); + if (ret == 0) { + ret = krb5_cc_get_principal (context, id, princ); + krb5_cc_close (context, id); + if (ret == 0) + return 0; + } + + kret = krb_get_tf_fullname (tkt_string(), + v4_princ.name, + v4_princ.instance, + v4_princ.realm); + if (kret == KSUCCESS) { + ret = krb5_425_conv_principal (context, + v4_princ.name, + v4_princ.instance, + v4_princ.realm, + princ); + if (ret == 0) + return 0; + } + return krb5_get_default_principal (context, princ); +} + +#else /* !KRB4 */ + +static krb5_error_code +kinit_get_default_principal (krb5_context context, + krb5_principal *princ) +{ + return krb5_get_default_principal (context, princ); +} + +#endif /* !KRB4 */ + +static krb5_error_code +get_server(krb5_context context, + krb5_principal client, + const char *server, + krb5_principal *princ) +{ + krb5_realm *client_realm; + if(server) + return krb5_parse_name(context, server, princ); + + client_realm = krb5_princ_realm (context, client); + return krb5_make_principal(context, princ, *client_realm, + KRB5_TGS_NAME, *client_realm, NULL); +} + +static krb5_error_code +do_524init(krb5_context context, krb5_ccache ccache, + krb5_creds *creds, const char *server) +{ + krb5_error_code ret; + + struct credentials c; + krb5_creds in_creds, *real_creds; + + if(creds != NULL) + real_creds = creds; + else { + krb5_principal client; + krb5_cc_get_principal(context, ccache, &client); + memset(&in_creds, 0, sizeof(in_creds)); + ret = get_server(context, client, server, &in_creds.server); + if(ret) { + krb5_free_principal(context, client); + return ret; + } + in_creds.client = client; + ret = krb5_get_credentials(context, 0, ccache, &in_creds, &real_creds); + krb5_free_principal(context, client); + krb5_free_principal(context, in_creds.server); + if(ret) + return ret; + } + ret = krb524_convert_creds_kdc_ccache(context, ccache, real_creds, &c); + if(ret) + krb5_warn(context, ret, "converting creds"); + else { + krb5_error_code tret = _krb5_krb_tf_setup(context, &c, NULL, 0); + if(tret) + krb5_warn(context, tret, "saving v4 creds"); + } + + if(creds == NULL) + krb5_free_creds(context, real_creds); + memset(&c, 0, sizeof(c)); + + return ret; +} + +static int +renew_validate(krb5_context context, + int renew, + int validate, + krb5_ccache cache, + const char *server, + krb5_deltat life) +{ + krb5_error_code ret; + krb5_creds in, *out = NULL; + krb5_kdc_flags flags; + + memset(&in, 0, sizeof(in)); + + ret = krb5_cc_get_principal(context, cache, &in.client); + if(ret) { + krb5_warn(context, ret, "krb5_cc_get_principal"); + return ret; + } + ret = get_server(context, in.client, server, &in.server); + if(ret) { + krb5_warn(context, ret, "get_server"); + goto out; + } + + if (renew) { + /* + * no need to check the error here, its only to be + * friendly to the user + */ + krb5_get_credentials(context, KRB5_GC_CACHED, cache, &in, &out); + } + + flags.i = 0; + flags.b.renewable = flags.b.renew = renew; + flags.b.validate = validate; + + if (forwardable_flag != -1) + flags.b.forwardable = forwardable_flag; + else if (out) + flags.b.forwardable = out->flags.b.forwardable; + + if (proxiable_flag != -1) + flags.b.proxiable = proxiable_flag; + else if (out) + flags.b.proxiable = out->flags.b.proxiable; + + if (anonymous_flag != -1) + flags.b.request_anonymous = anonymous_flag; + if(life) + in.times.endtime = time(NULL) + life; + + if (out) { + krb5_free_creds (context, out); + out = NULL; + } + + + ret = krb5_get_kdc_cred(context, + cache, + flags, + NULL, + NULL, + &in, + &out); + if(ret) { + krb5_warn(context, ret, "krb5_get_kdc_cred"); + goto out; + } + ret = krb5_cc_initialize(context, cache, in.client); + if(ret) { + krb5_free_creds (context, out); + krb5_warn(context, ret, "krb5_cc_initialize"); + goto out; + } + ret = krb5_cc_store_cred(context, cache, out); + + if(ret == 0 && server == NULL) { + /* only do this if it's a general renew-my-tgt request */ + if(get_v4_tgt) + do_524init(context, cache, out, NULL); + if(do_afslog && k_hasafs()) + krb5_afslog(context, cache, NULL, NULL); + } + + krb5_free_creds (context, out); + if(ret) { + krb5_warn(context, ret, "krb5_cc_store_cred"); + goto out; + } +out: + krb5_free_cred_contents(context, &in); + return ret; +} + +static krb5_error_code +get_new_tickets(krb5_context context, + krb5_principal principal, + krb5_ccache ccache, + krb5_deltat ticket_life, + int interactive) +{ + krb5_error_code ret; + krb5_get_init_creds_opt *opt; + krb5_creds cred; + char passwd[256]; + krb5_deltat start_time = 0; + krb5_deltat renew = 0; + char *renewstr = NULL; + krb5_enctype *enctype = NULL; + + passwd[0] = '\0'; + + if (password_file) { + FILE *f; + + if (strcasecmp("STDIN", password_file) == 0) + f = stdin; + else + f = fopen(password_file, "r"); + if (f == NULL) + krb5_errx(context, 1, "Failed to open the password file %s", + password_file); + + if (fgets(passwd, sizeof(passwd), f) == NULL) + krb5_errx(context, 1, + "Failed to read password from file %s", password_file); + if (f != stdin) + fclose(f); + passwd[strcspn(passwd, "\n")] = '\0'; + } + + + memset(&cred, 0, sizeof(cred)); + + ret = krb5_get_init_creds_opt_alloc (context, &opt); + if (ret) + krb5_err(context, 1, ret, "krb5_get_init_creds_opt_alloc"); + + krb5_get_init_creds_opt_set_default_flags(context, "kinit", + /* XXX */principal->realm, opt); + + if(forwardable_flag != -1) + krb5_get_init_creds_opt_set_forwardable (opt, forwardable_flag); + if(proxiable_flag != -1) + krb5_get_init_creds_opt_set_proxiable (opt, proxiable_flag); + if(anonymous_flag != -1) + krb5_get_init_creds_opt_set_anonymous (opt, anonymous_flag); + if (pac_flag != -1) + krb5_get_init_creds_opt_set_pac_request(context, opt, + pac_flag ? TRUE : FALSE); + if (pk_user_id) { + ret = krb5_get_init_creds_opt_set_pkinit(context, opt, + principal, + pk_user_id, + pk_x509_anchors, + NULL, + NULL, + pk_use_enckey ? 2 : 0, + krb5_prompter_posix, + NULL, + passwd); + if (ret) + krb5_err(context, 1, ret, "krb5_get_init_creds_opt_set_pkinit"); + } + + if (addrs_flag != -1) + krb5_get_init_creds_opt_set_addressless(context, opt, + addrs_flag ? FALSE : TRUE); + + if (renew_life == NULL && renewable_flag) + renewstr = "1 month"; + if (renew_life) + renewstr = renew_life; + if (renewstr) { + renew = parse_time (renewstr, "s"); + if (renew < 0) + errx (1, "unparsable time: %s", renewstr); + + krb5_get_init_creds_opt_set_renew_life (opt, renew); + } + + if(ticket_life != 0) + krb5_get_init_creds_opt_set_tkt_life (opt, ticket_life); + + if(start_str) { + int tmp = parse_time (start_str, "s"); + if (tmp < 0) + errx (1, "unparsable time: %s", start_str); + + start_time = tmp; + } + + if(etype_str.num_strings) { + int i; + + enctype = malloc(etype_str.num_strings * sizeof(*enctype)); + if(enctype == NULL) + errx(1, "out of memory"); + for(i = 0; i < etype_str.num_strings; i++) { + ret = krb5_string_to_enctype(context, + etype_str.strings[i], + &enctype[i]); + if(ret) + errx(1, "unrecognized enctype: %s", etype_str.strings[i]); + } + krb5_get_init_creds_opt_set_etype_list(opt, enctype, + etype_str.num_strings); + } + + if(use_keytab || keytab_str) { + krb5_keytab kt; + if(keytab_str) + ret = krb5_kt_resolve(context, keytab_str, &kt); + else + ret = krb5_kt_default(context, &kt); + if (ret) + krb5_err (context, 1, ret, "resolving keytab"); + ret = krb5_get_init_creds_keytab (context, + &cred, + principal, + kt, + start_time, + server_str, + opt); + krb5_kt_close(context, kt); + } else if (pk_user_id) { + ret = krb5_get_init_creds_password (context, + &cred, + principal, + passwd, + krb5_prompter_posix, + NULL, + start_time, + server_str, + opt); + } else if (!interactive) { + krb5_warnx(context, "Not interactive, failed to get initial ticket"); + krb5_get_init_creds_opt_free(context, opt); + return 0; + } else { + + if (passwd[0] == '\0') { + char *p, *prompt; + + krb5_unparse_name (context, principal, &p); + asprintf (&prompt, "%s's Password: ", p); + free (p); + + if (UI_UTIL_read_pw_string(passwd, sizeof(passwd)-1, prompt, 0)){ + memset(passwd, 0, sizeof(passwd)); + exit(1); + } + free (prompt); + } + + + ret = krb5_get_init_creds_password (context, + &cred, + principal, + passwd, + krb5_prompter_posix, + NULL, + start_time, + server_str, + opt); + } + krb5_get_init_creds_opt_free(context, opt); +#ifdef KRB4 + if (ret == KRB5KRB_AP_ERR_V4_REPLY || ret == KRB5_KDC_UNREACH) { + int exit_val; + + exit_val = do_v4_fallback (context, principal, ticket_life, + use_keytab, keytab_str, passwd); + get_v4_tgt = 0; + do_afslog = 0; + memset(passwd, 0, sizeof(passwd)); + if (exit_val == 0 || ret == KRB5KRB_AP_ERR_V4_REPLY) + return exit_val; + } +#endif + memset(passwd, 0, sizeof(passwd)); + + switch(ret){ + case 0: + break; + case KRB5_LIBOS_PWDINTR: /* don't print anything if it was just C-c:ed */ + exit(1); + case KRB5KRB_AP_ERR_BAD_INTEGRITY: + case KRB5KRB_AP_ERR_MODIFIED: + krb5_errx(context, 1, "Password incorrect"); + break; + default: + krb5_err(context, 1, ret, "krb5_get_init_creds"); + } + + if(ticket_life != 0) { + if(abs(cred.times.endtime - cred.times.starttime - ticket_life) > 30) { + char life[64]; + unparse_time_approx(cred.times.endtime - cred.times.starttime, + life, sizeof(life)); + krb5_warnx(context, "NOTICE: ticket lifetime is %s", life); + } + } + if(renew_life) { + if(abs(cred.times.renew_till - cred.times.starttime - renew) > 30) { + char life[64]; + unparse_time_approx(cred.times.renew_till - cred.times.starttime, + life, sizeof(life)); + krb5_warnx(context, "NOTICE: ticket renewable lifetime is %s", + life); + } + } + + ret = krb5_cc_initialize (context, ccache, cred.client); + if (ret) + krb5_err (context, 1, ret, "krb5_cc_initialize"); + + ret = krb5_cc_store_cred (context, ccache, &cred); + if (ret) + krb5_err (context, 1, ret, "krb5_cc_store_cred"); + + krb5_free_cred_contents (context, &cred); + + if (enctype) + free(enctype); + + return 0; +} + +static time_t +ticket_lifetime(krb5_context context, krb5_ccache cache, + krb5_principal client, const char *server) +{ + krb5_creds in_cred, *cred; + krb5_error_code ret; + time_t timeout; + + memset(&in_cred, 0, sizeof(in_cred)); + + ret = krb5_cc_get_principal(context, cache, &in_cred.client); + if(ret) { + krb5_warn(context, ret, "krb5_cc_get_principal"); + return 0; + } + ret = get_server(context, in_cred.client, server, &in_cred.server); + if(ret) { + krb5_free_principal(context, in_cred.client); + krb5_warn(context, ret, "get_server"); + return 0; + } + + ret = krb5_get_credentials(context, KRB5_GC_CACHED, + cache, &in_cred, &cred); + krb5_free_principal(context, in_cred.client); + krb5_free_principal(context, in_cred.server); + if(ret) { + krb5_warn(context, ret, "krb5_get_credentials"); + return 0; + } + timeout = cred->times.endtime - cred->times.starttime; + if (timeout < 0) + timeout = 0; + krb5_free_creds(context, cred); + return timeout; +} + +struct renew_ctx { + krb5_context context; + krb5_ccache ccache; + krb5_principal principal; + krb5_deltat ticket_life; +}; + +static time_t +renew_func(void *ptr) +{ + struct renew_ctx *ctx = ptr; + krb5_error_code ret; + time_t expire; + int new_tickets = 0; + + if (renewable_flag) { + ret = renew_validate(ctx->context, renewable_flag, validate_flag, + ctx->ccache, server_str, ctx->ticket_life); + if (ret) + new_tickets = 1; + } else + new_tickets = 1; + + if (new_tickets) + get_new_tickets(ctx->context, ctx->principal, + ctx->ccache, ctx->ticket_life, 0); + + if(get_v4_tgt || convert_524) + do_524init(ctx->context, ctx->ccache, NULL, server_str); + if(do_afslog && k_hasafs()) + krb5_afslog(ctx->context, ctx->ccache, NULL, NULL); + + expire = ticket_lifetime(ctx->context, ctx->ccache, ctx->principal, + server_str) / 2; + return expire + 1; +} + +int +main (int argc, char **argv) +{ + krb5_error_code ret; + krb5_context context; + krb5_ccache ccache; + krb5_principal principal; + int optidx = 0; + krb5_deltat ticket_life = 0; + + setprogname (argv[0]); + + ret = krb5_init_context (&context); + if (ret == KRB5_CONFIG_BADFORMAT) + errx (1, "krb5_init_context failed to parse configuration file"); + else if (ret) + errx(1, "krb5_init_context failed: %d", ret); + + if(getarg(args, sizeof(args) / sizeof(args[0]), argc, argv, &optidx)) + usage(1); + + if (help_flag) + usage (0); + + if(version_flag) { + print_version(NULL); + exit(0); + } + + argc -= optidx; + argv += optidx; + + if (argv[0]) { + ret = krb5_parse_name (context, argv[0], &principal); + if (ret) + krb5_err (context, 1, ret, "krb5_parse_name"); + } else { + ret = kinit_get_default_principal (context, &principal); + if (ret) + krb5_err (context, 1, ret, "krb5_get_default_principal"); + } + + if(fcache_version) + krb5_set_fcache_version(context, fcache_version); + + if(renewable_flag == -1) + /* this seems somewhat pointless, but whatever */ + krb5_appdefault_boolean(context, "kinit", + krb5_principal_get_realm(context, principal), + "renewable", FALSE, &renewable_flag); + if(get_v4_tgt == -1) + krb5_appdefault_boolean(context, "kinit", + krb5_principal_get_realm(context, principal), + "krb4_get_tickets", FALSE, &get_v4_tgt); + if(do_afslog == -1) + krb5_appdefault_boolean(context, "kinit", + krb5_principal_get_realm(context, principal), + "afslog", TRUE, &do_afslog); + + if(cred_cache) + ret = krb5_cc_resolve(context, cred_cache, &ccache); + else { + if(argc > 1) { + char s[1024]; + ret = krb5_cc_gen_new(context, &krb5_fcc_ops, &ccache); + if(ret) + krb5_err(context, 1, ret, "creating cred cache"); + snprintf(s, sizeof(s), "%s:%s", + krb5_cc_get_type(context, ccache), + krb5_cc_get_name(context, ccache)); + setenv("KRB5CCNAME", s, 1); + if (get_v4_tgt) { + int fd; + if (asprintf(&krb4_cc_name, "%s_XXXXXX", TKT_ROOT) < 0) + krb5_errx(context, 1, "out of memory"); + if((fd = mkstemp(krb4_cc_name)) >= 0) { + close(fd); + setenv("KRBTKFILE", krb4_cc_name, 1); + } else { + free(krb4_cc_name); + krb4_cc_name = NULL; + } + } + } else + ret = krb5_cc_default (context, &ccache); + } + if (ret) + krb5_err (context, 1, ret, "resolving credentials cache"); + + if(argc > 1 && k_hasafs ()) + k_setpag(); + + if (lifetime) { + int tmp = parse_time (lifetime, "s"); + if (tmp < 0) + errx (1, "unparsable time: %s", lifetime); + + ticket_life = tmp; + } + + if(addrs_flag == 0 && extra_addresses.num_strings > 0) + krb5_errx(context, 1, "specifying both extra addresses and " + "no addresses makes no sense"); + { + int i; + krb5_addresses addresses; + memset(&addresses, 0, sizeof(addresses)); + for(i = 0; i < extra_addresses.num_strings; i++) { + ret = krb5_parse_address(context, extra_addresses.strings[i], + &addresses); + if (ret == 0) { + krb5_add_extra_addresses(context, &addresses); + krb5_free_addresses(context, &addresses); + } + } + free_getarg_strings(&extra_addresses); + } + + if(renew_flag || validate_flag) { + ret = renew_validate(context, renew_flag, validate_flag, + ccache, server_str, ticket_life); + exit(ret != 0); + } + + if(!convert_524) + get_new_tickets(context, principal, ccache, ticket_life, 1); + + if(get_v4_tgt || convert_524) + do_524init(context, ccache, NULL, server_str); + if(do_afslog && k_hasafs()) + krb5_afslog(context, ccache, NULL, NULL); + if(argc > 1) { + struct renew_ctx ctx; + time_t timeout; + + timeout = ticket_lifetime(context, ccache, principal, server_str) / 2; + + ctx.context = context; + ctx.ccache = ccache; + ctx.principal = principal; + ctx.ticket_life = ticket_life; + + ret = simple_execvp_timed(argv[1], argv+1, + renew_func, &ctx, timeout); +#define EX_NOEXEC 126 +#define EX_NOTFOUND 127 + if(ret == EX_NOEXEC) + krb5_warnx(context, "permission denied: %s", argv[1]); + else if(ret == EX_NOTFOUND) + krb5_warnx(context, "command not found: %s", argv[1]); + + krb5_cc_destroy(context, ccache); + _krb5_krb_dest_tkt(context, krb4_cc_name); + if(k_hasafs()) + k_unlog(); + } else { + krb5_cc_close (context, ccache); + ret = 0; + } + krb5_free_principal(context, principal); + krb5_free_context (context); + return ret; +} diff --git a/source4/heimdal/kuser/kuser_locl.h b/source4/heimdal/kuser/kuser_locl.h new file mode 100644 index 0000000000..06403cbe67 --- /dev/null +++ b/source4/heimdal/kuser/kuser_locl.h @@ -0,0 +1,90 @@ +/* + * Copyright (c) 1997 - 1999 Kungliga Tekniska Högskolan + * (Royal Institute of Technology, Stockholm, Sweden). + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* $Id: kuser_locl.h,v 1.13 2003/01/21 14:13:51 nectar Exp $ */ + +#ifndef __KUSER_LOCL_H__ +#define __KUSER_LOCL_H__ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#ifdef HAVE_SYS_TYPES_H +#include +#endif +#ifdef HAVE_PWD_H +#include +#endif +#ifdef HAVE_SYS_TIME_H +#include +#endif +#ifdef HAVE_SYS_SOCKET_H +#include +#endif +#ifdef HAVE_NETINET_IN_H +#include +#endif +#ifdef HAVE_NETINET_IN6_H +#include +#endif +#ifdef HAVE_NETINET6_IN6_H +#include +#endif + +#ifdef HAVE_ARPA_INET_H +#include +#endif +#include +#include +#include +#include +#include + +#ifdef KRB4 +#include +#endif +#if defined(HAVE_SYS_IOCTL_H) && SunOS != 40 +#include +#endif +#ifdef HAVE_SYS_IOCCOM_H +#include +#endif +#include +#include "crypto-headers.h" /* for des_read_pw_string */ + +#endif /* __KUSER_LOCL_H__ */ diff --git a/source4/heimdal/lib/krb5/convert_creds.c b/source4/heimdal/lib/krb5/convert_creds.c new file mode 100644 index 0000000000..bff56a2602 --- /dev/null +++ b/source4/heimdal/lib/krb5/convert_creds.c @@ -0,0 +1,179 @@ +/* + * Copyright (c) 1997 - 2004 Kungliga Tekniska Högskolan + * (Royal Institute of Technology, Stockholm, Sweden). + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "krb5_locl.h" +RCSID("$Id: convert_creds.c,v 1.32 2005/04/23 19:40:57 lha Exp $"); + +#include "krb5-v4compat.h" + +static krb5_error_code +check_ticket_flags(TicketFlags f) +{ + return 0; /* maybe add some more tests here? */ +} + +/* Convert the v5 credentials in `in_cred' to v4-dito in `v4creds'. + * This is done by sending them to the 524 function in the KDC. If + * `in_cred' doesn't contain a DES session key, then a new one is + * gotten from the KDC and stored in the cred cache `ccache'. + */ + +krb5_error_code KRB5_LIB_FUNCTION +krb524_convert_creds_kdc(krb5_context context, + krb5_creds *in_cred, + struct credentials *v4creds) +{ + krb5_error_code ret; + krb5_data reply; + krb5_storage *sp; + int32_t tmp; + krb5_data ticket; + char realm[REALM_SZ]; + krb5_creds *v5_creds = in_cred; + + ret = check_ticket_flags(v5_creds->flags.b); + if(ret) + goto out2; + + { + krb5_krbhst_handle handle; + + ret = krb5_krbhst_init(context, + krb5_principal_get_realm(context, + v5_creds->server), + KRB5_KRBHST_KRB524, + &handle); + if (ret) + goto out2; + + ret = krb5_sendto (context, + &v5_creds->ticket, + handle, + &reply); + krb5_krbhst_free(context, handle); + if (ret) + goto out2; + } + sp = krb5_storage_from_mem(reply.data, reply.length); + if(sp == NULL) { + ret = ENOMEM; + krb5_set_error_string (context, "malloc: out of memory"); + goto out2; + } + krb5_ret_int32(sp, &tmp); + ret = tmp; + if(ret == 0) { + memset(v4creds, 0, sizeof(*v4creds)); + ret = krb5_ret_int32(sp, &tmp); + if(ret) + goto out; + v4creds->kvno = tmp; + ret = krb5_ret_data(sp, &ticket); + if(ret) + goto out; + v4creds->ticket_st.length = ticket.length; + memcpy(v4creds->ticket_st.dat, ticket.data, ticket.length); + krb5_data_free(&ticket); + ret = krb5_524_conv_principal(context, + v5_creds->server, + v4creds->service, + v4creds->instance, + v4creds->realm); + if(ret) + goto out; + v4creds->issue_date = v5_creds->times.starttime; + v4creds->lifetime = _krb5_krb_time_to_life(v4creds->issue_date, + v5_creds->times.endtime); + ret = krb5_524_conv_principal(context, v5_creds->client, + v4creds->pname, + v4creds->pinst, + realm); + if(ret) + goto out; + memcpy(v4creds->session, v5_creds->session.keyvalue.data, 8); + } else { + krb5_set_error_string(context, "converting credentials: %s", + krb5_get_err_text(context, ret)); + } +out: + krb5_storage_free(sp); + krb5_data_free(&reply); +out2: + if (v5_creds != in_cred) + krb5_free_creds (context, v5_creds); + return ret; +} + +krb5_error_code KRB5_LIB_FUNCTION +krb524_convert_creds_kdc_ccache(krb5_context context, + krb5_ccache ccache, + krb5_creds *in_cred, + struct credentials *v4creds) +{ + krb5_error_code ret; + krb5_creds *v5_creds = in_cred; + krb5_keytype keytype; + + keytype = v5_creds->session.keytype; + + if (keytype != ENCTYPE_DES_CBC_CRC) { + /* MIT krb524d doesn't like nothing but des-cbc-crc tickets, + so go get one */ + krb5_creds template; + + memset (&template, 0, sizeof(template)); + template.session.keytype = ENCTYPE_DES_CBC_CRC; + ret = krb5_copy_principal (context, in_cred->client, &template.client); + if (ret) { + krb5_free_cred_contents (context, &template); + return ret; + } + ret = krb5_copy_principal (context, in_cred->server, &template.server); + if (ret) { + krb5_free_cred_contents (context, &template); + return ret; + } + + ret = krb5_get_credentials (context, 0, ccache, + &template, &v5_creds); + krb5_free_cred_contents (context, &template); + if (ret) + return ret; + } + + ret = krb524_convert_creds_kdc(context, v5_creds, v4creds); + + if (v5_creds != in_cred) + krb5_free_creds (context, v5_creds); + return ret; +} diff --git a/source4/heimdal/lib/krb5/prompter_posix.c b/source4/heimdal/lib/krb5/prompter_posix.c new file mode 100644 index 0000000000..3ea512c9a7 --- /dev/null +++ b/source4/heimdal/lib/krb5/prompter_posix.c @@ -0,0 +1,74 @@ +/* + * Copyright (c) 1997 - 2001 Kungliga Tekniska Högskolan + * (Royal Institute of Technology, Stockholm, Sweden). + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "krb5_locl.h" + +RCSID("$Id: prompter_posix.c,v 1.10 2004/05/25 21:38:14 lha Exp $"); + +int KRB5_LIB_FUNCTION +krb5_prompter_posix (krb5_context context, + void *data, + const char *name, + const char *banner, + int num_prompts, + krb5_prompt prompts[]) +{ + int i; + + if (name) + fprintf (stderr, "%s\n", name); + if (banner) + fprintf (stderr, "%s\n", banner); + if (name || banner) + fflush(stderr); + for (i = 0; i < num_prompts; ++i) { + if (prompts[i].hidden) { + if(UI_UTIL_read_pw_string(prompts[i].reply->data, + prompts[i].reply->length, + prompts[i].prompt, + 0)) + return 1; + } else { + char *s = prompts[i].reply->data; + + fputs (prompts[i].prompt, stdout); + fflush (stdout); + if(fgets(prompts[i].reply->data, + prompts[i].reply->length, + stdin) == NULL) + return 1; + s[strcspn(s, "\n")] = '\0'; + } + } + return 0; +} diff --git a/source4/heimdal_build/config.mk b/source4/heimdal_build/config.mk index 9764da6bd6..8e59009d70 100644 --- a/source4/heimdal_build/config.mk +++ b/source4/heimdal_build/config.mk @@ -163,7 +163,7 @@ OBJ_FILES = \ ../heimdal/lib/gssapi/krb5/accept_sec_context.o \ ../heimdal/lib/gssapi/krb5/set_sec_context_option.o \ ../heimdal/lib/gssapi/krb5/process_context_token.o -PRIVATE_DEPENDENCIES = HEIMDAL_KRB5 HEIMDAL_ROKEN HEIMDAL_DES HEIMDAL_ASN1 HEIMDAL_GLUE HEIMDAL_SPNEGO_ASN1 +PRIVATE_DEPENDENCIES = HEIMDAL_KRB5 HEIMDAL_ROKEN HEIMDAL_DES HEIMDAL_ASN1 HEIMDAL_SPNEGO_ASN1 # End SUBSYSTEM HEIMDAL_GSSAPI ####################### @@ -171,7 +171,7 @@ PRIVATE_DEPENDENCIES = HEIMDAL_KRB5 HEIMDAL_ROKEN HEIMDAL_DES HEIMDAL_ASN1 HEIMD # Start SUBSYSTEM HEIMDAL_KRB5 [SUBSYSTEM::HEIMDAL_KRB5] CFLAGS = -Iheimdal_build -Iheimdal/lib/krb5 -PRIVATE_DEPENDENCIES = HEIMDAL_ROKEN HEIMDAL_DES HEIMDAL_HX509 HEIMDAL_PKINIT_ASN1 +PRIVATE_DEPENDENCIES = HEIMDAL_ROKEN HEIMDAL_DES HEIMDAL_HX509 HEIMDAL_PKINIT_ASN1 HEIMDAL_GLUE PUBLIC_DEPENDENCIES = HEIMDAL_KRB5_ASN1 OBJ_FILES = \ ../heimdal/lib/krb5/acache.o \ @@ -189,6 +189,7 @@ OBJ_FILES = \ ../heimdal/lib/krb5/config_file_netinfo.o \ ../heimdal/lib/krb5/constants.o \ ../heimdal/lib/krb5/context.o \ + ../heimdal/lib/krb5/convert_creds.o \ ../heimdal/lib/krb5/copy_host_realm.o \ ../heimdal/lib/krb5/crc.o \ ../heimdal/lib/krb5/creds.o \ @@ -236,6 +237,7 @@ OBJ_FILES = \ ../heimdal/lib/krb5/plugin.o \ ../heimdal/lib/krb5/principal.o \ ../heimdal/lib/krb5/pac.o \ + ../heimdal/lib/krb5/prompter_posix.o \ ../heimdal/lib/krb5/rd_cred.o \ ../heimdal/lib/krb5/rd_error.o \ ../heimdal/lib/krb5/rd_priv.o \ @@ -579,5 +581,17 @@ PUBLIC_DEPENDENCIES = HEIMDAL # End SUBSYSTEM KERBEROS_LIB ####################### +####################### +# Start BINARY compile_et +[BINARY::samba4kinit] +CFLAGS = -Iheimdal_build -Iheimdal/lib/roken +OBJ_FILES = ../heimdal/kuser/kinit.o \ + ../heimdal/lib/vers/print_version.o \ + ../heimdal/lib/roken/setprogname.o \ + ../heimdal/lib/roken/getarg.o +PRIVATE_DEPENDENCIES = HEIMDAL_KRB5 +# End BINARY compile_et +####################### + dist:: heimdal/lib/asn1/lex.c heimdal/lib/com_err/lex.c \ heimdal/lib/asn1/parse.c heimdal/lib/com_err/parse.c diff --git a/source4/heimdal_build/kafs.h b/source4/heimdal_build/kafs.h new file mode 100644 index 0000000000..878dd08397 --- /dev/null +++ b/source4/heimdal_build/kafs.h @@ -0,0 +1,19 @@ +int k_hasafs (void) { + return 0; +}; + +int krb_afslog (const char *cell, const char *realm) { + return 0; +}; +int k_unlog (void) { + return 0; +}; +int k_setpag (void) { + return 0; +}; +krb5_error_code krb5_afslog (krb5_context context, + krb5_ccache id, + const char *cell, + krb5_const_realm realm) { + return 0; +}; -- cgit