Home | History | Annotate | Line # | Download | only in kuser
      1  1.3  christos /*	$NetBSD: kinit.c,v 1.5 2023/06/19 21:41:42 christos Exp $	*/
      2  1.1     elric 
      3  1.1     elric /*
      4  1.1     elric  * Copyright (c) 1997-2007 Kungliga Tekniska Hgskolan
      5  1.1     elric  * (Royal Institute of Technology, Stockholm, Sweden).
      6  1.1     elric  * All rights reserved.
      7  1.1     elric  *
      8  1.1     elric  * Portions Copyright (c) 2009 Apple Inc. All rights reserved.
      9  1.1     elric  *
     10  1.1     elric  * Redistribution and use in source and binary forms, with or without
     11  1.1     elric  * modification, are permitted provided that the following conditions
     12  1.1     elric  * are met:
     13  1.1     elric  *
     14  1.1     elric  * 1. Redistributions of source code must retain the above copyright
     15  1.1     elric  *    notice, this list of conditions and the following disclaimer.
     16  1.1     elric  *
     17  1.1     elric  * 2. Redistributions in binary form must reproduce the above copyright
     18  1.1     elric  *    notice, this list of conditions and the following disclaimer in the
     19  1.1     elric  *    documentation and/or other materials provided with the distribution.
     20  1.1     elric  *
     21  1.1     elric  * 3. Neither the name of the Institute nor the names of its contributors
     22  1.1     elric  *    may be used to endorse or promote products derived from this software
     23  1.1     elric  *    without specific prior written permission.
     24  1.1     elric  *
     25  1.1     elric  * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
     26  1.1     elric  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     27  1.1     elric  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     28  1.1     elric  * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
     29  1.1     elric  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     30  1.1     elric  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     31  1.1     elric  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     32  1.1     elric  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     33  1.1     elric  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     34  1.1     elric  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     35  1.1     elric  * SUCH DAMAGE.
     36  1.1     elric  */
     37  1.1     elric 
     38  1.1     elric #include "kuser_locl.h"
     39  1.1     elric 
     40  1.1     elric #ifdef __APPLE__
     41  1.1     elric #include <Security/Security.h>
     42  1.1     elric #endif
     43  1.1     elric 
     44  1.1     elric #ifndef NO_NTLM
     45  1.1     elric #include <krb5/heimntlm.h>
     46  1.1     elric #endif
     47  1.1     elric 
     48  1.2  christos #ifndef SIGINFO
     49  1.2  christos #define SIGINFO SIGUSR1
     50  1.2  christos #endif
     51  1.2  christos 
     52  1.1     elric int forwardable_flag	= -1;
     53  1.1     elric int proxiable_flag	= -1;
     54  1.1     elric int renewable_flag	= -1;
     55  1.1     elric int renew_flag		= 0;
     56  1.1     elric int pac_flag		= -1;
     57  1.1     elric int validate_flag	= 0;
     58  1.1     elric int version_flag	= 0;
     59  1.1     elric int help_flag		= 0;
     60  1.1     elric int addrs_flag		= -1;
     61  1.1     elric struct getarg_strings extra_addresses;
     62  1.1     elric int anonymous_flag	= 0;
     63  1.1     elric char *lifetime 		= NULL;
     64  1.1     elric char *renew_life	= NULL;
     65  1.1     elric char *server_str	= NULL;
     66  1.1     elric char *cred_cache	= NULL;
     67  1.1     elric char *start_str		= NULL;
     68  1.1     elric static int switch_cache_flags = 1;
     69  1.1     elric struct getarg_strings etype_str;
     70  1.1     elric int use_keytab		= 0;
     71  1.1     elric char *keytab_str	= NULL;
     72  1.2  christos static krb5_keytab kt	= NULL;
     73  1.1     elric int do_afslog		= -1;
     74  1.1     elric int fcache_version;
     75  1.1     elric char *password_file	= NULL;
     76  1.1     elric char *pk_user_id	= NULL;
     77  1.1     elric int pk_enterprise_flag = 0;
     78  1.1     elric struct hx509_certs_data *ent_user_id = NULL;
     79  1.1     elric char *pk_x509_anchors	= NULL;
     80  1.1     elric int pk_use_enckey	= 0;
     81  1.1     elric static int canonicalize_flag = 0;
     82  1.1     elric static int enterprise_flag = 0;
     83  1.1     elric static int ok_as_delegate_flag = 0;
     84  1.2  christos static char *fast_armor_cache_string = NULL;
     85  1.1     elric static int use_referrals_flag = 0;
     86  1.1     elric static int windows_flag = 0;
     87  1.1     elric #ifndef NO_NTLM
     88  1.1     elric static char *ntlm_domain;
     89  1.1     elric #endif
     90  1.1     elric 
     91  1.1     elric 
     92  1.1     elric static struct getargs args[] = {
     93  1.1     elric     /*
     94  1.1     elric      * used by MIT
     95  1.1     elric      * a: ~A
     96  1.1     elric      * V: verbose
     97  1.1     elric      * F: ~f
     98  1.1     elric      * P: ~p
     99  1.1     elric      * C: v4 cache name?
    100  1.1     elric      * 5:
    101  1.1     elric      *
    102  1.1     elric      * old flags
    103  1.1     elric      * 4:
    104  1.1     elric      * 9:
    105  1.1     elric      */
    106  1.1     elric     { "afslog", 	0  , arg_flag, &do_afslog,
    107  1.2  christos       NP_("obtain afs tokens", ""), NULL },
    108  1.1     elric 
    109  1.1     elric     { "cache", 		'c', arg_string, &cred_cache,
    110  1.1     elric       NP_("credentials cache", ""), "cachename" },
    111  1.1     elric 
    112  1.2  christos     { "forwardable",	'F', arg_negative_flag, &forwardable_flag,
    113  1.2  christos       NP_("get tickets not forwardable", ""), NULL },
    114  1.1     elric 
    115  1.1     elric     { NULL,		'f', arg_flag, &forwardable_flag,
    116  1.2  christos       NP_("get forwardable tickets", ""), NULL },
    117  1.1     elric 
    118  1.1     elric     { "keytab",         't', arg_string, &keytab_str,
    119  1.1     elric       NP_("keytab to use", ""), "keytabname" },
    120  1.1     elric 
    121  1.1     elric     { "lifetime",	'l', arg_string, &lifetime,
    122  1.2  christos       NP_("lifetime of tickets", ""), "time" },
    123  1.1     elric 
    124  1.1     elric     { "proxiable",	'p', arg_flag, &proxiable_flag,
    125  1.2  christos       NP_("get proxiable tickets", ""), NULL },
    126  1.1     elric 
    127  1.1     elric     { "renew",          'R', arg_flag, &renew_flag,
    128  1.2  christos       NP_("renew TGT", ""), NULL },
    129  1.1     elric 
    130  1.1     elric     { "renewable",	0,   arg_flag, &renewable_flag,
    131  1.2  christos       NP_("get renewable tickets", ""), NULL },
    132  1.1     elric 
    133  1.1     elric     { "renewable-life",	'r', arg_string, &renew_life,
    134  1.1     elric       NP_("renewable lifetime of tickets", ""), "time" },
    135  1.1     elric 
    136  1.1     elric     { "server", 	'S', arg_string, &server_str,
    137  1.1     elric       NP_("server to get ticket for", ""), "principal" },
    138  1.1     elric 
    139  1.1     elric     { "start-time",	's', arg_string, &start_str,
    140  1.1     elric       NP_("when ticket gets valid", ""), "time" },
    141  1.1     elric 
    142  1.1     elric     { "use-keytab",     'k', arg_flag, &use_keytab,
    143  1.2  christos       NP_("get key from keytab", ""), NULL },
    144  1.1     elric 
    145  1.1     elric     { "validate",	'v', arg_flag, &validate_flag,
    146  1.2  christos       NP_("validate TGT", ""), NULL },
    147  1.1     elric 
    148  1.1     elric     { "enctypes",	'e', arg_strings, &etype_str,
    149  1.1     elric       NP_("encryption types to use", ""), "enctypes" },
    150  1.1     elric 
    151  1.1     elric     { "fcache-version", 0,   arg_integer, &fcache_version,
    152  1.2  christos       NP_("file cache version to create", ""), NULL },
    153  1.1     elric 
    154  1.1     elric     { "addresses",	'A',   arg_negative_flag,	&addrs_flag,
    155  1.2  christos       NP_("request a ticket with no addresses", ""), NULL },
    156  1.1     elric 
    157  1.1     elric     { "extra-addresses",'a', arg_strings,	&extra_addresses,
    158  1.1     elric       NP_("include these extra addresses", ""), "addresses" },
    159  1.1     elric 
    160  1.4  christos     { "anonymous",	'n',   arg_flag,	&anonymous_flag,
    161  1.2  christos       NP_("request an anonymous ticket", ""), NULL },
    162  1.1     elric 
    163  1.1     elric     { "request-pac",	0,   arg_flag,	&pac_flag,
    164  1.2  christos       NP_("request a Windows PAC", ""), NULL },
    165  1.1     elric 
    166  1.1     elric     { "password-file",	0,   arg_string, &password_file,
    167  1.2  christos       NP_("read the password from a file", ""), NULL },
    168  1.1     elric 
    169  1.1     elric     { "canonicalize",0,   arg_flag, &canonicalize_flag,
    170  1.2  christos       NP_("canonicalize client principal", ""), NULL },
    171  1.1     elric 
    172  1.1     elric     { "enterprise",0,   arg_flag, &enterprise_flag,
    173  1.2  christos       NP_("parse principal as a KRB5-NT-ENTERPRISE name", ""), NULL },
    174  1.1     elric #ifdef PKINIT
    175  1.1     elric     { "pk-enterprise",	0,	arg_flag,	&pk_enterprise_flag,
    176  1.2  christos       NP_("use enterprise name from certificate", ""), NULL },
    177  1.1     elric 
    178  1.1     elric     { "pk-user",	'C',	arg_string,	&pk_user_id,
    179  1.1     elric       NP_("principal's public/private/certificate identifier", ""), "id" },
    180  1.1     elric 
    181  1.1     elric     { "x509-anchors",	'D',  arg_string, &pk_x509_anchors,
    182  1.1     elric       NP_("directory with CA certificates", ""), "directory" },
    183  1.1     elric 
    184  1.1     elric     { "pk-use-enckey",	0,  arg_flag, &pk_use_enckey,
    185  1.2  christos       NP_("Use RSA encrypted reply (instead of DH)", ""), NULL },
    186  1.1     elric #endif
    187  1.1     elric #ifndef NO_NTLM
    188  1.1     elric     { "ntlm-domain",	0,  arg_string, &ntlm_domain,
    189  1.1     elric       NP_("NTLM domain", ""), "domain" },
    190  1.1     elric #endif
    191  1.1     elric 
    192  1.1     elric     { "change-default",  0,  arg_negative_flag, &switch_cache_flags,
    193  1.2  christos       NP_("switch the default cache to the new credentials cache", ""), NULL },
    194  1.1     elric 
    195  1.1     elric     { "ok-as-delegate",	0,  arg_flag, &ok_as_delegate_flag,
    196  1.2  christos       NP_("honor ok-as-delegate on tickets", ""), NULL },
    197  1.2  christos 
    198  1.2  christos     { "fast-armor-cache",	0,  arg_string, &fast_armor_cache_string,
    199  1.2  christos       NP_("use this credential cache as FAST armor cache", ""), "cache" },
    200  1.1     elric 
    201  1.1     elric     { "use-referrals",	0,  arg_flag, &use_referrals_flag,
    202  1.2  christos       NP_("only use referrals, no dns canalisation", ""), NULL },
    203  1.1     elric 
    204  1.1     elric     { "windows",	0,  arg_flag, &windows_flag,
    205  1.2  christos       NP_("get windows behavior", ""), NULL },
    206  1.1     elric 
    207  1.2  christos     { "version", 	0,   arg_flag, &version_flag, NULL, NULL },
    208  1.2  christos     { "help",		0,   arg_flag, &help_flag, NULL, NULL }
    209  1.1     elric };
    210  1.1     elric 
    211  1.4  christos static char *
    212  1.4  christos get_default_realm(krb5_context context);
    213  1.4  christos 
    214  1.1     elric static void
    215  1.2  christos usage(int ret)
    216  1.1     elric {
    217  1.2  christos     arg_printusage_i18n(args, sizeof(args)/sizeof(*args), N_("Usage: ", ""),
    218  1.2  christos 			NULL, "[principal [command]]", getarg_i18n);
    219  1.2  christos     exit(ret);
    220  1.1     elric }
    221  1.1     elric 
    222  1.1     elric static krb5_error_code
    223  1.1     elric get_server(krb5_context context,
    224  1.1     elric 	   krb5_principal client,
    225  1.1     elric 	   const char *server,
    226  1.1     elric 	   krb5_principal *princ)
    227  1.1     elric {
    228  1.1     elric     krb5_const_realm realm;
    229  1.2  christos     if (server)
    230  1.1     elric 	return krb5_parse_name(context, server, princ);
    231  1.1     elric 
    232  1.1     elric     realm = krb5_principal_get_realm(context, client);
    233  1.1     elric     return krb5_make_principal(context, princ, realm,
    234  1.1     elric 			       KRB5_TGS_NAME, realm, NULL);
    235  1.1     elric }
    236  1.1     elric 
    237  1.2  christos static krb5_error_code
    238  1.2  christos copy_configs(krb5_context context,
    239  1.2  christos 	     krb5_ccache dst,
    240  1.2  christos 	     krb5_ccache src,
    241  1.2  christos 	     krb5_principal start_ticket_server)
    242  1.2  christos {
    243  1.2  christos     krb5_error_code ret;
    244  1.4  christos     const char *cfg_names[] = {"realm-config", "FriendlyName", "anon_pkinit_realm", NULL};
    245  1.2  christos     const char *cfg_names_w_pname[] = {"fast_avail", NULL};
    246  1.2  christos     krb5_data cfg_data;
    247  1.2  christos     size_t i;
    248  1.2  christos 
    249  1.2  christos     for (i = 0; cfg_names[i]; i++) {
    250  1.2  christos 	ret = krb5_cc_get_config(context, src, NULL, cfg_names[i], &cfg_data);
    251  1.2  christos 	if (ret == KRB5_CC_NOTFOUND || ret == KRB5_CC_END) {
    252  1.2  christos 	    continue;
    253  1.2  christos 	} else if (ret) {
    254  1.2  christos 	    krb5_warn(context, ret, "krb5_cc_get_config");
    255  1.2  christos 	    return ret;
    256  1.2  christos 	}
    257  1.2  christos 	ret = krb5_cc_set_config(context, dst, NULL, cfg_names[i], &cfg_data);
    258  1.2  christos 	if (ret)
    259  1.2  christos 	    krb5_warn(context, ret, "krb5_cc_set_config");
    260  1.2  christos     }
    261  1.2  christos     for (i = 0; start_ticket_server && cfg_names_w_pname[i]; i++) {
    262  1.2  christos 	ret = krb5_cc_get_config(context, src, start_ticket_server,
    263  1.2  christos 				 cfg_names_w_pname[i], &cfg_data);
    264  1.2  christos 	if (ret == KRB5_CC_NOTFOUND || ret == KRB5_CC_END) {
    265  1.2  christos 	    continue;
    266  1.2  christos 	} else if (ret) {
    267  1.2  christos 	    krb5_warn(context, ret, "krb5_cc_get_config");
    268  1.2  christos 	    return ret;
    269  1.2  christos 	}
    270  1.2  christos 	ret = krb5_cc_set_config(context, dst, start_ticket_server,
    271  1.2  christos 				 cfg_names_w_pname[i], &cfg_data);
    272  1.2  christos 	if (ret && ret != KRB5_CC_NOTFOUND)
    273  1.2  christos 	    krb5_warn(context, ret, "krb5_cc_set_config");
    274  1.2  christos     }
    275  1.2  christos     /*
    276  1.2  christos      * We don't copy cc configs for any other principals though (mostly
    277  1.2  christos      * those are per-target time offsets and the like, so it's bad to
    278  1.2  christos      * lose them, but hardly the end of the world, and as they may not
    279  1.2  christos      * expire anyways, it's good to let them go).
    280  1.2  christos      */
    281  1.2  christos     return 0;
    282  1.2  christos }
    283  1.2  christos 
    284  1.2  christos static krb5_error_code
    285  1.4  christos get_anon_pkinit_tgs_name(krb5_context context,
    286  1.4  christos 			 krb5_ccache ccache,
    287  1.4  christos 			 krb5_principal *tgs_name)
    288  1.4  christos {
    289  1.4  christos     krb5_error_code ret;
    290  1.4  christos     krb5_data data;
    291  1.4  christos     char *realm;
    292  1.4  christos 
    293  1.4  christos     ret = krb5_cc_get_config(context, ccache, NULL, "anon_pkinit_realm", &data);
    294  1.4  christos     if (ret == 0)
    295  1.4  christos 	realm = strndup(data.data, data.length);
    296  1.4  christos     else
    297  1.4  christos 	realm = get_default_realm(context);
    298  1.4  christos 
    299  1.4  christos     krb5_data_free(&data);
    300  1.4  christos 
    301  1.4  christos     if (realm == NULL)
    302  1.4  christos 	return krb5_enomem(context);
    303  1.4  christos 
    304  1.4  christos     ret = krb5_make_principal(context, tgs_name, realm,
    305  1.4  christos 			      KRB5_TGS_NAME, realm, NULL);
    306  1.4  christos 
    307  1.4  christos     free(realm);
    308  1.4  christos 
    309  1.4  christos     return ret;
    310  1.4  christos }
    311  1.4  christos 
    312  1.4  christos static krb5_error_code
    313  1.1     elric renew_validate(krb5_context context,
    314  1.1     elric 	       int renew,
    315  1.1     elric 	       int validate,
    316  1.1     elric 	       krb5_ccache cache,
    317  1.1     elric 	       const char *server,
    318  1.1     elric 	       krb5_deltat life)
    319  1.1     elric {
    320  1.1     elric     krb5_error_code ret;
    321  1.2  christos     krb5_ccache tempccache = NULL;
    322  1.1     elric     krb5_creds in, *out = NULL;
    323  1.1     elric     krb5_kdc_flags flags;
    324  1.1     elric 
    325  1.1     elric     memset(&in, 0, sizeof(in));
    326  1.1     elric 
    327  1.1     elric     ret = krb5_cc_get_principal(context, cache, &in.client);
    328  1.2  christos     if (ret) {
    329  1.1     elric 	krb5_warn(context, ret, "krb5_cc_get_principal");
    330  1.1     elric 	return ret;
    331  1.1     elric     }
    332  1.4  christos 
    333  1.4  christos     if (server == NULL &&
    334  1.4  christos 	krb5_principal_is_anonymous(context, in.client,
    335  1.4  christos 				    KRB5_ANON_MATCH_UNAUTHENTICATED))
    336  1.4  christos 	ret = get_anon_pkinit_tgs_name(context, cache, &in.server);
    337  1.4  christos     else
    338  1.4  christos 	ret = get_server(context, in.client, server, &in.server);
    339  1.2  christos     if (ret) {
    340  1.1     elric 	krb5_warn(context, ret, "get_server");
    341  1.1     elric 	goto out;
    342  1.1     elric     }
    343  1.1     elric 
    344  1.1     elric     if (renew) {
    345  1.1     elric 	/*
    346  1.1     elric 	 * no need to check the error here, it's only to be
    347  1.1     elric 	 * friendly to the user
    348  1.1     elric 	 */
    349  1.1     elric 	krb5_get_credentials(context, KRB5_GC_CACHED, cache, &in, &out);
    350  1.1     elric     }
    351  1.1     elric 
    352  1.1     elric     flags.i = 0;
    353  1.1     elric     flags.b.renewable         = flags.b.renew = renew;
    354  1.1     elric     flags.b.validate          = validate;
    355  1.1     elric 
    356  1.1     elric     if (forwardable_flag != -1)
    357  1.1     elric 	flags.b.forwardable       = forwardable_flag;
    358  1.1     elric     else if (out)
    359  1.1     elric 	flags.b.forwardable 	  = out->flags.b.forwardable;
    360  1.1     elric 
    361  1.1     elric     if (proxiable_flag != -1)
    362  1.1     elric 	flags.b.proxiable         = proxiable_flag;
    363  1.1     elric     else if (out)
    364  1.1     elric 	flags.b.proxiable 	  = out->flags.b.proxiable;
    365  1.1     elric 
    366  1.1     elric     if (anonymous_flag)
    367  1.1     elric 	flags.b.request_anonymous = anonymous_flag;
    368  1.2  christos     if (life)
    369  1.1     elric 	in.times.endtime = time(NULL) + life;
    370  1.1     elric 
    371  1.1     elric     if (out) {
    372  1.2  christos 	krb5_free_creds(context, out);
    373  1.1     elric 	out = NULL;
    374  1.1     elric     }
    375  1.1     elric 
    376  1.1     elric 
    377  1.1     elric     ret = krb5_get_kdc_cred(context,
    378  1.1     elric 			    cache,
    379  1.1     elric 			    flags,
    380  1.1     elric 			    NULL,
    381  1.1     elric 			    NULL,
    382  1.1     elric 			    &in,
    383  1.1     elric 			    &out);
    384  1.2  christos     if (ret) {
    385  1.1     elric 	krb5_warn(context, ret, "krb5_get_kdc_cred");
    386  1.1     elric 	goto out;
    387  1.1     elric     }
    388  1.2  christos 
    389  1.2  christos     ret = krb5_cc_new_unique(context, krb5_cc_get_type(context, cache),
    390  1.2  christos 			     NULL, &tempccache);
    391  1.2  christos     if (ret) {
    392  1.2  christos 	krb5_warn(context, ret, "krb5_cc_new_unique");
    393  1.2  christos 	goto out;
    394  1.2  christos     }
    395  1.2  christos 
    396  1.2  christos     ret = krb5_cc_initialize(context, tempccache, in.client);
    397  1.2  christos     if (ret) {
    398  1.1     elric 	krb5_warn(context, ret, "krb5_cc_initialize");
    399  1.1     elric 	goto out;
    400  1.1     elric     }
    401  1.1     elric 
    402  1.2  christos     ret = krb5_cc_store_cred(context, tempccache, out);
    403  1.2  christos     if (ret) {
    404  1.2  christos 	krb5_warn(context, ret, "krb5_cc_store_cred");
    405  1.2  christos 	goto out;
    406  1.1     elric     }
    407  1.1     elric 
    408  1.2  christos     /*
    409  1.2  christos      * We want to preserve cc configs as some are security-relevant, and
    410  1.2  christos      * anyways it's the friendly thing to do.
    411  1.2  christos      */
    412  1.2  christos     ret = copy_configs(context, tempccache, cache, out->server);
    413  1.2  christos     if (ret)
    414  1.2  christos 	goto out;
    415  1.2  christos 
    416  1.2  christos     ret = krb5_cc_move(context, tempccache, cache);
    417  1.2  christos     if (ret) {
    418  1.2  christos 	krb5_warn(context, ret, "krb5_cc_move");
    419  1.1     elric 	goto out;
    420  1.1     elric     }
    421  1.2  christos     tempccache = NULL;
    422  1.2  christos 
    423  1.1     elric out:
    424  1.2  christos     if (tempccache)
    425  1.4  christos 	krb5_cc_destroy(context, tempccache);
    426  1.2  christos     if (out)
    427  1.2  christos 	krb5_free_creds(context, out);
    428  1.1     elric     krb5_free_cred_contents(context, &in);
    429  1.1     elric     return ret;
    430  1.1     elric }
    431  1.1     elric 
    432  1.1     elric #ifndef NO_NTLM
    433  1.1     elric 
    434  1.1     elric static krb5_error_code
    435  1.1     elric store_ntlmkey(krb5_context context, krb5_ccache id,
    436  1.1     elric 	      const char *domain, struct ntlm_buf *buf)
    437  1.1     elric {
    438  1.1     elric     krb5_error_code ret;
    439  1.1     elric     krb5_data data;
    440  1.1     elric     char *name;
    441  1.2  christos     int aret;
    442  1.1     elric 
    443  1.2  christos     ret = krb5_cc_get_config(context, id, NULL, "default-ntlm-domain", &data);
    444  1.2  christos     if (ret == 0) {
    445  1.2  christos         krb5_data_free(&data);
    446  1.2  christos     } else {
    447  1.2  christos         data.length = strlen(domain);
    448  1.2  christos         data.data = rk_UNCONST(domain);
    449  1.2  christos         ret = krb5_cc_set_config(context, id, NULL, "default-ntlm-domain", &data);
    450  1.2  christos         if (ret != 0)
    451  1.2  christos             return ret;
    452  1.1     elric     }
    453  1.1     elric 
    454  1.2  christos     aret = asprintf(&name, "ntlm-key-%s", domain);
    455  1.2  christos     if (aret == -1 || name == NULL)
    456  1.2  christos 	return krb5_enomem(context);
    457  1.2  christos 
    458  1.1     elric     data.length = buf->length;
    459  1.1     elric     data.data = buf->data;
    460  1.1     elric 
    461  1.1     elric     ret = krb5_cc_set_config(context, id, NULL, name, &data);
    462  1.1     elric     free(name);
    463  1.1     elric     return ret;
    464  1.1     elric }
    465  1.1     elric #endif
    466  1.1     elric 
    467  1.1     elric static krb5_error_code
    468  1.1     elric get_new_tickets(krb5_context context,
    469  1.1     elric 		krb5_principal principal,
    470  1.1     elric 		krb5_ccache ccache,
    471  1.1     elric 		krb5_deltat ticket_life,
    472  1.4  christos 		int interactive,
    473  1.4  christos 		int anonymous_pkinit)
    474  1.1     elric {
    475  1.1     elric     krb5_error_code ret;
    476  1.1     elric     krb5_creds cred;
    477  1.1     elric     char passwd[256];
    478  1.1     elric     krb5_deltat start_time = 0;
    479  1.1     elric     krb5_deltat renew = 0;
    480  1.2  christos     const char *renewstr = NULL;
    481  1.1     elric     krb5_enctype *enctype = NULL;
    482  1.2  christos     krb5_ccache tempccache = NULL;
    483  1.2  christos     krb5_init_creds_context ctx = NULL;
    484  1.2  christos     krb5_get_init_creds_opt *opt = NULL;
    485  1.2  christos     krb5_prompter_fct prompter = krb5_prompter_posix;
    486  1.1     elric #ifndef NO_NTLM
    487  1.1     elric     struct ntlm_buf ntlmkey;
    488  1.1     elric     memset(&ntlmkey, 0, sizeof(ntlmkey));
    489  1.1     elric #endif
    490  1.1     elric     passwd[0] = '\0';
    491  1.1     elric 
    492  1.2  christos     if (!interactive)
    493  1.2  christos 	prompter = NULL;
    494  1.2  christos 
    495  1.1     elric     if (password_file) {
    496  1.1     elric 	FILE *f;
    497  1.1     elric 
    498  1.1     elric 	if (strcasecmp("STDIN", password_file) == 0)
    499  1.1     elric 	    f = stdin;
    500  1.1     elric 	else
    501  1.1     elric 	    f = fopen(password_file, "r");
    502  1.2  christos 	if (f == NULL) {
    503  1.2  christos 	    krb5_warnx(context, "Failed to open the password file %s",
    504  1.2  christos 		       password_file);
    505  1.2  christos 	    return errno;
    506  1.2  christos 	}
    507  1.2  christos 
    508  1.2  christos 	if (fgets(passwd, sizeof(passwd), f) == NULL) {
    509  1.2  christos 	    krb5_warnx(context, N_("Failed to read password from file %s", ""),
    510  1.2  christos 		       password_file);
    511  1.2  christos 	    fclose(f);
    512  1.2  christos 	    return EINVAL; /* XXX Need a better error */
    513  1.2  christos 	}
    514  1.1     elric 	if (f != stdin)
    515  1.1     elric 	    fclose(f);
    516  1.1     elric 	passwd[strcspn(passwd, "\n")] = '\0';
    517  1.1     elric     }
    518  1.1     elric 
    519  1.1     elric #ifdef __APPLE__
    520  1.1     elric     if (passwd[0] == '\0') {
    521  1.1     elric 	const char *realm;
    522  1.1     elric 	OSStatus osret;
    523  1.1     elric 	UInt32 length;
    524  1.1     elric 	void *buffer;
    525  1.1     elric 	char *name;
    526  1.1     elric 
    527  1.1     elric 	realm = krb5_principal_get_realm(context, principal);
    528  1.1     elric 
    529  1.1     elric 	ret = krb5_unparse_name_flags(context, principal,
    530  1.1     elric 				      KRB5_PRINCIPAL_UNPARSE_NO_REALM, &name);
    531  1.1     elric 	if (ret)
    532  1.1     elric 	    goto nopassword;
    533  1.1     elric 
    534  1.1     elric 	osret = SecKeychainFindGenericPassword(NULL, strlen(realm), realm,
    535  1.1     elric 					       strlen(name), name,
    536  1.1     elric 					       &length, &buffer, NULL);
    537  1.1     elric 	free(name);
    538  1.1     elric 	if (osret == noErr && length < sizeof(passwd) - 1) {
    539  1.1     elric 	    memcpy(passwd, buffer, length);
    540  1.1     elric 	    passwd[length] = '\0';
    541  1.1     elric 	}
    542  1.1     elric     nopassword:
    543  1.1     elric 	do { } while(0);
    544  1.1     elric     }
    545  1.1     elric #endif
    546  1.1     elric 
    547  1.1     elric     memset(&cred, 0, sizeof(cred));
    548  1.1     elric 
    549  1.2  christos     ret = krb5_get_init_creds_opt_alloc(context, &opt);
    550  1.2  christos     if (ret) {
    551  1.2  christos 	krb5_warn(context, ret, "krb5_get_init_creds_opt_alloc");
    552  1.2  christos 	goto out;
    553  1.2  christos     }
    554  1.1     elric 
    555  1.1     elric     krb5_get_init_creds_opt_set_default_flags(context, "kinit",
    556  1.1     elric 	krb5_principal_get_realm(context, principal), opt);
    557  1.1     elric 
    558  1.2  christos     if (forwardable_flag != -1)
    559  1.2  christos 	krb5_get_init_creds_opt_set_forwardable(opt, forwardable_flag);
    560  1.2  christos     if (proxiable_flag != -1)
    561  1.2  christos 	krb5_get_init_creds_opt_set_proxiable(opt, proxiable_flag);
    562  1.2  christos     if (anonymous_flag)
    563  1.2  christos 	krb5_get_init_creds_opt_set_anonymous(opt, anonymous_flag);
    564  1.1     elric     if (pac_flag != -1)
    565  1.1     elric 	krb5_get_init_creds_opt_set_pac_request(context, opt,
    566  1.1     elric 						pac_flag ? TRUE : FALSE);
    567  1.1     elric     if (canonicalize_flag)
    568  1.1     elric 	krb5_get_init_creds_opt_set_canonicalize(context, opt, TRUE);
    569  1.2  christos     if (pk_enterprise_flag || enterprise_flag || canonicalize_flag || windows_flag)
    570  1.1     elric 	krb5_get_init_creds_opt_set_win2k(context, opt, TRUE);
    571  1.4  christos     if (pk_user_id || ent_user_id || anonymous_pkinit) {
    572  1.1     elric 	ret = krb5_get_init_creds_opt_set_pkinit(context, opt,
    573  1.1     elric 						 principal,
    574  1.1     elric 						 pk_user_id,
    575  1.1     elric 						 pk_x509_anchors,
    576  1.1     elric 						 NULL,
    577  1.1     elric 						 NULL,
    578  1.4  christos 						 pk_use_enckey ? KRB5_GIC_OPT_PKINIT_USE_ENCKEY : 0 |
    579  1.4  christos 						 anonymous_pkinit ? KRB5_GIC_OPT_PKINIT_ANONYMOUS : 0,
    580  1.2  christos 						 prompter,
    581  1.1     elric 						 NULL,
    582  1.1     elric 						 passwd);
    583  1.2  christos 	if (ret) {
    584  1.2  christos 	    krb5_warn(context, ret, "krb5_get_init_creds_opt_set_pkinit");
    585  1.2  christos 	    goto out;
    586  1.2  christos 	}
    587  1.1     elric 	if (ent_user_id)
    588  1.1     elric 	    krb5_get_init_creds_opt_set_pkinit_user_certs(context, opt, ent_user_id);
    589  1.1     elric     }
    590  1.1     elric 
    591  1.1     elric     if (addrs_flag != -1)
    592  1.1     elric 	krb5_get_init_creds_opt_set_addressless(context, opt,
    593  1.1     elric 						addrs_flag ? FALSE : TRUE);
    594  1.1     elric 
    595  1.1     elric     if (renew_life == NULL && renewable_flag)
    596  1.2  christos 	renewstr = "6 months";
    597  1.1     elric     if (renew_life)
    598  1.1     elric 	renewstr = renew_life;
    599  1.1     elric     if (renewstr) {
    600  1.2  christos 	renew = parse_time(renewstr, "s");
    601  1.1     elric 	if (renew < 0)
    602  1.2  christos 	    errx(1, "unparsable time: %s", renewstr);
    603  1.2  christos 
    604  1.2  christos 	krb5_get_init_creds_opt_set_renew_life(opt, renew);
    605  1.1     elric     }
    606  1.1     elric 
    607  1.2  christos     if (ticket_life != 0)
    608  1.2  christos 	krb5_get_init_creds_opt_set_tkt_life(opt, ticket_life);
    609  1.1     elric 
    610  1.2  christos     if (start_str) {
    611  1.2  christos 	int tmp = parse_time(start_str, "s");
    612  1.1     elric 	if (tmp < 0)
    613  1.2  christos 	    errx(1, N_("unparsable time: %s", ""), start_str);
    614  1.1     elric 
    615  1.1     elric 	start_time = tmp;
    616  1.1     elric     }
    617  1.1     elric 
    618  1.2  christos     if (etype_str.num_strings) {
    619  1.1     elric 	int i;
    620  1.1     elric 
    621  1.1     elric 	enctype = malloc(etype_str.num_strings * sizeof(*enctype));
    622  1.2  christos 	if (enctype == NULL)
    623  1.1     elric 	    errx(1, "out of memory");
    624  1.1     elric 	for(i = 0; i < etype_str.num_strings; i++) {
    625  1.1     elric 	    ret = krb5_string_to_enctype(context,
    626  1.1     elric 					 etype_str.strings[i],
    627  1.1     elric 					 &enctype[i]);
    628  1.2  christos 	    if (ret)
    629  1.1     elric 		errx(1, "unrecognized enctype: %s", etype_str.strings[i]);
    630  1.1     elric 	}
    631  1.1     elric 	krb5_get_init_creds_opt_set_etype_list(opt, enctype,
    632  1.1     elric 					       etype_str.num_strings);
    633  1.1     elric     }
    634  1.1     elric 
    635  1.2  christos     ret = krb5_init_creds_init(context, principal, prompter, NULL, start_time, opt, &ctx);
    636  1.2  christos     if (ret) {
    637  1.2  christos 	krb5_warn(context, ret, "krb5_init_creds_init");
    638  1.2  christos 	goto out;
    639  1.2  christos     }
    640  1.2  christos 
    641  1.2  christos     if (server_str) {
    642  1.2  christos 	ret = krb5_init_creds_set_service(context, ctx, server_str);
    643  1.2  christos 	if (ret) {
    644  1.2  christos 	    krb5_warn(context, ret, "krb5_init_creds_set_service");
    645  1.2  christos 	    goto out;
    646  1.2  christos 	}
    647  1.2  christos     }
    648  1.2  christos 
    649  1.2  christos     if (fast_armor_cache_string) {
    650  1.2  christos 	krb5_ccache fastid;
    651  1.2  christos 
    652  1.2  christos 	ret = krb5_cc_resolve(context, fast_armor_cache_string, &fastid);
    653  1.2  christos 	if (ret) {
    654  1.2  christos 	    krb5_warn(context, ret, "krb5_cc_resolve(FAST cache)");
    655  1.2  christos 	    goto out;
    656  1.2  christos 	}
    657  1.2  christos 
    658  1.2  christos 	ret = krb5_init_creds_set_fast_ccache(context, ctx, fastid);
    659  1.2  christos 	if (ret) {
    660  1.2  christos 	    krb5_warn(context, ret, "krb5_init_creds_set_fast_ccache");
    661  1.2  christos 	    goto out;
    662  1.2  christos 	}
    663  1.2  christos     }
    664  1.2  christos 
    665  1.2  christos     if (use_keytab || keytab_str) {
    666  1.2  christos 	ret = krb5_init_creds_set_keytab(context, ctx, kt);
    667  1.2  christos 	if (ret) {
    668  1.2  christos 	    krb5_warn(context, ret, "krb5_init_creds_set_keytab");
    669  1.2  christos 	    goto out;
    670  1.2  christos 	}
    671  1.4  christos     } else if (pk_user_id || ent_user_id ||
    672  1.4  christos 	       krb5_principal_is_anonymous(context, principal, KRB5_ANON_MATCH_ANY)) {
    673  1.5  christos         /* nop */;
    674  1.2  christos     } else if (!interactive && passwd[0] == '\0') {
    675  1.2  christos 	static int already_warned = 0;
    676  1.2  christos 
    677  1.2  christos 	if (!already_warned)
    678  1.2  christos 	    krb5_warnx(context, "Not interactive, failed to get "
    679  1.2  christos 	      "initial ticket");
    680  1.1     elric 	krb5_get_init_creds_opt_free(context, opt);
    681  1.2  christos 	already_warned = 1;
    682  1.1     elric 	return 0;
    683  1.1     elric     } else {
    684  1.1     elric 
    685  1.1     elric 	if (passwd[0] == '\0') {
    686  1.1     elric 	    char *p, *prompt;
    687  1.2  christos 	    int aret = 0;
    688  1.2  christos 
    689  1.2  christos 	    ret = krb5_unparse_name(context, principal, &p);
    690  1.2  christos 	    if (ret)
    691  1.2  christos 		errx(1, "failed to generate passwd prompt: not enough memory");
    692  1.2  christos 
    693  1.2  christos 	    aret = asprintf(&prompt, N_("%s's Password: ", ""), p);
    694  1.2  christos 	    free(p);
    695  1.2  christos 	    if (aret == -1)
    696  1.2  christos 		errx(1, "failed to generate passwd prompt: not enough memory");
    697  1.2  christos 
    698  1.1     elric 	    if (UI_UTIL_read_pw_string(passwd, sizeof(passwd)-1, prompt, 0)){
    699  1.1     elric 		memset(passwd, 0, sizeof(passwd));
    700  1.2  christos 		errx(1, "failed to read password");
    701  1.1     elric 	    }
    702  1.2  christos 	    free(prompt);
    703  1.1     elric 	}
    704  1.1     elric 
    705  1.2  christos 	if (passwd[0]) {
    706  1.2  christos 	    ret = krb5_init_creds_set_password(context, ctx, passwd);
    707  1.2  christos 	    if (ret) {
    708  1.2  christos 		krb5_warn(context, ret, "krb5_init_creds_set_password");
    709  1.2  christos 		goto out;
    710  1.2  christos 	    }
    711  1.2  christos 	}
    712  1.1     elric     }
    713  1.2  christos 
    714  1.2  christos     ret = krb5_init_creds_get(context, ctx);
    715  1.2  christos 
    716  1.1     elric #ifndef NO_NTLM
    717  1.1     elric     if (ntlm_domain && passwd[0])
    718  1.1     elric 	heim_ntlm_nt_key(passwd, &ntlmkey);
    719  1.1     elric #endif
    720  1.4  christos     memset_s(passwd, sizeof(passwd), 0, sizeof(passwd));
    721  1.1     elric 
    722  1.1     elric     switch(ret){
    723  1.1     elric     case 0:
    724  1.1     elric 	break;
    725  1.1     elric     case KRB5_LIBOS_PWDINTR: /* don't print anything if it was just C-c:ed */
    726  1.1     elric 	exit(1);
    727  1.1     elric     case KRB5KRB_AP_ERR_BAD_INTEGRITY:
    728  1.1     elric     case KRB5KRB_AP_ERR_MODIFIED:
    729  1.1     elric     case KRB5KDC_ERR_PREAUTH_FAILED:
    730  1.2  christos     case KRB5_GET_IN_TKT_LOOP:
    731  1.2  christos 	krb5_warnx(context, N_("Password incorrect", ""));
    732  1.2  christos 	goto out;
    733  1.1     elric     case KRB5KRB_AP_ERR_V4_REPLY:
    734  1.2  christos 	krb5_warnx(context, N_("Looks like a Kerberos 4 reply", ""));
    735  1.2  christos 	goto out;
    736  1.2  christos     case KRB5KDC_ERR_KEY_EXPIRED:
    737  1.2  christos 	krb5_warnx(context, N_("Password expired", ""));
    738  1.2  christos 	goto out;
    739  1.1     elric     default:
    740  1.2  christos 	krb5_warn(context, ret, "krb5_get_init_creds");
    741  1.2  christos 	goto out;
    742  1.2  christos     }
    743  1.2  christos 
    744  1.2  christos     krb5_process_last_request(context, opt, ctx);
    745  1.2  christos 
    746  1.2  christos     ret = krb5_init_creds_get_creds(context, ctx, &cred);
    747  1.2  christos     if (ret) {
    748  1.2  christos 	krb5_warn(context, ret, "krb5_init_creds_get_creds");
    749  1.2  christos 	goto out;
    750  1.1     elric     }
    751  1.1     elric 
    752  1.2  christos     if (ticket_life != 0) {
    753  1.3  christos 	krb5_deltat d = cred.times.endtime - cred.times.starttime;
    754  1.3  christos 	if (llabs(d - ticket_life) > 30) {
    755  1.1     elric 	    char life[64];
    756  1.3  christos 	    unparse_time_approx(d, life, sizeof(life));
    757  1.1     elric 	    krb5_warnx(context, N_("NOTICE: ticket lifetime is %s", ""), life);
    758  1.1     elric 	}
    759  1.1     elric     }
    760  1.2  christos     if (renew_life) {
    761  1.3  christos 	krb5_deltat d = cred.times.renew_till - cred.times.starttime;
    762  1.3  christos 	if (llabs(d - renew) > 30) {
    763  1.1     elric 	    char life[64];
    764  1.3  christos 	    unparse_time_approx(d, life, sizeof(life));
    765  1.2  christos 	    krb5_warnx(context,
    766  1.1     elric 		       N_("NOTICE: ticket renewable lifetime is %s", ""),
    767  1.1     elric 		       life);
    768  1.1     elric 	}
    769  1.1     elric     }
    770  1.2  christos     krb5_free_cred_contents(context, &cred);
    771  1.1     elric 
    772  1.1     elric     ret = krb5_cc_new_unique(context, krb5_cc_get_type(context, ccache),
    773  1.1     elric 			     NULL, &tempccache);
    774  1.2  christos     if (ret) {
    775  1.2  christos 	krb5_warn(context, ret, "krb5_cc_new_unique");
    776  1.2  christos 	goto out;
    777  1.2  christos     }
    778  1.1     elric 
    779  1.2  christos     ret = krb5_init_creds_store(context, ctx, tempccache);
    780  1.2  christos     if (ret) {
    781  1.2  christos 	krb5_warn(context, ret, "krb5_init_creds_store");
    782  1.2  christos 	goto out;
    783  1.2  christos     }
    784  1.1     elric 
    785  1.2  christos     krb5_init_creds_free(context, ctx);
    786  1.2  christos     ctx = NULL;
    787  1.1     elric 
    788  1.1     elric     ret = krb5_cc_move(context, tempccache, ccache);
    789  1.2  christos     if (ret) {
    790  1.2  christos 	krb5_warn(context, ret, "krb5_cc_move");
    791  1.2  christos 	goto out;
    792  1.2  christos     }
    793  1.2  christos     tempccache = NULL;
    794  1.1     elric 
    795  1.1     elric     if (switch_cache_flags)
    796  1.1     elric 	krb5_cc_switch(context, ccache);
    797  1.1     elric 
    798  1.1     elric #ifndef NO_NTLM
    799  1.1     elric     if (ntlm_domain && ntlmkey.data)
    800  1.1     elric 	store_ntlmkey(context, ccache, ntlm_domain, &ntlmkey);
    801  1.1     elric #endif
    802  1.1     elric 
    803  1.1     elric     if (ok_as_delegate_flag || windows_flag || use_referrals_flag) {
    804  1.1     elric 	unsigned char d = 0;
    805  1.1     elric 	krb5_data data;
    806  1.1     elric 
    807  1.1     elric 	if (ok_as_delegate_flag || windows_flag)
    808  1.1     elric 	    d |= 1;
    809  1.1     elric 	if (use_referrals_flag || windows_flag)
    810  1.1     elric 	    d |= 2;
    811  1.1     elric 
    812  1.1     elric 	data.length = 1;
    813  1.1     elric 	data.data = &d;
    814  1.1     elric 
    815  1.1     elric 	krb5_cc_set_config(context, ccache, NULL, "realm-config", &data);
    816  1.1     elric     }
    817  1.1     elric 
    818  1.4  christos     if (anonymous_pkinit) {
    819  1.4  christos 	krb5_data data;
    820  1.4  christos 
    821  1.4  christos 	data.length = strlen(principal->realm);
    822  1.4  christos 	data.data = principal->realm;
    823  1.4  christos 
    824  1.4  christos 	krb5_cc_set_config(context, ccache, NULL, "anon_pkinit_realm", &data);
    825  1.4  christos     }
    826  1.4  christos 
    827  1.2  christos out:
    828  1.2  christos     krb5_get_init_creds_opt_free(context, opt);
    829  1.2  christos     if (ctx)
    830  1.2  christos 	krb5_init_creds_free(context, ctx);
    831  1.2  christos     if (tempccache)
    832  1.4  christos 	krb5_cc_destroy(context, tempccache);
    833  1.1     elric 
    834  1.1     elric     if (enctype)
    835  1.1     elric 	free(enctype);
    836  1.1     elric 
    837  1.2  christos     return ret;
    838  1.1     elric }
    839  1.1     elric 
    840  1.1     elric static time_t
    841  1.2  christos ticket_lifetime(krb5_context context, krb5_ccache cache, krb5_principal client,
    842  1.2  christos 		const char *server, time_t *renew)
    843  1.1     elric {
    844  1.1     elric     krb5_creds in_cred, *cred;
    845  1.1     elric     krb5_error_code ret;
    846  1.1     elric     time_t timeout;
    847  1.2  christos     time_t curtime;
    848  1.1     elric 
    849  1.1     elric     memset(&in_cred, 0, sizeof(in_cred));
    850  1.1     elric 
    851  1.2  christos     if (renew != NULL)
    852  1.2  christos         *renew = 0;
    853  1.2  christos 
    854  1.1     elric     ret = krb5_cc_get_principal(context, cache, &in_cred.client);
    855  1.2  christos     if (ret) {
    856  1.1     elric 	krb5_warn(context, ret, "krb5_cc_get_principal");
    857  1.1     elric 	return 0;
    858  1.1     elric     }
    859  1.1     elric     ret = get_server(context, in_cred.client, server, &in_cred.server);
    860  1.2  christos     if (ret) {
    861  1.1     elric 	krb5_free_principal(context, in_cred.client);
    862  1.1     elric 	krb5_warn(context, ret, "get_server");
    863  1.1     elric 	return 0;
    864  1.1     elric     }
    865  1.1     elric 
    866  1.1     elric     ret = krb5_get_credentials(context, KRB5_GC_CACHED,
    867  1.1     elric 			       cache, &in_cred, &cred);
    868  1.1     elric     krb5_free_principal(context, in_cred.client);
    869  1.1     elric     krb5_free_principal(context, in_cred.server);
    870  1.2  christos     if (ret) {
    871  1.1     elric 	krb5_warn(context, ret, "krb5_get_credentials");
    872  1.1     elric 	return 0;
    873  1.1     elric     }
    874  1.2  christos     curtime = time(NULL);
    875  1.2  christos     timeout = cred->times.endtime - curtime;
    876  1.1     elric     if (timeout < 0)
    877  1.1     elric 	timeout = 0;
    878  1.2  christos     if (renew) {
    879  1.2  christos 	*renew = cred->times.renew_till - curtime;
    880  1.2  christos 	if (*renew < 0)
    881  1.2  christos 	    *renew = 0;
    882  1.2  christos     }
    883  1.1     elric     krb5_free_creds(context, cred);
    884  1.1     elric     return timeout;
    885  1.1     elric }
    886  1.1     elric 
    887  1.2  christos static time_t expire;
    888  1.2  christos 
    889  1.2  christos static char siginfo_msg[1024] = "No credentials\n";
    890  1.2  christos 
    891  1.2  christos static void
    892  1.2  christos update_siginfo_msg(time_t exp, const char *srv)
    893  1.2  christos {
    894  1.2  christos     /* Note that exp is relative time */
    895  1.2  christos     memset(siginfo_msg, 0, sizeof(siginfo_msg));
    896  1.2  christos     memcpy(&siginfo_msg, "Updating...\n", sizeof("Updating...\n"));
    897  1.2  christos     if (exp) {
    898  1.2  christos 	if (srv == NULL) {
    899  1.2  christos 	    snprintf(siginfo_msg, sizeof(siginfo_msg),
    900  1.2  christos 		     N_("kinit: TGT expires in %llu seconds\n", ""),
    901  1.2  christos 		     (unsigned long long)expire);
    902  1.2  christos 	} else {
    903  1.2  christos 	    snprintf(siginfo_msg, sizeof(siginfo_msg),
    904  1.2  christos 		     N_("kinit: Ticket for %s expired\n", ""), srv);
    905  1.2  christos 	}
    906  1.2  christos 	return;
    907  1.2  christos     }
    908  1.2  christos 
    909  1.2  christos     /* Expired creds */
    910  1.2  christos     if (srv == NULL) {
    911  1.2  christos 	snprintf(siginfo_msg, sizeof(siginfo_msg),
    912  1.2  christos 		 N_("kinit: TGT expired\n", ""));
    913  1.2  christos     } else {
    914  1.2  christos 	snprintf(siginfo_msg, sizeof(siginfo_msg),
    915  1.2  christos 		 N_("kinit: Ticket for %s expired\n", ""), srv);
    916  1.2  christos     }
    917  1.2  christos }
    918  1.2  christos 
    919  1.2  christos #ifdef HAVE_SIGACTION
    920  1.2  christos static void
    921  1.2  christos handle_siginfo(int sig)
    922  1.2  christos {
    923  1.2  christos     struct iovec iov[2];
    924  1.2  christos 
    925  1.2  christos     iov[0].iov_base = rk_UNCONST(siginfo_msg);
    926  1.2  christos     iov[0].iov_len = strlen(siginfo_msg);
    927  1.2  christos     iov[1].iov_base = "\n";
    928  1.2  christos     iov[1].iov_len = 1;
    929  1.2  christos 
    930  1.2  christos     writev(STDERR_FILENO, iov, sizeof(iov)/sizeof(iov[0]));
    931  1.2  christos }
    932  1.2  christos #endif
    933  1.2  christos 
    934  1.1     elric struct renew_ctx {
    935  1.1     elric     krb5_context context;
    936  1.1     elric     krb5_ccache  ccache;
    937  1.1     elric     krb5_principal principal;
    938  1.1     elric     krb5_deltat ticket_life;
    939  1.2  christos     krb5_deltat timeout;
    940  1.1     elric };
    941  1.1     elric 
    942  1.1     elric static time_t
    943  1.1     elric renew_func(void *ptr)
    944  1.1     elric {
    945  1.2  christos     krb5_error_code ret;
    946  1.1     elric     struct renew_ctx *ctx = ptr;
    947  1.2  christos     time_t renew_expire;
    948  1.2  christos     static time_t exp_delay = 1;
    949  1.2  christos 
    950  1.2  christos     /*
    951  1.2  christos      * NOTE: We count on the ccache implementation to notice changes to the
    952  1.2  christos      * actual ccache filesystem/whatever objects.  There should be no ccache
    953  1.2  christos      * types for which this is not the case, but it might not hurt to
    954  1.2  christos      * re-krb5_cc_resolve() after each successful renew_validate()/
    955  1.2  christos      * get_new_tickets() call.
    956  1.2  christos      */
    957  1.2  christos 
    958  1.2  christos     expire = ticket_lifetime(ctx->context, ctx->ccache, ctx->principal,
    959  1.2  christos 			     server_str, &renew_expire);
    960  1.1     elric 
    961  1.2  christos     /*
    962  1.2  christos      * When a keytab is available to obtain new tickets, if we are within
    963  1.2  christos      * half of the original ticket lifetime of the renew limit, get a new
    964  1.2  christos      * TGT instead of renewing the existing TGT.  Note, ctx->ticket_life
    965  1.2  christos      * is zero by default (without a '-l' option) and cannot be used to
    966  1.2  christos      * set the time scale on which we decide whether we're "close to the
    967  1.2  christos      * renew limit".
    968  1.2  christos      */
    969  1.2  christos     if (use_keytab || keytab_str)
    970  1.2  christos 	expire += ctx->timeout;
    971  1.2  christos     if (renew_expire > expire) {
    972  1.2  christos 	ret = renew_validate(ctx->context, 1, validate_flag, ctx->ccache,
    973  1.2  christos 		       server_str, ctx->ticket_life);
    974  1.2  christos     } else {
    975  1.2  christos 	ret = get_new_tickets(ctx->context, ctx->principal, ctx->ccache,
    976  1.4  christos 			      ctx->ticket_life, 0, 0);
    977  1.2  christos     }
    978  1.2  christos     expire = ticket_lifetime(ctx->context, ctx->ccache, ctx->principal,
    979  1.2  christos 			     server_str, &renew_expire);
    980  1.1     elric 
    981  1.1     elric #ifndef NO_AFS
    982  1.2  christos     if (ret == 0 && server_str == NULL && do_afslog && k_hasafs())
    983  1.1     elric 	krb5_afslog(ctx->context, ctx->ccache, NULL, NULL);
    984  1.1     elric #endif
    985  1.1     elric 
    986  1.2  christos     update_siginfo_msg(expire, server_str);
    987  1.2  christos 
    988  1.2  christos     /*
    989  1.2  christos      * If our tickets have expired and we been able to either renew them
    990  1.2  christos      * or obtain new tickets, then we still call this function but we use
    991  1.2  christos      * an exponential backoff.  This should take care of the case where
    992  1.2  christos      * we are using stored credentials but the KDC has been unavailable
    993  1.2  christos      * for some reason...
    994  1.2  christos      */
    995  1.2  christos 
    996  1.2  christos     if (expire < 1) {
    997  1.2  christos 	/*
    998  1.2  christos 	 * We can't ask to keep spamming stderr but not syslog, so we warn
    999  1.2  christos 	 * only once.
   1000  1.2  christos 	 */
   1001  1.2  christos 	if (exp_delay == 1) {
   1002  1.2  christos 	    krb5_warnx(ctx->context, N_("NOTICE: Could not renew/refresh "
   1003  1.2  christos 					"tickets", ""));
   1004  1.2  christos 	}
   1005  1.2  christos         if (exp_delay < 7200)
   1006  1.2  christos 	    exp_delay += exp_delay / 2 + 1;
   1007  1.2  christos 	return exp_delay;
   1008  1.2  christos     }
   1009  1.2  christos     exp_delay = 1;
   1010  1.2  christos 
   1011  1.2  christos     return expire / 2 + 1;
   1012  1.2  christos }
   1013  1.2  christos 
   1014  1.2  christos static void
   1015  1.2  christos set_princ_realm(krb5_context context,
   1016  1.2  christos 		krb5_principal principal,
   1017  1.2  christos 		const char *realm)
   1018  1.2  christos {
   1019  1.2  christos     krb5_error_code ret;
   1020  1.2  christos 
   1021  1.2  christos     if ((ret = krb5_principal_set_realm(context, principal, realm)) != 0)
   1022  1.2  christos 	krb5_err(context, 1, ret, "krb5_principal_set_realm");
   1023  1.2  christos }
   1024  1.2  christos 
   1025  1.2  christos static void
   1026  1.2  christos parse_name_realm(krb5_context context,
   1027  1.2  christos 		 const char *name,
   1028  1.2  christos 		 int flags,
   1029  1.2  christos 		 const char *realm,
   1030  1.2  christos 		 krb5_principal *princ)
   1031  1.2  christos {
   1032  1.2  christos     krb5_error_code ret;
   1033  1.2  christos 
   1034  1.2  christos     if (realm)
   1035  1.2  christos 	flags |= KRB5_PRINCIPAL_PARSE_NO_DEF_REALM;
   1036  1.2  christos     if ((ret = krb5_parse_name_flags(context, name, flags, princ)) != 0)
   1037  1.2  christos 	krb5_err(context, 1, ret, "krb5_parse_name_flags");
   1038  1.2  christos     if (realm && krb5_principal_get_realm(context, *princ) == NULL)
   1039  1.2  christos 	set_princ_realm(context, *princ, realm);
   1040  1.2  christos }
   1041  1.2  christos 
   1042  1.2  christos static char *
   1043  1.2  christos get_default_realm(krb5_context context)
   1044  1.2  christos {
   1045  1.2  christos     char *realm;
   1046  1.2  christos     krb5_error_code ret;
   1047  1.2  christos 
   1048  1.2  christos     if ((ret = krb5_get_default_realm(context, &realm)) != 0)
   1049  1.2  christos         krb5_err(context, 1, ret, "krb5_get_default_realm");
   1050  1.2  christos     return realm;
   1051  1.2  christos }
   1052  1.2  christos 
   1053  1.2  christos static void
   1054  1.2  christos get_default_principal(krb5_context context, krb5_principal *princ)
   1055  1.2  christos {
   1056  1.2  christos     krb5_error_code ret;
   1057  1.2  christos 
   1058  1.2  christos     if ((ret = krb5_get_default_principal(context, princ)) != 0)
   1059  1.2  christos 	krb5_err(context, 1, ret, "krb5_get_default_principal");
   1060  1.2  christos }
   1061  1.2  christos 
   1062  1.2  christos static char *
   1063  1.2  christos get_user_realm(krb5_context context)
   1064  1.2  christos {
   1065  1.2  christos     krb5_error_code ret;
   1066  1.2  christos     char *user_realm = NULL;
   1067  1.2  christos 
   1068  1.2  christos     /*
   1069  1.2  christos      * If memory allocation fails, we don't try to use the wrong realm,
   1070  1.2  christos      * that will trigger misleading error messages complicate support.
   1071  1.2  christos      */
   1072  1.2  christos     krb5_appdefault_string(context, "kinit", NULL, "user_realm", "",
   1073  1.2  christos 			   &user_realm);
   1074  1.2  christos     if (user_realm == NULL) {
   1075  1.2  christos 	ret = krb5_enomem(context);
   1076  1.2  christos 	krb5_err(context, 1, ret, "krb5_appdefault_string");
   1077  1.2  christos     }
   1078  1.2  christos 
   1079  1.2  christos     if (*user_realm == 0) {
   1080  1.2  christos 	free(user_realm);
   1081  1.2  christos 	user_realm = NULL;
   1082  1.2  christos     }
   1083  1.2  christos 
   1084  1.2  christos     return user_realm;
   1085  1.2  christos }
   1086  1.2  christos 
   1087  1.2  christos static void
   1088  1.2  christos get_princ(krb5_context context, krb5_principal *principal, const char *name)
   1089  1.2  christos {
   1090  1.2  christos     krb5_error_code ret;
   1091  1.2  christos     krb5_principal tmp;
   1092  1.2  christos     int parseflags = 0;
   1093  1.2  christos     char *user_realm;
   1094  1.2  christos 
   1095  1.2  christos     if (name == NULL) {
   1096  1.2  christos 	krb5_ccache ccache;
   1097  1.2  christos 
   1098  1.2  christos 	/* If credential cache provides a client principal, use that. */
   1099  1.2  christos 	if (krb5_cc_default(context, &ccache) == 0) {
   1100  1.2  christos 	    ret = krb5_cc_get_principal(context, ccache, principal);
   1101  1.2  christos 	    krb5_cc_close(context, ccache);
   1102  1.2  christos 	    if (ret == 0)
   1103  1.2  christos 		return;
   1104  1.2  christos 	}
   1105  1.2  christos     }
   1106  1.2  christos 
   1107  1.2  christos     user_realm = get_user_realm(context);
   1108  1.2  christos 
   1109  1.2  christos     if (name) {
   1110  1.2  christos 	if (canonicalize_flag || enterprise_flag)
   1111  1.2  christos 	    parseflags |= KRB5_PRINCIPAL_PARSE_ENTERPRISE;
   1112  1.2  christos 
   1113  1.2  christos 	parse_name_realm(context, name, parseflags, user_realm, &tmp);
   1114  1.2  christos 
   1115  1.2  christos 	if (user_realm && krb5_principal_get_num_comp(context, tmp) > 1) {
   1116  1.2  christos 	    /* Principal is instance qualified, reparse with default realm. */
   1117  1.2  christos 	    krb5_free_principal(context, tmp);
   1118  1.2  christos 	    parse_name_realm(context, name, parseflags, NULL, principal);
   1119  1.2  christos 	} else {
   1120  1.2  christos 	    *principal = tmp;
   1121  1.2  christos 	}
   1122  1.2  christos     } else {
   1123  1.2  christos 	get_default_principal(context, principal);
   1124  1.2  christos 	if (user_realm)
   1125  1.2  christos 	    set_princ_realm(context, *principal, user_realm);
   1126  1.2  christos     }
   1127  1.2  christos 
   1128  1.2  christos     if (user_realm)
   1129  1.2  christos 	free(user_realm);
   1130  1.2  christos }
   1131  1.2  christos 
   1132  1.2  christos static void
   1133  1.2  christos get_princ_kt(krb5_context context,
   1134  1.2  christos 	     krb5_principal *principal,
   1135  1.2  christos 	     char *name)
   1136  1.2  christos {
   1137  1.2  christos     krb5_error_code ret;
   1138  1.2  christos     krb5_principal tmp;
   1139  1.2  christos     krb5_ccache ccache;
   1140  1.2  christos     krb5_kt_cursor cursor;
   1141  1.2  christos     krb5_keytab_entry entry;
   1142  1.2  christos     char *def_realm;
   1143  1.2  christos 
   1144  1.2  christos     if (name == NULL) {
   1145  1.2  christos 	/*
   1146  1.2  christos 	 * If the credential cache exists and specifies a client principal,
   1147  1.2  christos 	 * use that.
   1148  1.2  christos 	 */
   1149  1.2  christos 	if (krb5_cc_default(context, &ccache) == 0) {
   1150  1.2  christos 	    ret = krb5_cc_get_principal(context, ccache, principal);
   1151  1.2  christos 	    krb5_cc_close(context, ccache);
   1152  1.2  christos 	    if (ret == 0)
   1153  1.2  christos 		return;
   1154  1.2  christos 	}
   1155  1.2  christos     }
   1156  1.2  christos 
   1157  1.2  christos     if (name) {
   1158  1.2  christos 	/* If the principal specifies an explicit realm, just use that. */
   1159  1.2  christos 	int parseflags = KRB5_PRINCIPAL_PARSE_NO_DEF_REALM;
   1160  1.2  christos 
   1161  1.2  christos 	parse_name_realm(context, name, parseflags, NULL, &tmp);
   1162  1.2  christos 	if (krb5_principal_get_realm(context, tmp) != NULL) {
   1163  1.2  christos 	    *principal = tmp;
   1164  1.2  christos 	    return;
   1165  1.2  christos 	}
   1166  1.2  christos     } else {
   1167  1.2  christos 	/* Otherwise, search keytab for bare name of the default principal. */
   1168  1.2  christos 	get_default_principal(context, &tmp);
   1169  1.2  christos 	set_princ_realm(context, tmp, NULL);
   1170  1.2  christos     }
   1171  1.2  christos 
   1172  1.2  christos     def_realm = get_default_realm(context);
   1173  1.2  christos 
   1174  1.2  christos     ret = krb5_kt_start_seq_get(context, kt, &cursor);
   1175  1.2  christos     if (ret)
   1176  1.2  christos 	krb5_err(context, 1, ret, "krb5_kt_start_seq_get");
   1177  1.2  christos 
   1178  1.2  christos     while (ret == 0 &&
   1179  1.2  christos            krb5_kt_next_entry(context, kt, &entry, &cursor) == 0) {
   1180  1.2  christos         const char *realm;
   1181  1.2  christos 
   1182  1.2  christos         if (!krb5_principal_compare_any_realm(context, tmp, entry.principal))
   1183  1.2  christos             continue;
   1184  1.2  christos         if (*principal &&
   1185  1.2  christos 	    krb5_principal_compare(context, *principal, entry.principal))
   1186  1.2  christos             continue;
   1187  1.2  christos         /* The default realm takes precedence */
   1188  1.2  christos         realm = krb5_principal_get_realm(context, entry.principal);
   1189  1.2  christos         if (*principal && strcmp(def_realm, realm) == 0) {
   1190  1.2  christos             krb5_free_principal(context, *principal);
   1191  1.2  christos             ret = krb5_copy_principal(context, entry.principal, principal);
   1192  1.2  christos             break;
   1193  1.2  christos         }
   1194  1.2  christos         if (!*principal)
   1195  1.2  christos             ret = krb5_copy_principal(context, entry.principal, principal);
   1196  1.2  christos     }
   1197  1.2  christos     if (ret != 0 || (ret = krb5_kt_end_seq_get(context, kt, &cursor)) != 0)
   1198  1.2  christos 	krb5_err(context, 1, ret, "get_princ_kt");
   1199  1.2  christos     if (!*principal) {
   1200  1.2  christos 	if (name)
   1201  1.2  christos 	    parse_name_realm(context, name, 0, NULL, principal);
   1202  1.2  christos 	else
   1203  1.2  christos 	    krb5_err(context, 1, KRB5_CC_NOTFOUND, "get_princ_kt");
   1204  1.2  christos     }
   1205  1.2  christos 
   1206  1.2  christos     krb5_free_principal(context, tmp);
   1207  1.2  christos     free(def_realm);
   1208  1.2  christos }
   1209  1.2  christos 
   1210  1.2  christos static krb5_error_code
   1211  1.2  christos get_switched_ccache(krb5_context context,
   1212  1.2  christos 		    const char * type,
   1213  1.2  christos 		    krb5_principal principal,
   1214  1.2  christos 		    krb5_ccache *ccache)
   1215  1.2  christos {
   1216  1.2  christos     krb5_error_code ret;
   1217  1.2  christos 
   1218  1.2  christos #ifdef _WIN32
   1219  1.2  christos     if (strcmp(type, "API") == 0) {
   1220  1.2  christos 	/*
   1221  1.2  christos 	 * Windows stores the default ccache name in the
   1222  1.2  christos 	 * registry which is shared across multiple logon
   1223  1.2  christos 	 * sessions for the same user.  The API credential
   1224  1.2  christos 	 * cache provides a unique name space per logon
   1225  1.2  christos 	 * session.  Therefore there is no need to generate
   1226  1.2  christos 	 * a unique ccache name.  Instead use the principal
   1227  1.2  christos 	 * name.  This provides a friendlier user experience.
   1228  1.2  christos 	 */
   1229  1.2  christos 	char * unparsed_name;
   1230  1.2  christos 	char * cred_cache;
   1231  1.2  christos 
   1232  1.2  christos 	ret = krb5_unparse_name(context, principal,
   1233  1.2  christos 				&unparsed_name);
   1234  1.2  christos 	if (ret)
   1235  1.2  christos 	    krb5_err(context, 1, ret,
   1236  1.2  christos 		     N_("unparsing principal name", ""));
   1237  1.2  christos 
   1238  1.2  christos 	ret = asprintf(&cred_cache, "API:%s", unparsed_name);
   1239  1.2  christos 	krb5_free_unparsed_name(context, unparsed_name);
   1240  1.2  christos 	if (ret == -1 || cred_cache == NULL)
   1241  1.2  christos 	    krb5_err(context, 1, ret,
   1242  1.2  christos 		      N_("building credential cache name", ""));
   1243  1.2  christos 
   1244  1.2  christos 	ret = krb5_cc_resolve(context, cred_cache, ccache);
   1245  1.2  christos 	free(cred_cache);
   1246  1.2  christos     } else if (strcmp(type, "MSLSA") == 0) {
   1247  1.2  christos 	/*
   1248  1.2  christos 	 * The Windows MSLSA cache when it is writeable
   1249  1.2  christos 	 * stores tickets for multiple client principals
   1250  1.2  christos 	 * in a single credential cache.
   1251  1.2  christos 	 */
   1252  1.2  christos 	ret = krb5_cc_resolve(context, "MSLSA:", ccache);
   1253  1.2  christos     } else {
   1254  1.2  christos 	ret = krb5_cc_new_unique(context, type, NULL, ccache);
   1255  1.2  christos     }
   1256  1.2  christos #else /* !_WIN32 */
   1257  1.2  christos     ret = krb5_cc_new_unique(context, type, NULL, ccache);
   1258  1.2  christos #endif /* _WIN32 */
   1259  1.2  christos 
   1260  1.2  christos     return ret;
   1261  1.1     elric }
   1262  1.1     elric 
   1263  1.1     elric int
   1264  1.2  christos main(int argc, char **argv)
   1265  1.1     elric {
   1266  1.1     elric     krb5_error_code ret;
   1267  1.1     elric     krb5_context context;
   1268  1.1     elric     krb5_ccache  ccache;
   1269  1.2  christos     krb5_principal principal = NULL;
   1270  1.1     elric     int optidx = 0;
   1271  1.1     elric     krb5_deltat ticket_life = 0;
   1272  1.2  christos #ifdef HAVE_SIGACTION
   1273  1.2  christos     struct sigaction sa;
   1274  1.2  christos #endif
   1275  1.4  christos     krb5_boolean unique_ccache = FALSE;
   1276  1.5  christos     krb5_boolean historical_anon_pkinit = FALSE;
   1277  1.4  christos     int anonymous_pkinit = FALSE;
   1278  1.1     elric 
   1279  1.2  christos     setprogname(argv[0]);
   1280  1.1     elric 
   1281  1.2  christos     setlocale(LC_ALL, "");
   1282  1.2  christos     bindtextdomain("heimdal_kuser", HEIMDAL_LOCALEDIR);
   1283  1.1     elric     textdomain("heimdal_kuser");
   1284  1.1     elric 
   1285  1.2  christos     ret = krb5_init_context(&context);
   1286  1.1     elric     if (ret == KRB5_CONFIG_BADFORMAT)
   1287  1.2  christos 	errx(1, "krb5_init_context failed to parse configuration file");
   1288  1.1     elric     else if (ret)
   1289  1.1     elric 	errx(1, "krb5_init_context failed: %d", ret);
   1290  1.1     elric 
   1291  1.2  christos     if (getarg(args, sizeof(args) / sizeof(args[0]), argc, argv, &optidx))
   1292  1.1     elric 	usage(1);
   1293  1.1     elric 
   1294  1.1     elric     if (help_flag)
   1295  1.2  christos 	usage(0);
   1296  1.1     elric 
   1297  1.2  christos     if (version_flag) {
   1298  1.1     elric 	print_version(NULL);
   1299  1.1     elric 	exit(0);
   1300  1.1     elric     }
   1301  1.1     elric 
   1302  1.1     elric     argc -= optidx;
   1303  1.1     elric     argv += optidx;
   1304  1.1     elric 
   1305  1.5  christos     krb5_appdefault_boolean(context, "kinit", NULL, "historical_anon_pkinit",
   1306  1.5  christos                             FALSE, &historical_anon_pkinit);
   1307  1.5  christos 
   1308  1.2  christos     /*
   1309  1.2  christos      * Open the keytab now, we use the keytab to determine the principal's
   1310  1.2  christos      * realm when the requested principal has no realm.
   1311  1.2  christos      */
   1312  1.2  christos     if (use_keytab || keytab_str) {
   1313  1.2  christos 	if (keytab_str)
   1314  1.2  christos 	    ret = krb5_kt_resolve(context, keytab_str, &kt);
   1315  1.2  christos 	else
   1316  1.2  christos 	    ret = krb5_kt_default(context, &kt);
   1317  1.2  christos 	if (ret)
   1318  1.2  christos 	    krb5_err(context, 1, ret, "resolving keytab");
   1319  1.2  christos     }
   1320  1.1     elric 
   1321  1.1     elric     if (pk_enterprise_flag) {
   1322  1.1     elric 	ret = krb5_pk_enterprise_cert(context, pk_user_id,
   1323  1.1     elric 				      argv[0], &principal,
   1324  1.1     elric 				      &ent_user_id);
   1325  1.1     elric 	if (ret)
   1326  1.1     elric 	    krb5_err(context, 1, ret, "krb5_pk_enterprise_certs");
   1327  1.1     elric 
   1328  1.1     elric 	pk_user_id = NULL;
   1329  1.1     elric 
   1330  1.4  christos     } else if (anonymous_flag && argc && argv[0][0] == '@') {
   1331  1.4  christos 	/* If principal argument as @REALM, try anonymous PKINIT */
   1332  1.1     elric 
   1333  1.4  christos 	ret = krb5_make_principal(context, &principal, &argv[0][1],
   1334  1.2  christos 				  KRB5_WELLKNOWN_NAME, KRB5_ANON_NAME,
   1335  1.1     elric 				  NULL);
   1336  1.1     elric 	if (ret)
   1337  1.1     elric 	    krb5_err(context, 1, ret, "krb5_make_principal");
   1338  1.1     elric 	krb5_principal_set_type(context, principal, KRB5_NT_WELLKNOWN);
   1339  1.4  christos 	anonymous_pkinit = TRUE;
   1340  1.5  christos     } else if (anonymous_flag && historical_anon_pkinit) {
   1341  1.5  christos         char *realm = argc == 0 ? get_default_realm(context) :
   1342  1.5  christos                       argv[0][0] == '@' ? &argv[0][1] : argv[0];
   1343  1.5  christos 
   1344  1.5  christos 	ret = krb5_make_principal(context, &principal, realm,
   1345  1.5  christos 				  KRB5_WELLKNOWN_NAME, KRB5_ANON_NAME, NULL);
   1346  1.5  christos 	if (ret)
   1347  1.5  christos 	    krb5_err(context, 1, ret, "krb5_make_principal");
   1348  1.5  christos 	krb5_principal_set_type(context, principal, KRB5_NT_WELLKNOWN);
   1349  1.5  christos 	anonymous_pkinit = TRUE;
   1350  1.2  christos     } else if (use_keytab || keytab_str) {
   1351  1.2  christos 	get_princ_kt(context, &principal, argv[0]);
   1352  1.1     elric     } else {
   1353  1.2  christos 	get_princ(context, &principal, argv[0]);
   1354  1.1     elric     }
   1355  1.1     elric 
   1356  1.2  christos     if (fcache_version)
   1357  1.1     elric 	krb5_set_fcache_version(context, fcache_version);
   1358  1.1     elric 
   1359  1.2  christos     if (renewable_flag == -1)
   1360  1.1     elric 	/* this seems somewhat pointless, but whatever */
   1361  1.1     elric 	krb5_appdefault_boolean(context, "kinit",
   1362  1.1     elric 				krb5_principal_get_realm(context, principal),
   1363  1.1     elric 				"renewable", FALSE, &renewable_flag);
   1364  1.2  christos     if (do_afslog == -1)
   1365  1.1     elric 	krb5_appdefault_boolean(context, "kinit",
   1366  1.1     elric 				krb5_principal_get_realm(context, principal),
   1367  1.1     elric 				"afslog", TRUE, &do_afslog);
   1368  1.1     elric 
   1369  1.2  christos     if (cred_cache)
   1370  1.1     elric 	ret = krb5_cc_resolve(context, cred_cache, &ccache);
   1371  1.1     elric     else {
   1372  1.2  christos 	if (argc > 1) {
   1373  1.1     elric 	    char s[1024];
   1374  1.1     elric 	    ret = krb5_cc_new_unique(context, NULL, NULL, &ccache);
   1375  1.2  christos 	    if (ret)
   1376  1.1     elric 		krb5_err(context, 1, ret, "creating cred cache");
   1377  1.1     elric 	    snprintf(s, sizeof(s), "%s:%s",
   1378  1.1     elric 		     krb5_cc_get_type(context, ccache),
   1379  1.1     elric 		     krb5_cc_get_name(context, ccache));
   1380  1.1     elric 	    setenv("KRB5CCNAME", s, 1);
   1381  1.4  christos 	    unique_ccache = TRUE;
   1382  1.1     elric 	} else {
   1383  1.1     elric 	    ret = krb5_cc_cache_match(context, principal, &ccache);
   1384  1.1     elric 	    if (ret) {
   1385  1.1     elric 		const char *type;
   1386  1.2  christos 		ret = krb5_cc_default(context, &ccache);
   1387  1.1     elric 		if (ret)
   1388  1.2  christos 		    krb5_err(context, 1, ret,
   1389  1.2  christos 			     N_("resolving credentials cache", ""));
   1390  1.1     elric 
   1391  1.2  christos 		/*
   1392  1.1     elric 		 * Check if the type support switching, and we do,
   1393  1.1     elric 		 * then do that instead over overwriting the current
   1394  1.1     elric 		 * default credential
   1395  1.1     elric 		 */
   1396  1.1     elric 		type = krb5_cc_get_type(context, ccache);
   1397  1.1     elric 		if (krb5_cc_support_switch(context, type)) {
   1398  1.1     elric 		    krb5_cc_close(context, ccache);
   1399  1.2  christos 		    ret = get_switched_ccache(context, type, principal,
   1400  1.2  christos 					      &ccache);
   1401  1.4  christos 		    if (ret == 0)
   1402  1.4  christos 			unique_ccache = TRUE;
   1403  1.1     elric 		}
   1404  1.1     elric 	    }
   1405  1.1     elric 	}
   1406  1.1     elric     }
   1407  1.1     elric     if (ret)
   1408  1.2  christos 	krb5_err(context, 1, ret, N_("resolving credentials cache", ""));
   1409  1.1     elric 
   1410  1.1     elric #ifndef NO_AFS
   1411  1.2  christos     if (argc > 1 && k_hasafs())
   1412  1.1     elric 	k_setpag();
   1413  1.1     elric #endif
   1414  1.1     elric 
   1415  1.1     elric     if (lifetime) {
   1416  1.2  christos 	int tmp = parse_time(lifetime, "s");
   1417  1.1     elric 	if (tmp < 0)
   1418  1.2  christos 	    errx(1, N_("unparsable time: %s", ""), lifetime);
   1419  1.1     elric 
   1420  1.1     elric 	ticket_life = tmp;
   1421  1.1     elric     }
   1422  1.1     elric 
   1423  1.2  christos     if (addrs_flag == 0 && extra_addresses.num_strings > 0)
   1424  1.1     elric 	krb5_errx(context, 1,
   1425  1.1     elric 		  N_("specifying both extra addresses and "
   1426  1.1     elric 		     "no addresses makes no sense", ""));
   1427  1.1     elric     {
   1428  1.1     elric 	int i;
   1429  1.1     elric 	krb5_addresses addresses;
   1430  1.1     elric 	memset(&addresses, 0, sizeof(addresses));
   1431  1.1     elric 	for(i = 0; i < extra_addresses.num_strings; i++) {
   1432  1.1     elric 	    ret = krb5_parse_address(context, extra_addresses.strings[i],
   1433  1.1     elric 				     &addresses);
   1434  1.1     elric 	    if (ret == 0) {
   1435  1.1     elric 		krb5_add_extra_addresses(context, &addresses);
   1436  1.1     elric 		krb5_free_addresses(context, &addresses);
   1437  1.1     elric 	    }
   1438  1.1     elric 	}
   1439  1.1     elric 	free_getarg_strings(&extra_addresses);
   1440  1.1     elric     }
   1441  1.1     elric 
   1442  1.2  christos     if (renew_flag || validate_flag) {
   1443  1.1     elric 	ret = renew_validate(context, renew_flag, validate_flag,
   1444  1.1     elric 			     ccache, server_str, ticket_life);
   1445  1.2  christos 
   1446  1.2  christos #ifndef NO_AFS
   1447  1.2  christos 	if (ret == 0 && server_str == NULL && do_afslog && k_hasafs())
   1448  1.2  christos 	    krb5_afslog(context, ccache, NULL, NULL);
   1449  1.2  christos #endif
   1450  1.2  christos 
   1451  1.4  christos 	if (unique_ccache)
   1452  1.4  christos 	    krb5_cc_destroy(context, ccache);
   1453  1.1     elric 	exit(ret != 0);
   1454  1.1     elric     }
   1455  1.1     elric 
   1456  1.4  christos     ret = get_new_tickets(context, principal, ccache, ticket_life,
   1457  1.4  christos 			  1, anonymous_pkinit);
   1458  1.4  christos     if (ret) {
   1459  1.4  christos 	if (unique_ccache)
   1460  1.4  christos 	    krb5_cc_destroy(context, ccache);
   1461  1.2  christos 	exit(1);
   1462  1.4  christos     }
   1463  1.1     elric 
   1464  1.1     elric #ifndef NO_AFS
   1465  1.2  christos     if (ret == 0 && server_str == NULL && do_afslog && k_hasafs())
   1466  1.1     elric 	krb5_afslog(context, ccache, NULL, NULL);
   1467  1.1     elric #endif
   1468  1.2  christos 
   1469  1.2  christos     if (argc > 1) {
   1470  1.1     elric 	struct renew_ctx ctx;
   1471  1.1     elric 	time_t timeout;
   1472  1.1     elric 
   1473  1.2  christos 	timeout = ticket_lifetime(context, ccache, principal,
   1474  1.2  christos 				  server_str, NULL) / 2;
   1475  1.1     elric 
   1476  1.1     elric 	ctx.context = context;
   1477  1.1     elric 	ctx.ccache = ccache;
   1478  1.1     elric 	ctx.principal = principal;
   1479  1.1     elric 	ctx.ticket_life = ticket_life;
   1480  1.2  christos 	ctx.timeout = timeout;
   1481  1.2  christos 
   1482  1.2  christos #ifdef HAVE_SIGACTION
   1483  1.2  christos 	memset(&sa, 0, sizeof(sa));
   1484  1.2  christos 	sigemptyset(&sa.sa_mask);
   1485  1.2  christos 	sa.sa_handler = handle_siginfo;
   1486  1.2  christos 
   1487  1.2  christos 	sigaction(SIGINFO, &sa, NULL);
   1488  1.2  christos #endif
   1489  1.1     elric 
   1490  1.1     elric 	ret = simple_execvp_timed(argv[1], argv+1,
   1491  1.1     elric 				  renew_func, &ctx, timeout);
   1492  1.1     elric #define EX_NOEXEC	126
   1493  1.1     elric #define EX_NOTFOUND	127
   1494  1.2  christos 	if (ret == EX_NOEXEC)
   1495  1.1     elric 	    krb5_warnx(context, N_("permission denied: %s", ""), argv[1]);
   1496  1.2  christos 	else if (ret == EX_NOTFOUND)
   1497  1.1     elric 	    krb5_warnx(context, N_("command not found: %s", ""), argv[1]);
   1498  1.2  christos 
   1499  1.1     elric 	krb5_cc_destroy(context, ccache);
   1500  1.1     elric #ifndef NO_AFS
   1501  1.2  christos 	if (k_hasafs())
   1502  1.1     elric 	    k_unlog();
   1503  1.1     elric #endif
   1504  1.1     elric     } else {
   1505  1.2  christos 	krb5_cc_close(context, ccache);
   1506  1.1     elric 	ret = 0;
   1507  1.1     elric     }
   1508  1.1     elric     krb5_free_principal(context, principal);
   1509  1.2  christos     if (kt)
   1510  1.2  christos 	krb5_kt_close(context, kt);
   1511  1.2  christos     krb5_free_context(context);
   1512  1.1     elric     return ret;
   1513  1.1     elric }
   1514