LCOV - code coverage report
Current view: top level - third_party/heimdal/kuser - kinit.c (source / functions) Hit Total Coverage
Test: coverage report for v4-17-test 1498b464 Lines: 361 840 43.0 %
Date: 2024-06-13 04:01:37 Functions: 11 23 47.8 %

          Line data    Source code
       1             : /*
       2             :  * Copyright (c) 1997-2007 Kungliga Tekniska Högskolan
       3             :  * (Royal Institute of Technology, Stockholm, Sweden).
       4             :  * All rights reserved.
       5             :  *
       6             :  * Portions Copyright (c) 2009 Apple Inc. All rights reserved.
       7             :  *
       8             :  * Redistribution and use in source and binary forms, with or without
       9             :  * modification, are permitted provided that the following conditions
      10             :  * are met:
      11             :  *
      12             :  * 1. Redistributions of source code must retain the above copyright
      13             :  *    notice, this list of conditions and the following disclaimer.
      14             :  *
      15             :  * 2. Redistributions in binary form must reproduce the above copyright
      16             :  *    notice, this list of conditions and the following disclaimer in the
      17             :  *    documentation and/or other materials provided with the distribution.
      18             :  *
      19             :  * 3. Neither the name of the Institute nor the names of its contributors
      20             :  *    may be used to endorse or promote products derived from this software
      21             :  *    without specific prior written permission.
      22             :  *
      23             :  * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
      24             :  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
      25             :  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
      26             :  * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
      27             :  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
      28             :  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
      29             :  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
      30             :  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
      31             :  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
      32             :  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
      33             :  * SUCH DAMAGE.
      34             :  */
      35             : 
      36             : #include "kuser_locl.h"
      37             : #undef HC_DEPRECATED_CRYPTO
      38             : #include <krb5_locl.h>
      39             : 
      40             : #ifdef HAVE_FRAMEWORK_SECURITY
      41             : #include <Security/Security.h>
      42             : #endif
      43             : 
      44             : #ifndef NO_NTLM
      45             : #include "heimntlm.h"
      46             : #endif
      47             : 
      48             : #ifndef SIGINFO
      49             : #define SIGINFO SIGUSR1
      50             : #endif
      51             : 
      52             : int forwardable_flag    = -1;
      53             : int proxiable_flag      = -1;
      54             : int renewable_flag      = -1;
      55             : int renew_flag          = 0;
      56             : int pac_flag            = -1;
      57             : int validate_flag       = 0;
      58             : int version_flag        = 0;
      59             : int help_flag           = 0;
      60             : int addrs_flag          = -1;
      61             : struct getarg_strings extra_addresses;
      62             : int anonymous_flag      = 0;
      63             : char *lifetime          = NULL;
      64             : char *renew_life        = NULL;
      65             : char *server_str        = NULL;
      66             : static krb5_principal tgs_service;
      67             : char *cred_cache        = NULL;
      68             : char *start_str         = NULL;
      69             : static int default_for = 0;
      70             : static int switch_cache_flags = -1;
      71             : struct getarg_strings etype_str;
      72             : int use_keytab          = 0;
      73             : char *keytab_str        = NULL;
      74             : static krb5_keytab kt   = NULL;
      75             : int do_afslog           = -1;
      76             : int fcache_version;
      77             : char *password_file     = NULL;
      78             : char *pk_user_id        = NULL;
      79             : int pk_enterprise_flag = 0;
      80             : struct hx509_certs_data *ent_user_id = NULL;
      81             : char *pk_x509_anchors   = NULL;
      82             : int pk_use_enckey       = 0;
      83             : int pk_anon_fast_armor  = -1;
      84             : char *gss_preauth_mech  = NULL;
      85             : char *gss_preauth_name  = NULL;
      86             : char *kdc_hostname      = NULL;
      87             : static int canonicalize_flag = 0;
      88             : static int enterprise_flag = 0;
      89             : static int ok_as_delegate_flag = 0;
      90             : static char *fast_armor_cache_string = NULL;
      91             : static int use_referrals_flag = 0;
      92             : static int windows_flag = 0;
      93             : #ifndef NO_NTLM
      94             : static char *ntlm_domain;
      95             : #endif
      96             : 
      97             : 
      98             : static struct getargs args[] = {
      99             :     /*
     100             :      * used by MIT
     101             :      * a: ~A
     102             :      * V: verbose
     103             :      * F: ~f
     104             :      * P: ~p
     105             :      * C: v4 cache name?
     106             :      * 5:
     107             :      *
     108             :      * old flags
     109             :      * 4:
     110             :      * 9:
     111             :      */
     112             :     { "afslog",       0  , arg_flag, &do_afslog,
     113             :       NP_("obtain afs tokens", ""), NULL },
     114             : 
     115             :     { "cache",                'c', arg_string, &cred_cache,
     116             :       NP_("credentials cache", ""), "cachename" },
     117             : 
     118             :     { "forwardable",  'F', arg_negative_flag, &forwardable_flag,
     119             :       NP_("get tickets not forwardable", ""), NULL },
     120             : 
     121             :     { NULL,             'f', arg_flag, &forwardable_flag,
     122             :       NP_("get forwardable tickets", ""), NULL },
     123             : 
     124             :     { "keytab",         't', arg_string, &keytab_str,
     125             :       NP_("keytab to use", ""), "keytabname" },
     126             : 
     127             :     { "lifetime",     'l', arg_string, &lifetime,
     128             :       NP_("lifetime of tickets", ""), "time" },
     129             : 
     130             :     { "proxiable",    'p', arg_flag, &proxiable_flag,
     131             :       NP_("get proxiable tickets", ""), NULL },
     132             : 
     133             :     { "renew",          'R', arg_flag, &renew_flag,
     134             :       NP_("renew TGT", ""), NULL },
     135             : 
     136             :     { "renewable",    0,   arg_flag, &renewable_flag,
     137             :       NP_("get renewable tickets", ""), NULL },
     138             : 
     139             :     { "renewable-life",       'r', arg_string, &renew_life,
     140             :       NP_("renewable lifetime of tickets", ""), "time" },
     141             : 
     142             :     { "server",       'S', arg_string, &server_str,
     143             :       NP_("server to get ticket for", ""), "principal" },
     144             : 
     145             :     { "start-time",   's', arg_string, &start_str,
     146             :       NP_("when ticket gets valid", ""), "time" },
     147             : 
     148             :     { "use-keytab",     'k', arg_flag, &use_keytab,
     149             :       NP_("get key from keytab", ""), NULL },
     150             : 
     151             :     { "validate",     'v', arg_flag, &validate_flag,
     152             :       NP_("validate TGT", ""), NULL },
     153             : 
     154             :     { "enctypes",     'e', arg_strings, &etype_str,
     155             :       NP_("encryption types to use", ""), "enctypes" },
     156             : 
     157             :     { "fcache-version", 0,   arg_integer, &fcache_version,
     158             :       NP_("file cache version to create", ""), NULL },
     159             : 
     160             :     { "addresses",    'A',   arg_negative_flag,       &addrs_flag,
     161             :       NP_("request a ticket with no addresses", ""), NULL },
     162             : 
     163             :     { "extra-addresses",'a', arg_strings,     &extra_addresses,
     164             :       NP_("include these extra addresses", ""), "addresses" },
     165             : 
     166             :     { "anonymous",    'n',   arg_flag,        &anonymous_flag,
     167             :       NP_("request an anonymous ticket", ""), NULL },
     168             : 
     169             :     { "request-pac",  0,   arg_flag,  &pac_flag,
     170             :       NP_("request a Windows PAC", ""), NULL },
     171             : 
     172             :     { "password-file",        0,   arg_string, &password_file,
     173             :       NP_("read the password from a file", ""), NULL },
     174             : 
     175             :     { "canonicalize",0,   arg_flag, &canonicalize_flag,
     176             :       NP_("canonicalize client principal", ""), NULL },
     177             : 
     178             :     { "enterprise",0,   arg_flag, &enterprise_flag,
     179             :       NP_("parse principal as a KRB5-NT-ENTERPRISE name", ""), NULL },
     180             : #ifdef PKINIT
     181             :     { "pk-enterprise",        0,      arg_flag,       &pk_enterprise_flag,
     182             :       NP_("use enterprise name from certificate", ""), NULL },
     183             : 
     184             :     { "pk-user",      'C',    arg_string,     &pk_user_id,
     185             :       NP_("principal's public/private/certificate identifier", ""), "id" },
     186             : 
     187             :     { "x509-anchors", 'D',  arg_string, &pk_x509_anchors,
     188             :       NP_("directory with CA certificates", ""), "directory" },
     189             : 
     190             :     { "pk-use-enckey",        0,  arg_flag, &pk_use_enckey,
     191             :       NP_("Use RSA encrypted reply (instead of DH)", ""), NULL },
     192             : 
     193             :     { "pk-anon-fast-armor",   0,  arg_flag, &pk_anon_fast_armor,
     194             :       NP_("use unauthenticated anonymous PKINIT as FAST armor", ""), NULL },
     195             : #endif
     196             : 
     197             :     { "gss-mech",   0,        arg_string, &gss_preauth_mech,
     198             :       NP_("use GSS mechanism for pre-authentication", ""), NULL },
     199             : 
     200             :     { "gss-name",   0,        arg_string, &gss_preauth_name,
     201             :       NP_("use distinct GSS identity for pre-authentication", ""), NULL },
     202             : 
     203             :     { "kdc-hostname", 0,  arg_string, &kdc_hostname,
     204             :       NP_("KDC host name", ""), "hostname" },
     205             : 
     206             : #ifndef NO_NTLM
     207             :     { "ntlm-domain",  0,  arg_string, &ntlm_domain,
     208             :       NP_("NTLM domain", ""), "domain" },
     209             : #endif
     210             : 
     211             :     { "change-default",  0,  arg_negative_flag, &switch_cache_flags,
     212             :       NP_("switch the default cache to the new credentials cache", ""), NULL },
     213             : 
     214             :     { "default-for-principal",  0,  arg_flag, &default_for,
     215             :       NP_("Use a default cache appropriate for the client principal", ""), NULL },
     216             : 
     217             :     { "ok-as-delegate",       0,  arg_flag, &ok_as_delegate_flag,
     218             :       NP_("honor ok-as-delegate on tickets", ""), NULL },
     219             : 
     220             :     { "fast-armor-cache",     0,  arg_string, &fast_armor_cache_string,
     221             :       NP_("use this credential cache as FAST armor cache", ""), "cache" },
     222             : 
     223             :     { "use-referrals",        0,  arg_flag, &use_referrals_flag,
     224             :       NP_("only use referrals, no dns canalisation", ""), NULL },
     225             : 
     226             :     { "windows",      0,  arg_flag, &windows_flag,
     227             :       NP_("get windows behavior", ""), NULL },
     228             : 
     229             :     { "version",      0,   arg_flag, &version_flag, NULL, NULL },
     230             :     { "help",         0,   arg_flag, &help_flag, NULL, NULL }
     231             : };
     232             : 
     233             : static char *
     234             : get_default_realm(krb5_context context);
     235             : 
     236             : static void
     237           0 : usage(int ret)
     238             : {
     239           0 :     arg_printusage_i18n(args, sizeof(args)/sizeof(*args), N_("Usage: ", ""),
     240             :                         NULL, "[principal [command]]", getarg_i18n);
     241           0 :     exit(ret);
     242             : }
     243             : 
     244             : static krb5_error_code
     245           6 : tgs_principal(krb5_context context,
     246             :           krb5_ccache cache,
     247             :           krb5_principal client,
     248             :           krb5_const_realm tgs_realm,
     249             :           krb5_principal *out_princ)
     250             : {
     251             :     krb5_error_code ret;
     252             :     krb5_principal tgs_princ;
     253             :     krb5_creds creds;
     254             :     krb5_creds *tick;
     255             :     krb5_flags options;
     256             : 
     257           6 :     ret = krb5_make_principal(context, &tgs_princ, tgs_realm,
     258             :                               KRB5_TGS_NAME, tgs_realm, NULL);
     259           6 :     if (ret)
     260           0 :         return ret;
     261             : 
     262             :     /*
     263             :      * Don't fail-over to a different realm just because a TGT expired
     264             :      */
     265           6 :     options = KRB5_GC_CACHED | KRB5_GC_EXPIRED_OK;
     266             : 
     267           6 :     memset(&creds, 0, sizeof(creds));
     268           6 :     creds.client = client;
     269           6 :     creds.server = tgs_princ;
     270           6 :     ret = krb5_get_credentials(context, options, cache, &creds, &tick);
     271           6 :     if (ret == 0) {
     272           6 :         krb5_free_creds(context, tick);
     273           6 :         *out_princ = tgs_princ;
     274             :     } else {
     275           0 :         krb5_free_principal(context, tgs_princ);
     276             :     }
     277             : 
     278           6 :     return ret;
     279             : }
     280             : 
     281             : 
     282             : /*
     283             :  * Try TGS specified with '-S',
     284             :  * then TGS of client realm,
     285             :  * then if fallback is FALSE: fail,
     286             :  * otherwise try TGS of default realm,
     287             :  * and finally first TGT in ccache.
     288             :  */
     289             : static krb5_error_code
     290           6 : get_server(krb5_context context,
     291             :            krb5_ccache cache,
     292             :            krb5_principal client,
     293             :            const char *server,
     294             :            krb5_boolean fallback,
     295             :            krb5_principal *princ)
     296             : {
     297           6 :     krb5_error_code ret = 0;
     298             :     krb5_const_realm realm;
     299             :     krb5_realm def_realm;
     300             :     krb5_cc_cursor cursor;
     301             :     krb5_creds creds;
     302             :     const char *pcomp;
     303             : 
     304           6 :     if (tgs_service)
     305           0 :         goto done;
     306             : 
     307           6 :     if (server) {
     308           0 :         ret = krb5_parse_name(context, server, &tgs_service);
     309           0 :         goto done;
     310             :     }
     311             : 
     312             :     /* Try the client realm first */
     313           6 :     realm = krb5_principal_get_realm(context, client);
     314           6 :     ret = tgs_principal(context, cache, client, realm, &tgs_service);
     315           6 :     if (ret == 0 || ret != KRB5_CC_NOTFOUND)
     316             :         goto done;
     317             : 
     318           0 :     if (!fallback)
     319           0 :         return ret;
     320             : 
     321             :     /* Next try the default realm */
     322           0 :     ret = krb5_get_default_realm(context, &def_realm);
     323           0 :     if (ret)
     324           0 :         return ret;
     325           0 :     ret = tgs_principal(context, cache, client, def_realm, &tgs_service);
     326           0 :     free(def_realm);
     327           0 :     if (ret == 0 || ret != KRB5_CC_NOTFOUND)
     328             :         goto done;
     329             : 
     330             :     /* Finally try the first TGT with instance == realm in the cache */
     331           0 :     ret = krb5_cc_start_seq_get(context, cache, &cursor);
     332           0 :     if (ret)
     333           0 :         return ret;
     334             : 
     335           0 :     for (/**/; ret == 0; krb5_free_cred_contents (context, &creds)) {
     336             : 
     337           0 :         ret = krb5_cc_next_cred(context, cache, &cursor, &creds);
     338           0 :         if (ret)
     339           0 :             break;
     340           0 :         if (creds.server->name.name_string.len != 2)
     341           0 :             continue;
     342           0 :         pcomp = krb5_principal_get_comp_string(context, creds.server, 0);
     343           0 :         if (strcmp(pcomp, KRB5_TGS_NAME) != 0)
     344           0 :             continue;
     345           0 :         realm = krb5_principal_get_realm(context, creds.server);
     346           0 :         pcomp = krb5_principal_get_comp_string(context, creds.server, 1);
     347           0 :         if (strcmp(realm, pcomp) != 0)
     348           0 :             continue;
     349           0 :         ret = krb5_copy_principal(context, creds.server, &tgs_service);
     350           0 :         break;
     351             :     }
     352           0 :     if (ret == KRB5_CC_END) {
     353           0 :         ret = KRB5_CC_NOTFOUND;
     354           0 :         krb5_set_error_message(context, ret,
     355           0 :                                N_("Credential cache contains no TGTs", ""));
     356             :     }
     357           0 :     krb5_cc_end_seq_get(context, cache, &cursor);
     358             : 
     359          12 : done:
     360           6 :     if (!ret)
     361           6 :         ret = krb5_copy_principal(context, tgs_service, princ);
     362           6 :     return ret;
     363             : }
     364             : 
     365             : static krb5_error_code
     366           6 : copy_configs(krb5_context context,
     367             :              krb5_ccache dst,
     368             :              krb5_ccache src,
     369             :              krb5_principal start_ticket_server)
     370             : {
     371             :     krb5_error_code ret;
     372           6 :     const char *cfg_names[] = {"realm-config", "FriendlyName", "anon_pkinit_realm", NULL};
     373           6 :     const char *cfg_names_w_pname[] = {"fast_avail", NULL};
     374             :     krb5_data cfg_data;
     375             :     size_t i;
     376             : 
     377          24 :     for (i = 0; cfg_names[i]; i++) {
     378          18 :         ret = krb5_cc_get_config(context, src, NULL, cfg_names[i], &cfg_data);
     379          18 :         if (ret == KRB5_CC_NOTFOUND || ret == KRB5_CC_END) {
     380          12 :             continue;
     381           6 :         } else if (ret) {
     382           0 :             krb5_warn(context, ret, "krb5_cc_get_config");
     383           0 :             return ret;
     384             :         }
     385           6 :         ret = krb5_cc_set_config(context, dst, NULL, cfg_names[i], &cfg_data);
     386           6 :         if (ret)
     387           0 :             krb5_warn(context, ret, "krb5_cc_set_config");
     388             :     }
     389          12 :     for (i = 0; start_ticket_server && cfg_names_w_pname[i]; i++) {
     390           6 :         ret = krb5_cc_get_config(context, src, start_ticket_server,
     391             :                                  cfg_names_w_pname[i], &cfg_data);
     392           6 :         if (ret == KRB5_CC_NOTFOUND || ret == KRB5_CC_END) {
     393           0 :             continue;
     394           6 :         } else if (ret) {
     395           0 :             krb5_warn(context, ret, "krb5_cc_get_config");
     396           0 :             return ret;
     397             :         }
     398           6 :         ret = krb5_cc_set_config(context, dst, start_ticket_server,
     399             :                                  cfg_names_w_pname[i], &cfg_data);
     400           6 :         if (ret && ret != KRB5_CC_NOTFOUND)
     401           0 :             krb5_warn(context, ret, "krb5_cc_set_config");
     402             :     }
     403             :     /*
     404             :      * We don't copy cc configs for any other principals though (mostly
     405             :      * those are per-target time offsets and the like, so it's bad to
     406             :      * lose them, but hardly the end of the world, and as they may not
     407             :      * expire anyways, it's good to let them go).
     408             :      */
     409           6 :     return 0;
     410             : }
     411             : 
     412             : static krb5_error_code
     413           0 : get_anon_pkinit_tgs_name(krb5_context context,
     414             :                          krb5_ccache ccache,
     415             :                          krb5_principal *tgs_name)
     416             : {
     417             :     krb5_error_code ret;
     418             :     krb5_data data;
     419             :     char *realm;
     420             : 
     421           0 :     ret = krb5_cc_get_config(context, ccache, NULL, "anon_pkinit_realm", &data);
     422           0 :     if (ret == 0)
     423           0 :         realm = strndup(data.data, data.length);
     424             :     else
     425           0 :         realm = get_default_realm(context);
     426             : 
     427           0 :     krb5_data_free(&data);
     428             : 
     429           0 :     if (realm == NULL)
     430           0 :         return krb5_enomem(context);
     431             : 
     432           0 :     ret = krb5_make_principal(context, tgs_name, realm,
     433             :                               KRB5_TGS_NAME, realm, NULL);
     434             : 
     435           0 :     free(realm);
     436             : 
     437           0 :     return ret;
     438             : }
     439             : 
     440             : static krb5_error_code
     441           6 : renew_validate(krb5_context context,
     442             :                int renew,
     443             :                int validate,
     444             :                krb5_ccache *cachep,
     445             :                krb5_const_principal principal,
     446             :                krb5_boolean cache_is_default_for,
     447             :                const char *server,
     448             :                krb5_deltat life)
     449             : {
     450             :     krb5_error_code ret;
     451           6 :     krb5_ccache tempccache = NULL;
     452           6 :     krb5_ccache cache = *cachep;
     453           6 :     krb5_creds in, *out = NULL;
     454             :     krb5_kdc_flags flags;
     455             : 
     456           6 :     memset(&in, 0, sizeof(in));
     457             : 
     458           6 :     ret = krb5_cc_get_principal(context, cache, &in.client);
     459           6 :     if (ret && cache_is_default_for && principal) {
     460             :         krb5_error_code ret2;
     461           0 :         krb5_ccache def_ccache = NULL;
     462             : 
     463           0 :         ret2 = krb5_cc_default(context, &def_ccache);
     464           0 :         if (ret2 == 0)
     465           0 :             ret2 = krb5_cc_get_principal(context, def_ccache, &in.client);
     466           0 :         if (ret2 == 0 &&
     467           0 :             krb5_principal_compare(context, principal, in.client)) {
     468           0 :             krb5_cc_close(context, *cachep);
     469           0 :             cache = *cachep = def_ccache;
     470           0 :             def_ccache = NULL;
     471           0 :             ret = 0;
     472             :         }
     473           0 :         krb5_cc_close(context, def_ccache);
     474             :     }
     475           6 :     if (ret) {
     476           0 :         krb5_warn(context, ret, "krb5_cc_get_principal");
     477           0 :         return ret;
     478             :     }
     479             : 
     480           6 :     if (principal && !krb5_principal_compare(context, principal, in.client)) {
     481           0 :         char *ccname = NULL;
     482             : 
     483           0 :         (void) krb5_cc_get_full_name(context, cache, &ccname);
     484           0 :         krb5_errx(context, 1, "Credentials in cache %s do not match requested "
     485           0 :                   "principal", ccname ? ccname : "requested");
     486             :         free(ccname);
     487             :     }
     488             : 
     489          12 :     if (server == NULL &&
     490           6 :         krb5_principal_is_anonymous(context, in.client,
     491             :                                     KRB5_ANON_MATCH_UNAUTHENTICATED))
     492           0 :         ret = get_anon_pkinit_tgs_name(context, cache, &in.server);
     493             :     else
     494           6 :         ret = get_server(context, cache, in.client, server, TRUE, &in.server);
     495           6 :     if (ret) {
     496           0 :         krb5_warn(context, ret, "get_server");
     497           0 :         goto out;
     498             :     }
     499             : 
     500           6 :     if (renew) {
     501             :         /*
     502             :          * no need to check the error here, it's only to be
     503             :          * friendly to the user
     504             :          */
     505           6 :         (void) krb5_get_credentials(context, KRB5_GC_CACHED, cache, &in, &out);
     506             :     }
     507             : 
     508           6 :     flags.i = 0;
     509           6 :     flags.b.renewable         = flags.b.renew = renew;
     510           6 :     flags.b.validate          = validate;
     511             : 
     512           6 :     if (forwardable_flag != -1)
     513           0 :         flags.b.forwardable       = forwardable_flag;
     514           6 :     else if (out)
     515           6 :         flags.b.forwardable       = out->flags.b.forwardable;
     516             : 
     517           6 :     if (proxiable_flag != -1)
     518           0 :         flags.b.proxiable         = proxiable_flag;
     519           6 :     else if (out)
     520           6 :         flags.b.proxiable         = out->flags.b.proxiable;
     521             : 
     522           6 :     if (anonymous_flag)
     523           0 :         flags.b.request_anonymous = anonymous_flag;
     524           6 :     if (life)
     525           0 :         in.times.endtime = time(NULL) + life;
     526             : 
     527           6 :     if (out) {
     528           6 :         krb5_free_creds(context, out);
     529           6 :         out = NULL;
     530             :     }
     531             : 
     532             : 
     533           6 :     ret = krb5_get_kdc_cred(context,
     534             :                             cache,
     535             :                             flags,
     536             :                             NULL,
     537             :                             NULL,
     538             :                             &in,
     539             :                             &out);
     540           6 :     if (ret) {
     541           0 :         krb5_warn(context, ret, "krb5_get_kdc_cred");
     542           0 :         goto out;
     543             :     }
     544             : 
     545           6 :     ret = krb5_cc_new_unique(context, krb5_cc_get_type(context, cache),
     546             :                              NULL, &tempccache);
     547           6 :     if (ret) {
     548           0 :         krb5_warn(context, ret, "krb5_cc_new_unique");
     549           0 :         goto out;
     550             :     }
     551             : 
     552           6 :     ret = krb5_cc_initialize(context, tempccache, in.client);
     553           6 :     if (ret) {
     554           0 :         krb5_warn(context, ret, "krb5_cc_initialize");
     555           0 :         goto out;
     556             :     }
     557             : 
     558           6 :     ret = krb5_cc_store_cred(context, tempccache, out);
     559           6 :     if (ret) {
     560           0 :         krb5_warn(context, ret, "krb5_cc_store_cred");
     561           0 :         goto out;
     562             :     }
     563             : 
     564             :     /*
     565             :      * We want to preserve cc configs as some are security-relevant, and
     566             :      * anyways it's the friendly thing to do.
     567             :      */
     568           6 :     ret = copy_configs(context, tempccache, cache, out->server);
     569           6 :     if (ret)
     570           0 :         goto out;
     571             : 
     572           6 :     ret = krb5_cc_move(context, tempccache, cache);
     573           6 :     if (ret) {
     574           0 :         krb5_warn(context, ret, "krb5_cc_move");
     575           0 :         goto out;
     576             :     }
     577           6 :     tempccache = NULL;
     578             : 
     579           6 : out:
     580           6 :     if (tempccache)
     581           0 :         krb5_cc_destroy(context, tempccache);
     582           6 :     if (out)
     583           6 :         krb5_free_creds(context, out);
     584           6 :     krb5_free_cred_contents(context, &in);
     585           6 :     return ret;
     586             : }
     587             : 
     588             : static krb5_error_code
     589           0 : make_wellknown_name(krb5_context context,
     590             :                     krb5_const_realm realm,
     591             :                     const char *instance,
     592             :                     krb5_principal *principal)
     593             : {
     594             :     krb5_error_code ret;
     595             : 
     596           0 :     ret = krb5_make_principal(context, principal, realm,
     597             :                               KRB5_WELLKNOWN_NAME, instance, NULL);
     598           0 :     if (ret == 0)
     599           0 :         krb5_principal_set_type(context, *principal, KRB5_NT_WELLKNOWN);
     600             : 
     601           0 :     return ret;
     602             : }
     603             : 
     604             : static krb5_error_code
     605           0 : acquire_gss_cred(krb5_context context,
     606             :                  krb5_const_principal client,
     607             :                  krb5_deltat life,
     608             :                  const char *passwd,
     609             :                  gss_cred_id_t *cred,
     610             :                  gss_OID *mech)
     611             : {
     612             :     krb5_error_code ret;
     613             :     OM_uint32 major, minor;
     614           0 :     gss_name_t name = GSS_C_NO_NAME;
     615             :     gss_key_value_element_desc cred_element;
     616             :     gss_key_value_set_desc cred_store;
     617             :     gss_OID_set_desc mechs;
     618             : 
     619           0 :     *cred = GSS_C_NO_CREDENTIAL;
     620           0 :     *mech = GSS_C_NO_OID;
     621             : 
     622           0 :     if (gss_preauth_mech) {
     623           0 :         *mech = gss_name_to_oid(gss_preauth_mech);
     624           0 :         if (*mech == GSS_C_NO_OID)
     625           0 :             return EINVAL;
     626             :     }
     627             : 
     628           0 :     if (gss_preauth_name) {
     629             :         gss_buffer_desc buf;
     630             : 
     631           0 :         buf.value = gss_preauth_name;
     632           0 :         buf.length = strlen(gss_preauth_name);
     633             : 
     634           0 :         major = gss_import_name(&minor, &buf, GSS_C_NT_USER_NAME, &name);
     635           0 :         ret = _krb5_gss_map_error(major, minor);
     636           0 :     } else if (!krb5_principal_is_federated(context, client)) {
     637           0 :         ret = _krb5_gss_pa_unparse_name(context, client, &name);
     638             :     } else {
     639             :         /*
     640             :          * WELLKNOWN/FEDERATED is used a placeholder where the user
     641             :          * did not specify either a Kerberos credential or a GSS-API
     642             :          * initiator name. It avoids the expense of acquiring a default
     643             :          * credential purely to interrogate the credential name.
     644             :          */
     645           0 :         name = GSS_C_NO_NAME;
     646           0 :         ret = 0;
     647             :     }
     648           0 :     if (ret)
     649           0 :         goto out;
     650             : 
     651           0 :     cred_store.count = 1;
     652           0 :     cred_store.elements = &cred_element;
     653             : 
     654           0 :     if (passwd && passwd[0]) {
     655           0 :         cred_element.key = "password";
     656           0 :         cred_element.value = passwd;
     657           0 :     } else if (keytab_str) {
     658           0 :         cred_element.key = "client_keytab",
     659           0 :         cred_element.value = keytab_str;
     660             :     } else {
     661           0 :         cred_store.count = 0;
     662             :     }
     663             : 
     664           0 :     if (*mech) {
     665           0 :         mechs.count = 1;
     666           0 :         mechs.elements = (gss_OID)*mech;
     667             :     }
     668             : 
     669           0 :     major = gss_acquire_cred_from(&minor,
     670             :                                   name,
     671             :                                   life ? life : GSS_C_INDEFINITE,
     672           0 :                                   *mech ? &mechs : GSS_C_NO_OID_SET,
     673             :                                   GSS_C_INITIATE,
     674             :                                   &cred_store,
     675             :                                   cred,
     676             :                                   NULL,
     677             :                                   NULL);
     678           0 :     if (major != GSS_S_COMPLETE) {
     679           0 :         ret = _krb5_gss_map_error(major, minor);
     680           0 :         goto out;
     681             :     }
     682             : 
     683           0 : out:
     684           0 :     gss_release_name(&minor, &name);
     685           0 :     return ret;
     686             : }
     687             : 
     688             : #ifndef NO_NTLM
     689             : 
     690             : static krb5_error_code
     691           0 : store_ntlmkey(krb5_context context, krb5_ccache id,
     692             :               const char *domain, struct ntlm_buf *buf)
     693             : {
     694             :     krb5_error_code ret;
     695             :     krb5_data data;
     696             :     char *name;
     697             :     int aret;
     698             : 
     699           0 :     ret = krb5_cc_get_config(context, id, NULL, "default-ntlm-domain", &data);
     700           0 :     if (ret == 0) {
     701           0 :         krb5_data_free(&data);
     702             :     } else {
     703           0 :         data.length = strlen(domain);
     704           0 :         data.data = rk_UNCONST(domain);
     705           0 :         ret = krb5_cc_set_config(context, id, NULL, "default-ntlm-domain", &data);
     706           0 :         if (ret != 0)
     707           0 :             return ret;
     708             :     }
     709             : 
     710           0 :     aret = asprintf(&name, "ntlm-key-%s", domain);
     711           0 :     if (aret == -1 || name == NULL)
     712           0 :         return krb5_enomem(context);
     713             : 
     714           0 :     data.length = buf->length;
     715           0 :     data.data = buf->data;
     716             : 
     717           0 :     ret = krb5_cc_set_config(context, id, NULL, name, &data);
     718           0 :     free(name);
     719           0 :     return ret;
     720             : }
     721             : #endif
     722             : 
     723             : static krb5_error_code
     724          77 : get_new_tickets(krb5_context context,
     725             :                 krb5_principal principal,
     726             :                 krb5_ccache ccache,
     727             :                 krb5_deltat ticket_life,
     728             :                 int interactive,
     729             :                 int anonymous_pkinit)
     730             : {
     731             :     krb5_error_code ret;
     732             :     krb5_creds cred;
     733             :     char passwd[256];
     734          77 :     krb5_deltat start_time = 0;
     735          77 :     krb5_deltat renew = 0;
     736          77 :     const char *renewstr = NULL;
     737          77 :     krb5_enctype *enctype = NULL;
     738          77 :     krb5_ccache tempccache = NULL;
     739          77 :     krb5_init_creds_context ctx = NULL;
     740          77 :     krb5_get_init_creds_opt *opt = NULL;
     741          77 :     krb5_prompter_fct prompter = krb5_prompter_posix;
     742          77 :     gss_cred_id_t gss_cred = GSS_C_NO_CREDENTIAL;
     743          77 :     gss_OID gss_mech = GSS_C_NO_OID;
     744          77 :     krb5_principal federated_name = NULL;
     745             : 
     746             : #ifndef NO_NTLM
     747             :     struct ntlm_buf ntlmkey;
     748          77 :     memset(&ntlmkey, 0, sizeof(ntlmkey));
     749             : #endif
     750          77 :     passwd[0] = '\0';
     751             : 
     752          77 :     if (!interactive)
     753           0 :         prompter = NULL;
     754             : 
     755          77 :     if (password_file) {
     756             :         FILE *f;
     757             : 
     758          66 :         if (strcasecmp("STDIN", password_file) == 0)
     759           0 :             f = stdin;
     760             :         else
     761          66 :             f = fopen(password_file, "r");
     762          66 :         if (f == NULL) {
     763           0 :             krb5_warnx(context, N_("Failed to open the password file %s", ""),
     764             :                        password_file);
     765           0 :             return errno;
     766             :         }
     767             : 
     768          66 :         if (fgets(passwd, sizeof(passwd), f) == NULL) {
     769           0 :             krb5_warnx(context, N_("Failed to read password from file %s", ""),
     770             :                        password_file);
     771           0 :             if (f != stdin)
     772           0 :                 fclose(f);
     773           0 :             return EINVAL; /* XXX Need a better error */
     774             :         }
     775          66 :         if (f != stdin)
     776          66 :             fclose(f);
     777          66 :         passwd[strcspn(passwd, "\n")] = '\0';
     778             :     }
     779             : 
     780             : #ifdef HAVE_FRAMEWORK_SECURITY
     781             :     if (passwd[0] == '\0') {
     782             :         const char *realm;
     783             :         OSStatus osret;
     784             :         UInt32 length;
     785             :         void *buffer;
     786             :         char *name;
     787             : 
     788             :         realm = krb5_principal_get_realm(context, principal);
     789             : 
     790             :         ret = krb5_unparse_name_flags(context, principal,
     791             :                                       KRB5_PRINCIPAL_UNPARSE_NO_REALM, &name);
     792             :         if (ret)
     793             :             goto nopassword;
     794             : 
     795             :         osret = SecKeychainFindGenericPassword(NULL, strlen(realm), realm,
     796             :                                                strlen(name), name,
     797             :                                                &length, &buffer, NULL);
     798             :         free(name);
     799             :         if (osret == noErr && length < sizeof(passwd) - 1) {
     800             :             memcpy(passwd, buffer, length);
     801             :             passwd[length] = '\0';
     802             :         }
     803             :     nopassword:
     804             :         do { } while(0);
     805             :     }
     806             : #endif
     807             : 
     808          77 :     memset(&cred, 0, sizeof(cred));
     809             : 
     810          77 :     ret = krb5_get_init_creds_opt_alloc(context, &opt);
     811          77 :     if (ret) {
     812           0 :         krb5_warn(context, ret, "krb5_get_init_creds_opt_alloc");
     813           0 :         goto out;
     814             :     }
     815             : 
     816          77 :     krb5_get_init_creds_opt_set_default_flags(context, "kinit",
     817             :         krb5_principal_get_realm(context, principal), opt);
     818             : 
     819          77 :     if (forwardable_flag != -1)
     820           4 :         krb5_get_init_creds_opt_set_forwardable(opt, forwardable_flag);
     821          77 :     if (proxiable_flag != -1)
     822           0 :         krb5_get_init_creds_opt_set_proxiable(opt, proxiable_flag);
     823          77 :     if (anonymous_flag)
     824           0 :         krb5_get_init_creds_opt_set_anonymous(opt, anonymous_flag);
     825          77 :     if (pac_flag != -1)
     826          61 :         krb5_get_init_creds_opt_set_pac_request(context, opt,
     827             :                                                 pac_flag ? TRUE : FALSE);
     828          77 :     if (canonicalize_flag)
     829           0 :         krb5_get_init_creds_opt_set_canonicalize(context, opt, TRUE);
     830          77 :     if (pk_enterprise_flag || enterprise_flag || canonicalize_flag || windows_flag)
     831          22 :         krb5_get_init_creds_opt_set_win2k(context, opt, TRUE);
     832          77 :     if (pk_user_id || ent_user_id || anonymous_pkinit) {
     833           0 :         if (pk_anon_fast_armor == -1)
     834           0 :             pk_anon_fast_armor = 0;
     835           0 :         ret = krb5_get_init_creds_opt_set_pkinit(context, opt,
     836             :                                                  principal,
     837             :                                                  pk_user_id,
     838             :                                                  pk_x509_anchors,
     839             :                                                  NULL,
     840             :                                                  NULL,
     841           0 :                                                  pk_use_enckey ? KRB5_GIC_OPT_PKINIT_USE_ENCKEY : 0 |
     842           0 :                                                  anonymous_pkinit ? KRB5_GIC_OPT_PKINIT_ANONYMOUS : 0,
     843             :                                                  prompter,
     844             :                                                  NULL,
     845             :                                                  passwd);
     846           0 :         if (ret) {
     847           0 :             krb5_warn(context, ret, "krb5_get_init_creds_opt_set_pkinit");
     848           0 :             goto out;
     849             :         }
     850           0 :         if (ent_user_id)
     851           0 :             krb5_get_init_creds_opt_set_pkinit_user_certs(context, opt, ent_user_id);
     852             :     }
     853             : 
     854          77 :     if (addrs_flag != -1)
     855           0 :         krb5_get_init_creds_opt_set_addressless(context, opt,
     856             :                                                 addrs_flag ? FALSE : TRUE);
     857             : 
     858          77 :     if (renew_life == NULL && renewable_flag)
     859           9 :         renewstr = "6 months";
     860          77 :     if (renew_life)
     861           0 :         renewstr = renew_life;
     862          77 :     if (renewstr) {
     863           9 :         renew = parse_time(renewstr, "s");
     864           9 :         if (renew < 0)
     865           0 :             errx(1, "unparsable time: %s", renewstr);
     866             : 
     867           9 :         krb5_get_init_creds_opt_set_renew_life(opt, renew);
     868             :     }
     869             : 
     870          77 :     if (ticket_life != 0)
     871           3 :         krb5_get_init_creds_opt_set_tkt_life(opt, ticket_life);
     872             : 
     873          77 :     if (start_str) {
     874           0 :         int tmp = parse_time(start_str, "s");
     875           0 :         if (tmp < 0)
     876           0 :             errx(1, N_("unparsable time: %s", ""), start_str);
     877             : 
     878           0 :         start_time = tmp;
     879             :     }
     880             : 
     881          77 :     if (etype_str.num_strings) {
     882             :         int i;
     883             : 
     884          60 :         enctype = malloc(etype_str.num_strings * sizeof(*enctype));
     885          60 :         if (enctype == NULL)
     886           0 :             errx(1, "out of memory");
     887         120 :         for(i = 0; i < etype_str.num_strings; i++) {
     888         120 :             ret = krb5_string_to_enctype(context,
     889          60 :                                          etype_str.strings[i],
     890          60 :                                          &enctype[i]);
     891          60 :             if (ret)
     892           0 :                 errx(1, "unrecognized enctype: %s", etype_str.strings[i]);
     893             :         }
     894          60 :         krb5_get_init_creds_opt_set_etype_list(opt, enctype,
     895             :                                                etype_str.num_strings);
     896             :     }
     897             : 
     898          77 :     if (gss_preauth_mech || gss_preauth_name) {
     899           0 :         ret = acquire_gss_cred(context, principal, ticket_life,
     900             :                                passwd, &gss_cred, &gss_mech);
     901           0 :         if (ret)
     902           0 :             goto out;
     903             : 
     904             :         /*
     905             :          * The principal specified on the command line is used as the GSS-API
     906             :          * initiator name, unless the --gss-name option was present, in which
     907             :          * case the initiator name is specified independently.
     908             :          */
     909           0 :         if (gss_preauth_name == NULL) {
     910           0 :             krb5_const_realm realm = krb5_principal_get_realm(context, principal);
     911             : 
     912           0 :             ret = make_wellknown_name(context, realm,
     913             :                                       KRB5_FEDERATED_NAME, &federated_name);
     914           0 :             if (ret)
     915           0 :                 goto out;
     916             : 
     917           0 :             principal = federated_name;
     918             :         }
     919             :     }
     920             : 
     921          77 :     ret = krb5_init_creds_init(context, principal, prompter, NULL, start_time, opt, &ctx);
     922          77 :     if (ret) {
     923           0 :         krb5_warn(context, ret, "krb5_init_creds_init");
     924           0 :         goto out;
     925             :     }
     926             : 
     927          77 :     if (server_str) {
     928           3 :         ret = krb5_init_creds_set_service(context, ctx, server_str);
     929           3 :         if (ret) {
     930           0 :             krb5_warn(context, ret, "krb5_init_creds_set_service");
     931           0 :             goto out;
     932             :         }
     933             :     }
     934             : 
     935          77 :     if (kdc_hostname) {
     936           0 :         ret = krb5_init_creds_set_kdc_hostname(context, ctx, kdc_hostname);
     937           0 :         if (ret) {
     938           0 :             krb5_warn(context, ret, "krb5_init_creds_set_kdc_hostname");
     939           0 :             goto out;
     940             :         }
     941             :     }
     942             : 
     943          77 :     if (anonymous_flag && pk_anon_fast_armor == -1)
     944           0 :         pk_anon_fast_armor = 0;
     945          77 :     if (!gss_preauth_mech && anonymous_flag && pk_anon_fast_armor) {
     946           0 :         krb5_warnx(context, N_("Ignoring --pk-anon-fast-armor because "
     947             :                                "--anonymous given", ""));
     948           0 :         pk_anon_fast_armor = 0;
     949             :     }
     950             : 
     951          77 :     if (fast_armor_cache_string) {
     952           0 :         krb5_ccache fastid = NULL;
     953             : 
     954           0 :         if (pk_anon_fast_armor > 0)
     955           0 :             krb5_errx(context, 1,
     956           0 :                 N_("cannot specify FAST armor cache with FAST "
     957             :                      "anonymous PKINIT option", ""));
     958           0 :         pk_anon_fast_armor = 0;
     959             : 
     960           0 :         ret = krb5_cc_resolve(context, fast_armor_cache_string, &fastid);
     961           0 :         if (ret) {
     962           0 :             krb5_warn(context, ret, "krb5_cc_resolve(FAST cache)");
     963           0 :             goto out;
     964             :         }
     965             : 
     966           0 :         ret = krb5_init_creds_set_fast_ccache(context, ctx, fastid);
     967           0 :         if (ret) {
     968           0 :             krb5_warn(context, ret, "krb5_init_creds_set_fast_ccache");
     969           0 :             goto out;
     970             :         }
     971          77 :     } else if (pk_anon_fast_armor == -1) {
     972          77 :         ret = _krb5_init_creds_set_fast_anon_pkinit_optimistic(context, ctx);
     973          77 :         if (ret) {
     974           0 :             krb5_warn(context, ret, "_krb5_init_creds_set_fast_anon_pkinit_optimistic");
     975           0 :             goto out;
     976             :         }
     977           0 :     } else if (pk_anon_fast_armor) {
     978           0 :         ret = krb5_init_creds_set_fast_anon_pkinit(context, ctx);
     979           0 :         if (ret) {
     980           0 :             krb5_warn(context, ret, "krb5_init_creds_set_fast_anon_pkinit");
     981           0 :             goto out;
     982             :         }
     983             :     }
     984             : 
     985          77 :     if (gss_mech != GSS_C_NO_OID) {
     986           0 :         ret = krb5_gss_set_init_creds(context, ctx, gss_cred, gss_mech);
     987           0 :         if (ret) {
     988           0 :             krb5_warn(context, ret, "krb5_gss_set_init_creds");
     989           0 :             goto out;
     990             :         }
     991          77 :     } else if (use_keytab || keytab_str) {
     992           7 :         ret = krb5_init_creds_set_keytab(context, ctx, kt);
     993          14 :         if (ret) {
     994           0 :             krb5_warn(context, ret, "krb5_init_creds_set_keytab");
     995           0 :             goto out;
     996             :         }
     997         140 :     } else if (pk_user_id || ent_user_id ||
     998          70 :                krb5_principal_is_anonymous(context, principal, KRB5_ANON_MATCH_ANY)) {
     999             :         /* nop */;
    1000          70 :     } else if (!interactive && passwd[0] == '\0') {
    1001             :         static int already_warned = 0;
    1002             : 
    1003           0 :         if (!already_warned)
    1004           0 :             krb5_warnx(context, "Not interactive, failed to get "
    1005             :               "initial ticket");
    1006           0 :         krb5_get_init_creds_opt_free(context, opt);
    1007           0 :         already_warned = 1;
    1008           0 :         return 0;
    1009             :     } else {
    1010             : 
    1011          70 :         if (passwd[0] == '\0') {
    1012             :             char *p, *prompt;
    1013           4 :             int aret = 0;
    1014             : 
    1015           4 :             ret = krb5_unparse_name(context, principal, &p);
    1016           4 :             if (ret)
    1017           0 :                 errx(1, "failed to generate passwd prompt: not enough memory");
    1018             : 
    1019           4 :             aret = asprintf(&prompt, N_("%s's Password: ", ""), p);
    1020           4 :             free(p);
    1021           4 :             if (aret == -1)
    1022           0 :                 errx(1, "failed to generate passwd prompt: not enough memory");
    1023             : 
    1024           4 :             if (UI_UTIL_read_pw_string(passwd, sizeof(passwd)-1, prompt, 0)){
    1025           0 :                 memset(passwd, 0, sizeof(passwd));
    1026           0 :                 errx(1, "failed to read password");
    1027             :             }
    1028           4 :             free(prompt);
    1029             :         }
    1030             : 
    1031          70 :         if (passwd[0]) {
    1032          70 :             ret = krb5_init_creds_set_password(context, ctx, passwd);
    1033          70 :             if (ret) {
    1034           0 :                 krb5_warn(context, ret, "krb5_init_creds_set_password");
    1035           0 :                 goto out;
    1036             :             }
    1037             :         }
    1038             :     }
    1039             : 
    1040          77 :     ret = krb5_init_creds_get(context, ctx);
    1041             : 
    1042             : #ifndef NO_NTLM
    1043          77 :     if (ntlm_domain && passwd[0])
    1044           0 :         heim_ntlm_nt_key(passwd, &ntlmkey);
    1045             : #endif
    1046          77 :     memset_s(passwd, sizeof(passwd), 0, sizeof(passwd));
    1047             : 
    1048          77 :     switch(ret){
    1049          77 :     case 0:
    1050          77 :         break;
    1051           0 :     case KRB5_LIBOS_PWDINTR: /* don't print anything if it was just C-c:ed */
    1052           0 :         exit(1);
    1053           0 :     case KRB5KRB_AP_ERR_BAD_INTEGRITY:
    1054             :     case KRB5KRB_AP_ERR_MODIFIED:
    1055             :     case KRB5KDC_ERR_PREAUTH_FAILED:
    1056             :     case KRB5_GET_IN_TKT_LOOP:
    1057           0 :         krb5_warnx(context, N_("Password incorrect", ""));
    1058           0 :         goto out;
    1059           0 :     case KRB5KRB_AP_ERR_V4_REPLY:
    1060           0 :         krb5_warnx(context, N_("Looks like a Kerberos 4 reply", ""));
    1061           0 :         goto out;
    1062           0 :     case KRB5KDC_ERR_KEY_EXPIRED:
    1063           0 :         krb5_warnx(context, N_("Password expired", ""));
    1064           0 :         goto out;
    1065           0 :     default:
    1066           0 :         krb5_warn(context, ret, "krb5_get_init_creds");
    1067           0 :         goto out;
    1068             :     }
    1069             : 
    1070          77 :     krb5_process_last_request(context, opt, ctx);
    1071             : 
    1072          77 :     ret = krb5_init_creds_get_creds(context, ctx, &cred);
    1073          77 :     if (ret) {
    1074           0 :         krb5_warn(context, ret, "krb5_init_creds_get_creds");
    1075           0 :         goto out;
    1076             :     }
    1077             : 
    1078          77 :     if (ticket_life != 0) {
    1079           3 :         if (labs(cred.times.endtime - cred.times.starttime - ticket_life) > 30) {
    1080             :             char life[64];
    1081           0 :             unparse_time_approx(cred.times.endtime - cred.times.starttime,
    1082             :                                 life, sizeof(life));
    1083           0 :             krb5_warnx(context, N_("NOTICE: ticket lifetime is %s", ""), life);
    1084             :         }
    1085             :     }
    1086          77 :     if (renew_life) {
    1087           0 :         if (labs(cred.times.renew_till - cred.times.starttime - renew) > 30) {
    1088             :             char life[64];
    1089           0 :             unparse_time_approx(cred.times.renew_till - cred.times.starttime,
    1090             :                                 life, sizeof(life));
    1091           0 :             krb5_warnx(context,
    1092           0 :                        N_("NOTICE: ticket renewable lifetime is %s", ""),
    1093             :                        life);
    1094             :         }
    1095             :     }
    1096          77 :     krb5_free_cred_contents(context, &cred);
    1097             : 
    1098          77 :     ret = krb5_cc_new_unique(context, krb5_cc_get_type(context, ccache),
    1099             :                              NULL, &tempccache);
    1100          77 :     if (ret) {
    1101           0 :         krb5_warn(context, ret, "krb5_cc_new_unique");
    1102           0 :         goto out;
    1103             :     }
    1104             : 
    1105          77 :     ret = krb5_init_creds_store(context, ctx, tempccache);
    1106          77 :     if (ret) {
    1107           0 :         krb5_warn(context, ret, "krb5_init_creds_store");
    1108           0 :         goto out;
    1109             :     }
    1110             : 
    1111          77 :     ret = krb5_init_creds_store_config(context, ctx, tempccache);
    1112          77 :     if (ret) {
    1113           0 :         krb5_warn(context, ret, "krb5_init_creds_store_config");
    1114           0 :         goto out;
    1115             :     }
    1116             : 
    1117          77 :     ret = krb5_init_creds_warn_user(context, ctx);
    1118          77 :     if (ret) {
    1119           0 :         krb5_warn(context, ret, "krb5_init_creds_warn_user");
    1120           0 :         goto out;
    1121             :     }
    1122             : 
    1123          77 :     krb5_init_creds_free(context, ctx);
    1124          77 :     ctx = NULL;
    1125             : 
    1126          77 :     ret = krb5_cc_move(context, tempccache, ccache);
    1127          77 :     if (ret) {
    1128           0 :         krb5_warn(context, ret, "krb5_cc_move");
    1129           0 :         goto out;
    1130             :     }
    1131          77 :     tempccache = NULL;
    1132             : 
    1133          77 :     if (switch_cache_flags)
    1134          77 :         krb5_cc_switch(context, ccache);
    1135             : 
    1136             : #ifndef NO_NTLM
    1137          77 :     if (ntlm_domain && ntlmkey.data)
    1138           0 :         store_ntlmkey(context, ccache, ntlm_domain, &ntlmkey);
    1139             : #endif
    1140             : 
    1141          77 :     if (ok_as_delegate_flag || windows_flag || use_referrals_flag) {
    1142           9 :         unsigned char d = 0;
    1143             :         krb5_data data;
    1144             : 
    1145           9 :         if (ok_as_delegate_flag || windows_flag)
    1146           9 :             d |= 1;
    1147           9 :         if (use_referrals_flag || windows_flag)
    1148           9 :             d |= 2;
    1149             : 
    1150           9 :         data.length = 1;
    1151           9 :         data.data = &d;
    1152             : 
    1153           9 :         krb5_cc_set_config(context, ccache, NULL, "realm-config", &data);
    1154             :     }
    1155             : 
    1156          77 :     if (anonymous_pkinit) {
    1157             :         krb5_data data;
    1158             : 
    1159           0 :         data.length = strlen(principal->realm);
    1160           0 :         data.data = principal->realm;
    1161             : 
    1162           0 :         krb5_cc_set_config(context, ccache, NULL, "anon_pkinit_realm", &data);
    1163             :     }
    1164             : 
    1165         154 : out:
    1166             :     {
    1167             :         OM_uint32 minor;
    1168          77 :         gss_release_cred(&minor, &gss_cred);
    1169             :     }
    1170          77 :     krb5_free_principal(context, federated_name);
    1171          77 :     krb5_get_init_creds_opt_free(context, opt);
    1172          77 :     if (ctx)
    1173           0 :         krb5_init_creds_free(context, ctx);
    1174          77 :     if (tempccache)
    1175           0 :         krb5_cc_destroy(context, tempccache);
    1176          77 :     if (enctype)
    1177          60 :         free(enctype);
    1178             : 
    1179          77 :     return ret;
    1180             : }
    1181             : 
    1182             : static time_t
    1183           0 : ticket_lifetime(krb5_context context, krb5_ccache cache, krb5_principal client,
    1184             :                 const char *server, time_t *renew)
    1185             : {
    1186             :     krb5_creds in_cred, *cred;
    1187             :     krb5_error_code ret;
    1188             :     time_t timeout;
    1189             :     time_t curtime;
    1190             : 
    1191           0 :     memset(&in_cred, 0, sizeof(in_cred));
    1192             : 
    1193           0 :     if (renew != NULL)
    1194           0 :         *renew = 0;
    1195             : 
    1196           0 :     ret = krb5_cc_get_principal(context, cache, &in_cred.client);
    1197           0 :     if (ret) {
    1198           0 :         krb5_warn(context, ret, "krb5_cc_get_principal");
    1199           0 :         return 0;
    1200             :     }
    1201             : 
    1202             :     /* Determine TGS principal without fallback */
    1203           0 :     ret = get_server(context, cache, in_cred.client, server, FALSE,
    1204             :                      &in_cred.server);
    1205           0 :     if (ret) {
    1206           0 :         krb5_free_principal(context, in_cred.client);
    1207           0 :         krb5_warn(context, ret, "get_server");
    1208           0 :         return 0;
    1209             :     }
    1210             : 
    1211           0 :     ret = krb5_get_credentials(context, KRB5_GC_CACHED,
    1212             :                                cache, &in_cred, &cred);
    1213           0 :     krb5_free_principal(context, in_cred.client);
    1214           0 :     krb5_free_principal(context, in_cred.server);
    1215           0 :     if (ret) {
    1216           0 :         krb5_warn(context, ret, "krb5_get_credentials");
    1217           0 :         return 0;
    1218             :     }
    1219           0 :     curtime = time(NULL);
    1220           0 :     timeout = cred->times.endtime - curtime;
    1221           0 :     if (timeout < 0)
    1222           0 :         timeout = 0;
    1223           0 :     if (renew) {
    1224           0 :         *renew = cred->times.renew_till - curtime;
    1225           0 :         if (*renew < 0)
    1226           0 :             *renew = 0;
    1227             :     }
    1228           0 :     krb5_free_creds(context, cred);
    1229           0 :     return timeout;
    1230             : }
    1231             : 
    1232             : static time_t expire;
    1233             : 
    1234             : static char siginfo_msg[1024] = "No credentials\n";
    1235             : 
    1236             : static void
    1237           0 : update_siginfo_msg(time_t exp, const char *srv)
    1238             : {
    1239             :     /* Note that exp is relative time */
    1240           0 :     memset(siginfo_msg, 0, sizeof(siginfo_msg));
    1241           0 :     memcpy(&siginfo_msg, "Updating...\n", sizeof("Updating...\n"));
    1242           0 :     if (exp) {
    1243           0 :         if (srv == NULL) {
    1244           0 :             snprintf(siginfo_msg, sizeof(siginfo_msg),
    1245           0 :                      N_("kinit: TGT expires in %llu seconds\n", ""),
    1246             :                      (unsigned long long)expire);
    1247             :         } else {
    1248           0 :             snprintf(siginfo_msg, sizeof(siginfo_msg),
    1249           0 :                      N_("kinit: Ticket for %s expired\n", ""), srv);
    1250             :         }
    1251           0 :         return;
    1252             :     }
    1253             : 
    1254             :     /* Expired creds */
    1255           0 :     if (srv == NULL) {
    1256           0 :         snprintf(siginfo_msg, sizeof(siginfo_msg),
    1257           0 :                  N_("kinit: TGT expired\n", ""));
    1258             :     } else {
    1259           0 :         snprintf(siginfo_msg, sizeof(siginfo_msg),
    1260           0 :                  N_("kinit: Ticket for %s expired\n", ""), srv);
    1261             :     }
    1262             : }
    1263             : 
    1264             : #ifdef HAVE_SIGACTION
    1265             : static void
    1266           0 : handle_siginfo(int sig)
    1267             : {
    1268             :     struct iovec iov[2];
    1269             : 
    1270           0 :     iov[0].iov_base = rk_UNCONST(siginfo_msg);
    1271           0 :     iov[0].iov_len = strlen(siginfo_msg);
    1272           0 :     iov[1].iov_base = "\n";
    1273           0 :     iov[1].iov_len = 1;
    1274             : 
    1275           0 :     writev(STDERR_FILENO, iov, sizeof(iov)/sizeof(iov[0]));
    1276           0 : }
    1277             : #endif
    1278             : 
    1279             : struct renew_ctx {
    1280             :     krb5_context context;
    1281             :     krb5_ccache  ccache;
    1282             :     krb5_principal principal;
    1283             :     krb5_deltat ticket_life;
    1284             :     krb5_deltat timeout;
    1285             :     int anonymous_pkinit;
    1286             : };
    1287             : 
    1288             : static time_t
    1289           0 : renew_func(void *ptr)
    1290             : {
    1291             :     krb5_error_code ret;
    1292           0 :     struct renew_ctx *ctx = ptr;
    1293             :     time_t renew_expire;
    1294             :     static time_t exp_delay = 1;
    1295             : 
    1296             :     /*
    1297             :      * NOTE: We count on the ccache implementation to notice changes to the
    1298             :      * actual ccache filesystem/whatever objects.  There should be no ccache
    1299             :      * types for which this is not the case, but it might not hurt to
    1300             :      * re-krb5_cc_resolve() after each successful renew_validate()/
    1301             :      * get_new_tickets() call.
    1302             :      */
    1303             : 
    1304           0 :     expire = ticket_lifetime(ctx->context, ctx->ccache, ctx->principal,
    1305             :                              server_str, &renew_expire);
    1306             : 
    1307             :     /*
    1308             :      * When a keytab is available to obtain new tickets, if we are within
    1309             :      * half of the original ticket lifetime of the renew limit, get a new
    1310             :      * TGT instead of renewing the existing TGT.  Note, ctx->ticket_life
    1311             :      * is zero by default (without a '-l' option) and cannot be used to
    1312             :      * set the time scale on which we decide whether we're "close to the
    1313             :      * renew limit".
    1314             :      */
    1315           0 :     if (use_keytab || keytab_str)
    1316           0 :         expire += ctx->timeout;
    1317           0 :     if (renew_expire > expire) {
    1318           0 :         ret = renew_validate(ctx->context, 1, validate_flag, &ctx->ccache,
    1319             :                              NULL, FALSE, server_str, ctx->ticket_life);
    1320             :     } else {
    1321           0 :         ret = get_new_tickets(ctx->context, ctx->principal, ctx->ccache,
    1322             :                               ctx->ticket_life, 0, ctx->anonymous_pkinit);
    1323             :     }
    1324           0 :     expire = ticket_lifetime(ctx->context, ctx->ccache, ctx->principal,
    1325             :                              server_str, &renew_expire);
    1326             : 
    1327             : #ifndef NO_AFS
    1328           0 :     if (ret == 0 && server_str == NULL && do_afslog && k_hasafs())
    1329           0 :         krb5_afslog(ctx->context, ctx->ccache, NULL, NULL);
    1330             : #endif
    1331             : 
    1332           0 :     update_siginfo_msg(expire, server_str);
    1333             : 
    1334             :     /*
    1335             :      * If our tickets have expired and we been able to either renew them
    1336             :      * or obtain new tickets, then we still call this function but we use
    1337             :      * an exponential backoff.  This should take care of the case where
    1338             :      * we are using stored credentials but the KDC has been unavailable
    1339             :      * for some reason...
    1340             :      */
    1341             : 
    1342           0 :     if (expire < 1) {
    1343             :         /*
    1344             :          * We can't ask to keep spamming stderr but not syslog, so we warn
    1345             :          * only once.
    1346             :          */
    1347           0 :         if (exp_delay == 1) {
    1348           0 :             krb5_warnx(ctx->context, N_("NOTICE: Could not renew/refresh "
    1349             :                                         "tickets", ""));
    1350             :         }
    1351           0 :         if (exp_delay < 7200)
    1352           0 :             exp_delay += exp_delay / 2 + 1;
    1353           0 :         return exp_delay;
    1354             :     }
    1355           0 :     exp_delay = 1;
    1356             : 
    1357           0 :     return expire / 2 + 1;
    1358             : }
    1359             : 
    1360             : static void
    1361           0 : set_princ_realm(krb5_context context,
    1362             :                 krb5_principal principal,
    1363             :                 const char *realm)
    1364             : {
    1365             :     krb5_error_code ret;
    1366             : 
    1367           0 :     if ((ret = krb5_principal_set_realm(context, principal, realm)) != 0)
    1368           0 :         krb5_err(context, 1, ret, "krb5_principal_set_realm");
    1369           0 : }
    1370             : 
    1371             : static void
    1372          77 : parse_name_realm(krb5_context context,
    1373             :                  const char *name,
    1374             :                  int flags,
    1375             :                  const char *realm,
    1376             :                  krb5_principal *princ)
    1377             : {
    1378             :     krb5_error_code ret;
    1379             :     
    1380          77 :     if (realm)
    1381           0 :         flags |= KRB5_PRINCIPAL_PARSE_NO_DEF_REALM;
    1382          77 :     if ((ret = krb5_parse_name_flags(context, name, flags, princ)) != 0)
    1383           0 :         krb5_err(context, 1, ret, "krb5_parse_name_flags");
    1384          77 :     if (realm && krb5_principal_get_realm(context, *princ) == NULL)
    1385           0 :         set_princ_realm(context, *princ, realm);
    1386          77 : }
    1387             : 
    1388             : static char *
    1389           3 : get_default_realm(krb5_context context)
    1390             : {
    1391             :     char *realm;
    1392             :     krb5_error_code ret;
    1393             : 
    1394           3 :     if ((ret = krb5_get_default_realm(context, &realm)) != 0)
    1395           0 :         krb5_err(context, 1, ret, "krb5_get_default_realm");
    1396           3 :     return realm;
    1397             : }
    1398             : 
    1399             : static void
    1400           0 : get_default_principal(krb5_context context, krb5_principal *princ)
    1401             : {
    1402             :     krb5_error_code ret;
    1403             : 
    1404           0 :     if ((ret = krb5_get_default_principal(context, princ)) != 0)
    1405           0 :         krb5_err(context, 1, ret, "krb5_get_default_principal");
    1406           0 : }
    1407             : 
    1408             : static char *
    1409          70 : get_user_realm(krb5_context context)
    1410             : {
    1411             :     krb5_error_code ret;
    1412          70 :     char *user_realm = NULL;
    1413             : 
    1414             :     /*
    1415             :      * If memory allocation fails, we don't try to use the wrong realm,
    1416             :      * that will trigger misleading error messages complicate support.
    1417             :      */
    1418          70 :     krb5_appdefault_string(context, "kinit", NULL, "user_realm", "",
    1419             :                            &user_realm);
    1420          70 :     if (user_realm == NULL) {
    1421           0 :         ret = krb5_enomem(context);
    1422           0 :         krb5_err(context, 1, ret, "krb5_appdefault_string");
    1423             :     }
    1424             : 
    1425          70 :     if (*user_realm == 0) {
    1426          70 :         free(user_realm);
    1427          70 :         user_realm = NULL;
    1428             :     }
    1429             : 
    1430          70 :     return user_realm;
    1431             : }
    1432             : 
    1433             : static void
    1434          76 : get_princ(krb5_context context,
    1435             :           krb5_principal *principal,
    1436             :           const char *ccname,
    1437             :           const char *name)
    1438             : {
    1439          76 :     krb5_error_code ret = 0;
    1440             :     krb5_principal tmp;
    1441          76 :     int parseflags = 0;
    1442             :     char *user_realm;
    1443             : 
    1444          76 :     if (name == NULL) {
    1445           6 :         krb5_ccache ccache = NULL;
    1446             : 
    1447             :         /* If credential cache provides a client principal, use that. */
    1448           6 :         if (ccname)
    1449           6 :             ret = krb5_cc_resolve(context, ccname, &ccache);
    1450             :         else
    1451           0 :             ret = krb5_cc_default(context, &ccache);
    1452           6 :         if (ret  == 0)
    1453           6 :             ret = krb5_cc_get_principal(context, ccache, principal);
    1454           6 :         krb5_cc_close(context, ccache);
    1455           6 :         if (ret == 0)
    1456           6 :             return;
    1457             :     }
    1458             : 
    1459          70 :     user_realm = get_user_realm(context);
    1460             : 
    1461          70 :     if (name) {
    1462          70 :         if (enterprise_flag)
    1463          13 :             parseflags |= KRB5_PRINCIPAL_PARSE_ENTERPRISE;
    1464             : 
    1465          70 :         parse_name_realm(context, name, parseflags, user_realm, &tmp);
    1466             : 
    1467          70 :         if (user_realm && krb5_principal_get_num_comp(context, tmp) > 1) {
    1468             :             /* Principal is instance qualified, reparse with default realm. */
    1469           0 :             krb5_free_principal(context, tmp);
    1470           0 :             parse_name_realm(context, name, parseflags, NULL, principal);
    1471             :         } else {
    1472          70 :             *principal = tmp;
    1473             :         }
    1474             :     } else {
    1475           0 :         get_default_principal(context, principal);
    1476           0 :         if (user_realm)
    1477           0 :             set_princ_realm(context, *principal, user_realm);
    1478             :     }
    1479             : 
    1480          70 :     if (user_realm)
    1481           0 :         free(user_realm);
    1482             : }
    1483             : 
    1484             : static void
    1485           7 : get_princ_kt(krb5_context context,
    1486             :              krb5_principal *principal,
    1487             :              char *name)
    1488             : {
    1489             :     krb5_error_code ret;
    1490             :     krb5_principal tmp;
    1491             :     krb5_ccache ccache;
    1492             :     krb5_kt_cursor cursor;
    1493             :     krb5_keytab_entry entry;
    1494             :     char *def_realm;
    1495             : 
    1496           7 :     if (name == NULL) {
    1497             :         /*
    1498             :          * If the credential cache exists and specifies a client principal,
    1499             :          * use that.
    1500             :          */
    1501           0 :         if (krb5_cc_default(context, &ccache) == 0) {
    1502           0 :             ret = krb5_cc_get_principal(context, ccache, principal);
    1503           0 :             krb5_cc_close(context, ccache);
    1504           0 :             if (ret == 0)
    1505           4 :                 return;
    1506             :         }
    1507             :     }
    1508             : 
    1509           7 :     if (name) {
    1510             :         /* If the principal specifies an explicit realm, just use that. */
    1511           7 :         int parseflags = KRB5_PRINCIPAL_PARSE_NO_DEF_REALM;
    1512             : 
    1513           7 :         parse_name_realm(context, name, parseflags, NULL, &tmp);
    1514           7 :         if (krb5_principal_get_realm(context, tmp) != NULL) {
    1515           4 :             *principal = tmp;
    1516           4 :             return;
    1517             :         }
    1518             :     } else {
    1519             :         /* Otherwise, search keytab for bare name of the default principal. */
    1520           0 :         get_default_principal(context, &tmp);
    1521           0 :         set_princ_realm(context, tmp, NULL);
    1522             :     }
    1523             : 
    1524           3 :     def_realm = get_default_realm(context);
    1525             : 
    1526           3 :     ret = krb5_kt_start_seq_get(context, kt, &cursor);
    1527           3 :     if (ret)
    1528           0 :         krb5_err(context, 1, ret, "krb5_kt_start_seq_get");
    1529             : 
    1530          69 :     while (ret == 0 &&
    1531          33 :            krb5_kt_next_entry(context, kt, &entry, &cursor) == 0) {
    1532             :         const char *realm;
    1533             : 
    1534          30 :         if (!krb5_principal_compare_any_realm(context, tmp, entry.principal))
    1535          18 :             continue;
    1536          21 :         if (*principal &&
    1537           9 :             krb5_principal_compare(context, *principal, entry.principal))
    1538           9 :             continue;
    1539             :         /* The default realm takes precedence */
    1540           3 :         realm = krb5_principal_get_realm(context, entry.principal);
    1541           3 :         if (*principal && strcmp(def_realm, realm) == 0) {
    1542           0 :             krb5_free_principal(context, *principal);
    1543           0 :             ret = krb5_copy_principal(context, entry.principal, principal);
    1544           0 :             break;
    1545             :         }
    1546           3 :         if (!*principal)
    1547           3 :             ret = krb5_copy_principal(context, entry.principal, principal);
    1548             :     }
    1549           3 :     if (ret != 0 || (ret = krb5_kt_end_seq_get(context, kt, &cursor)) != 0)
    1550           0 :         krb5_err(context, 1, ret, "get_princ_kt");
    1551           3 :     if (!*principal) {
    1552           0 :         if (name)
    1553           0 :             parse_name_realm(context, name, 0, NULL, principal);
    1554             :         else
    1555           0 :             krb5_err(context, 1, KRB5_CC_NOTFOUND, "get_princ_kt");
    1556             :     }
    1557             : 
    1558           3 :     krb5_free_principal(context, tmp);
    1559           3 :     free(def_realm);
    1560             : }
    1561             : 
    1562             : static krb5_error_code
    1563           0 : get_switched_ccache(krb5_context context,
    1564             :                     const char * type,
    1565             :                     krb5_principal principal,
    1566             :                     krb5_ccache *ccache)
    1567             : {
    1568             :     krb5_error_code ret;
    1569             : 
    1570             : #ifdef _WIN32
    1571             :     if (strcmp(type, "API") == 0) {
    1572             :         /*
    1573             :          * Windows stores the default ccache name in the
    1574             :          * registry which is shared across multiple logon
    1575             :          * sessions for the same user.  The API credential
    1576             :          * cache provides a unique name space per logon
    1577             :          * session.  Therefore there is no need to generate
    1578             :          * a unique ccache name.  Instead use the principal
    1579             :          * name.  This provides a friendlier user experience.
    1580             :          */
    1581             :         char * unparsed_name;
    1582             :         char * cred_cache;
    1583             : 
    1584             :         ret = krb5_unparse_name(context, principal,
    1585             :                                 &unparsed_name);
    1586             :         if (ret)
    1587             :             krb5_err(context, 1, ret,
    1588             :                      N_("unparsing principal name", ""));
    1589             : 
    1590             :         ret = asprintf(&cred_cache, "API:%s", unparsed_name);
    1591             :         krb5_free_unparsed_name(context, unparsed_name);
    1592             :         if (ret == -1 || cred_cache == NULL)
    1593             :             krb5_err(context, 1, ret,
    1594             :                       N_("building credential cache name", ""));
    1595             : 
    1596             :         ret = krb5_cc_resolve(context, cred_cache, ccache);
    1597             :         free(cred_cache);
    1598             :     } else if (strcmp(type, "MSLSA") == 0) {
    1599             :         /*
    1600             :          * The Windows MSLSA cache when it is writeable
    1601             :          * stores tickets for multiple client principals
    1602             :          * in a single credential cache.
    1603             :          */
    1604             :         ret = krb5_cc_resolve(context, "MSLSA:", ccache);
    1605             :     } else {
    1606             :         ret = krb5_cc_new_unique(context, type, NULL, ccache);
    1607             :     }
    1608             : #else /* !_WIN32 */
    1609           0 :     ret = krb5_cc_new_unique(context, type, NULL, ccache);
    1610             : #endif /* _WIN32 */
    1611             : 
    1612           0 :     return ret;
    1613             : }
    1614             : 
    1615             : int
    1616          83 : main(int argc, char **argv)
    1617             : {
    1618             :     krb5_error_code ret;
    1619             :     krb5_context context;
    1620             :     krb5_ccache  ccache;
    1621          83 :     krb5_principal principal = NULL;
    1622          83 :     int optidx = 0;
    1623          83 :     krb5_deltat ticket_life = 0;
    1624             : #ifdef HAVE_SIGACTION
    1625             :     struct sigaction sa;
    1626             : #endif
    1627          83 :     krb5_boolean unique_ccache = FALSE;
    1628          83 :     krb5_boolean historical_anon_pkinit = FALSE;
    1629          83 :     int anonymous_pkinit = FALSE;
    1630             : 
    1631          83 :     setprogname(argv[0]);
    1632             : 
    1633          83 :     setlocale(LC_ALL, "");
    1634          83 :     bindtextdomain("heimdal_kuser", HEIMDAL_LOCALEDIR);
    1635          83 :     textdomain("heimdal_kuser");
    1636             : 
    1637          83 :     ret = krb5_init_context(&context);
    1638          83 :     if (ret == KRB5_CONFIG_BADFORMAT)
    1639           0 :         errx(1, "krb5_init_context failed to parse configuration file");
    1640          83 :     else if (ret)
    1641           0 :         errx(1, "krb5_init_context failed: %d", ret);
    1642             : 
    1643          83 :     if (getarg(args, sizeof(args) / sizeof(args[0]), argc, argv, &optidx))
    1644           0 :         usage(1);
    1645             : 
    1646          83 :     if (help_flag)
    1647           0 :         usage(0);
    1648             : 
    1649          83 :     if (version_flag) {
    1650           0 :         print_version(NULL);
    1651           0 :         exit(0);
    1652             :     }
    1653             : 
    1654          83 :     argc -= optidx;
    1655          83 :     argv += optidx;
    1656             : 
    1657          83 :     krb5_appdefault_boolean(context, "kinit", NULL, "historical_anon_pkinit",
    1658             :                             FALSE, &historical_anon_pkinit);
    1659             : 
    1660             :     /*
    1661             :      * Open the keytab now, we use the keytab to determine the principal's
    1662             :      * realm when the requested principal has no realm.
    1663             :      */
    1664          83 :     if (use_keytab || keytab_str) {
    1665           7 :         if (keytab_str)
    1666           7 :             ret = krb5_kt_resolve(context, keytab_str, &kt);
    1667             :         else
    1668           0 :             ret = krb5_kt_default(context, &kt);
    1669           7 :         if (ret)
    1670           0 :             krb5_err(context, 1, ret, "resolving keytab");
    1671             :     }
    1672             : 
    1673          83 :     if (pk_enterprise_flag) {
    1674           0 :         ret = krb5_pk_enterprise_cert(context, pk_user_id,
    1675             :                                       argv[0], &principal,
    1676             :                                       &ent_user_id);
    1677           0 :         if (ret)
    1678           0 :             krb5_err(context, 1, ret, "krb5_pk_enterprise_certs");
    1679             : 
    1680           0 :         pk_user_id = NULL;
    1681           0 :         if (pk_anon_fast_armor > 0)
    1682           0 :             krb5_warnx(context, N_("Ignoring --pk-anon-fast-armor "
    1683             :                                    "because --pk-user given", ""));
    1684           0 :         pk_anon_fast_armor = 0;
    1685          83 :     } else if (argc && argv[0][0] == '@' &&
    1686           0 :         (gss_preauth_mech || anonymous_flag)) {
    1687             :         const char *instance;
    1688             : 
    1689           0 :         if (gss_preauth_mech) {
    1690           0 :             instance = KRB5_FEDERATED_NAME;
    1691           0 :         } else if (anonymous_flag) {
    1692           0 :             instance = KRB5_ANON_NAME;
    1693           0 :             anonymous_pkinit = TRUE;
    1694             :         }
    1695             : 
    1696           0 :         ret = make_wellknown_name(context, &argv[0][1], instance, &principal);
    1697           0 :         if (ret)
    1698           0 :             krb5_err(context, 1, ret, "make_wellknown_name");
    1699           0 :         if (!gss_preauth_mech && pk_anon_fast_armor > 1) {
    1700           0 :             krb5_warnx(context, N_("Ignoring --pk-anon-fast-armor "
    1701             :                                    "because --anonymous given", ""));
    1702           0 :             pk_anon_fast_armor = 0;
    1703             :         }
    1704          83 :     } else if (anonymous_flag && historical_anon_pkinit) {
    1705           0 :         char *realm = argc == 0 ? get_default_realm(context) :
    1706           0 :                       argv[0][0] == '@' ? &argv[0][1] : argv[0];
    1707             : 
    1708           0 :         ret = krb5_make_principal(context, &principal, realm,
    1709             :                                   KRB5_WELLKNOWN_NAME, KRB5_ANON_NAME, NULL);
    1710           0 :         if (ret)
    1711           0 :             krb5_err(context, 1, ret, "krb5_make_principal");
    1712           0 :         krb5_principal_set_type(context, principal, KRB5_NT_WELLKNOWN);
    1713           0 :         anonymous_pkinit = TRUE;
    1714          83 :     } else if (use_keytab || keytab_str) {
    1715           7 :         get_princ_kt(context, &principal, argv[0]);
    1716          76 :     } else if (gss_preauth_mech && argc == 0 && gss_preauth_name == NULL) {
    1717             :         /*
    1718             :          * Use the federated name as a placeholder if we have neither a Kerberos
    1719             :          * nor a GSS-API client name, and we are performing GSS-API preauth.
    1720             :          */
    1721           0 :         ret = make_wellknown_name(context, get_default_realm(context),
    1722             :                                   KRB5_FEDERATED_NAME, &principal);
    1723           0 :         if (ret)
    1724           0 :             krb5_err(context, 1, ret, "make_wellknown_name");
    1725             :     } else {
    1726          76 :         get_princ(context, &principal, cred_cache, argv[0]);
    1727             :     }
    1728             : 
    1729          83 :     if (fcache_version)
    1730           0 :         krb5_set_fcache_version(context, fcache_version);
    1731             : 
    1732          83 :     if (renewable_flag == -1)
    1733             :         /* this seems somewhat pointless, but whatever */
    1734          74 :         krb5_appdefault_boolean(context, "kinit",
    1735             :                                 krb5_principal_get_realm(context, principal),
    1736             :                                 "renewable", FALSE, &renewable_flag);
    1737          83 :     if (do_afslog == -1)
    1738          83 :         krb5_appdefault_boolean(context, "kinit",
    1739             :                                 krb5_principal_get_realm(context, principal),
    1740             :                                 "afslog", TRUE, &do_afslog);
    1741             : 
    1742             :     /*
    1743             :      * Cases:
    1744             :      *
    1745             :      *  - use the given ccache
    1746             :      *  - use a new unique ccache for running a command with (in this case we
    1747             :      *    get to set KRB5CCNAME, so a new unique ccache makes sense)
    1748             :      *  - use the default ccache for the given principal as requested and do
    1749             :      *    not later switch the collection's default/primary to it
    1750             :      *  - use the default cache, possibly a new unique one that later gets
    1751             :      *    switched to it
    1752             :      *
    1753             :      * The important thing is that, except for the case where we're running a
    1754             :      * command, we _can't set KRB5CCNAME_, and we can't expect the user to read
    1755             :      * our output and figure out to set it (we could have an output-for-shell-
    1756             :      * eval mode, like ssh-agent and such, but we don't).  Therefore, in all
    1757             :      * cases where we can't set KRB5CCNAME we must do something that makes
    1758             :      * sense to the user, and that is to either initialize a given ccache, use
    1759             :      * the default, or use a subsidiary ccache named after the principal whose
    1760             :      * creds we're initializing.
    1761             :      */
    1762          83 :     if (cred_cache) {
    1763             :         /* Use the given ccache */
    1764          82 :         ret = krb5_cc_resolve(context, cred_cache, &ccache);
    1765           1 :     } else if (argc > 1) {
    1766             :         char s[1024];
    1767             : 
    1768             :         /*
    1769             :          * A command was given, so use a new unique ccache (and destroy it
    1770             :          * later).
    1771             :          */
    1772           0 :         ret = krb5_cc_new_unique(context, NULL, NULL, &ccache);
    1773           0 :         if (ret)
    1774           0 :             krb5_err(context, 1, ret, "creating cred cache");
    1775           0 :         snprintf(s, sizeof(s), "%s:%s",
    1776             :                  krb5_cc_get_type(context, ccache),
    1777             :                  krb5_cc_get_name(context, ccache));
    1778           0 :         setenv("KRB5CCNAME", s, 1);
    1779           0 :         unique_ccache = TRUE;
    1780           0 :         switch_cache_flags = 0;
    1781           1 :     } else if (default_for) {
    1782           0 :         ret = krb5_cc_default_for(context, principal, &ccache);
    1783           0 :         if (switch_cache_flags == -1)
    1784           0 :             switch_cache_flags = 0;
    1785             :     } else {
    1786           1 :         ret = krb5_cc_cache_match(context, principal, &ccache);
    1787           1 :         if (ret) {
    1788             :             const char *type;
    1789           1 :             ret = krb5_cc_default(context, &ccache);
    1790           1 :             if (ret)
    1791           0 :                 krb5_err(context, 1, ret,
    1792           0 :                          N_("resolving credentials cache", ""));
    1793             : 
    1794             :             /*
    1795             :              * Check if the type support switching, and we do,
    1796             :              * then do that instead over overwriting the current
    1797             :              * default credential
    1798             :              */
    1799           1 :             type = krb5_cc_get_type(context, ccache);
    1800           2 :             if (krb5_cc_support_switch(context, type) &&
    1801           1 :                 strcmp(type, "FILE")) {
    1802           0 :                 krb5_cc_close(context, ccache);
    1803           0 :                 ret = get_switched_ccache(context, type, principal,
    1804             :                                           &ccache);
    1805           0 :                 if (ret == 0)
    1806           0 :                     unique_ccache = TRUE;
    1807             :             }
    1808             :         }
    1809             :     }
    1810          83 :     if (ret)
    1811           0 :         krb5_err(context, 1, ret, N_("resolving credentials cache", ""));
    1812             : 
    1813             : #ifndef NO_AFS
    1814          83 :     if (argc > 1 && k_hasafs())
    1815           0 :         k_setpag();
    1816             : #endif
    1817             : 
    1818          83 :     if (lifetime) {
    1819           3 :         int tmp = parse_time(lifetime, "s");
    1820           3 :         if (tmp < 0)
    1821           0 :             errx(1, N_("unparsable time: %s", ""), lifetime);
    1822             : 
    1823           3 :         ticket_life = tmp;
    1824             :     }
    1825             : 
    1826          83 :     if (addrs_flag == 0 && extra_addresses.num_strings > 0)
    1827           0 :         krb5_errx(context, 1,
    1828           0 :                   N_("specifying both extra addresses and "
    1829             :                      "no addresses makes no sense", ""));
    1830             :     {
    1831             :         int i;
    1832             :         krb5_addresses addresses;
    1833          83 :         memset(&addresses, 0, sizeof(addresses));
    1834          83 :         for(i = 0; i < extra_addresses.num_strings; i++) {
    1835           0 :             ret = krb5_parse_address(context, extra_addresses.strings[i],
    1836             :                                      &addresses);
    1837           0 :             if (ret == 0) {
    1838           0 :                 krb5_add_extra_addresses(context, &addresses);
    1839           0 :                 krb5_free_addresses(context, &addresses);
    1840             :             }
    1841             :         }
    1842          83 :         free_getarg_strings(&extra_addresses);
    1843             :     }
    1844             : 
    1845          83 :     if (renew_flag || validate_flag) {
    1846           6 :         ret = renew_validate(context, renew_flag, validate_flag,
    1847             :                              &ccache, principal,
    1848             :                              default_for ? TRUE : FALSE, server_str,
    1849             :                              ticket_life);
    1850             : 
    1851             : #ifndef NO_AFS
    1852           6 :         if (ret == 0 && server_str == NULL && do_afslog && k_hasafs())
    1853           0 :             krb5_afslog(context, ccache, NULL, NULL);
    1854             : #endif
    1855             : 
    1856           6 :         if (unique_ccache)
    1857           0 :             krb5_cc_destroy(context, ccache);
    1858           6 :         exit(ret != 0);
    1859             :     }
    1860             : 
    1861          77 :     ret = get_new_tickets(context, principal, ccache, ticket_life,
    1862             :                           1, anonymous_pkinit);
    1863          77 :     if (ret) {
    1864           0 :         if (unique_ccache)
    1865           0 :             krb5_cc_destroy(context, ccache);
    1866           0 :         exit(1);
    1867             :     }
    1868             : 
    1869             : #ifndef NO_AFS
    1870          77 :     if (ret == 0 && server_str == NULL && do_afslog && k_hasafs())
    1871           0 :         krb5_afslog(context, ccache, NULL, NULL);
    1872             : #endif
    1873             : 
    1874          77 :     if (argc > 1) {
    1875             :         struct renew_ctx ctx;
    1876             :         time_t timeout;
    1877             : 
    1878           0 :         timeout = ticket_lifetime(context, ccache, principal,
    1879             :                                   server_str, NULL) / 2;
    1880             : 
    1881           0 :         ctx.context = context;
    1882           0 :         ctx.ccache = ccache;
    1883           0 :         ctx.principal = principal;
    1884           0 :         ctx.ticket_life = ticket_life;
    1885           0 :         ctx.timeout = timeout;
    1886           0 :         ctx.anonymous_pkinit = anonymous_pkinit;
    1887             : 
    1888             : #ifdef HAVE_SIGACTION
    1889           0 :         memset(&sa, 0, sizeof(sa));
    1890           0 :         sigemptyset(&sa.sa_mask);
    1891           0 :         sa.sa_handler = handle_siginfo;
    1892             : 
    1893           0 :         sigaction(SIGINFO, &sa, NULL);
    1894             : #endif
    1895             : 
    1896           0 :         ret = simple_execvp_timed(argv[1], argv+1,
    1897             :                                   renew_func, &ctx, timeout);
    1898             : #define EX_NOEXEC       126
    1899             : #define EX_NOTFOUND     127
    1900           0 :         if (ret == EX_NOEXEC)
    1901           0 :             krb5_warnx(context, N_("permission denied: %s", ""), argv[1]);
    1902           0 :         else if (ret == EX_NOTFOUND)
    1903           0 :             krb5_warnx(context, N_("command not found: %s", ""), argv[1]);
    1904             : 
    1905           0 :         krb5_cc_destroy(context, ccache);
    1906             : #ifndef NO_AFS
    1907           0 :         if (k_hasafs())
    1908           0 :             k_unlog();
    1909             : #endif
    1910             :     } else {
    1911          77 :         krb5_cc_close(context, ccache);
    1912          77 :         ret = 0;
    1913             :     }
    1914          77 :     krb5_free_principal(context, principal);
    1915          77 :     if (kt)
    1916           7 :         krb5_kt_close(context, kt);
    1917          77 :     krb5_free_context(context);
    1918          77 :     return ret;
    1919             : }

Generated by: LCOV version 1.13