LCOV - code coverage report
Current view: top level - third_party/heimdal/kuser - kinit.c (source / functions) Hit Total Coverage
Test: coverage report for support-claim-type-attributes 6b5c566e Lines: 380 849 44.8 %
Date: 2023-11-21 12:31:41 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          24 : 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          24 :     ret = krb5_make_principal(context, &tgs_princ, tgs_realm,
     258             :                               KRB5_TGS_NAME, tgs_realm, NULL);
     259          24 :     if (ret)
     260           0 :         return ret;
     261             : 
     262             :     /*
     263             :      * Don't fail-over to a different realm just because a TGT expired
     264             :      */
     265          24 :     options = KRB5_GC_CACHED | KRB5_GC_EXPIRED_OK;
     266             : 
     267          24 :     memset(&creds, 0, sizeof(creds));
     268          24 :     creds.client = client;
     269          24 :     creds.server = tgs_princ;
     270          24 :     ret = krb5_get_credentials(context, options, cache, &creds, &tick);
     271          24 :     if (ret == 0) {
     272          24 :         krb5_free_creds(context, tick);
     273          24 :         *out_princ = tgs_princ;
     274             :     } else {
     275           0 :         krb5_free_principal(context, tgs_princ);
     276             :     }
     277             : 
     278          24 :     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          24 : 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          24 :     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          24 :     if (tgs_service)
     305           0 :         goto done;
     306             : 
     307          24 :     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          24 :     realm = krb5_principal_get_realm(context, client);
     314          24 :     ret = tgs_principal(context, cache, client, realm, &tgs_service);
     315          24 :     if (ret == 0 || ret != KRB5_CC_NOTFOUND)
     316          24 :         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           0 :         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          24 : done:
     360          24 :     if (!ret)
     361          24 :         ret = krb5_copy_principal(context, tgs_service, princ);
     362          24 :     return ret;
     363             : }
     364             : 
     365             : static krb5_error_code
     366          24 : 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          24 :     const char *cfg_names[] = {"realm-config", "FriendlyName", "anon_pkinit_realm", NULL};
     373          24 :     const char *cfg_names_w_pname[] = {"fast_avail", NULL};
     374             :     krb5_data cfg_data;
     375             :     size_t i;
     376             : 
     377          96 :     for (i = 0; cfg_names[i]; i++) {
     378          72 :         ret = krb5_cc_get_config(context, src, NULL, cfg_names[i], &cfg_data);
     379          72 :         if (ret == KRB5_CC_NOTFOUND || ret == KRB5_CC_END) {
     380          69 :             continue;
     381           3 :         } else if (ret) {
     382           0 :             krb5_warn(context, ret, "krb5_cc_get_config");
     383           0 :             return ret;
     384             :         }
     385           3 :         ret = krb5_cc_set_config(context, dst, NULL, cfg_names[i], &cfg_data);
     386           3 :         if (ret)
     387           0 :             krb5_warn(context, ret, "krb5_cc_set_config");
     388             :     }
     389          48 :     for (i = 0; start_ticket_server && cfg_names_w_pname[i]; i++) {
     390          24 :         ret = krb5_cc_get_config(context, src, start_ticket_server,
     391             :                                  cfg_names_w_pname[i], &cfg_data);
     392          24 :         if (ret == KRB5_CC_NOTFOUND || ret == KRB5_CC_END) {
     393           0 :             continue;
     394          24 :         } else if (ret) {
     395           0 :             krb5_warn(context, ret, "krb5_cc_get_config");
     396           0 :             return ret;
     397             :         }
     398          24 :         ret = krb5_cc_set_config(context, dst, start_ticket_server,
     399             :                                  cfg_names_w_pname[i], &cfg_data);
     400          24 :         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          24 :     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          24 : 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          24 :     krb5_ccache tempccache = NULL;
     452          24 :     krb5_ccache cache = *cachep;
     453          24 :     krb5_creds in, *out = NULL;
     454             :     krb5_kdc_flags flags;
     455             : 
     456          24 :     memset(&in, 0, sizeof(in));
     457             : 
     458          24 :     ret = krb5_cc_get_principal(context, cache, &in.client);
     459          24 :     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          24 :     if (ret) {
     476           0 :         krb5_warn(context, ret, "krb5_cc_get_principal");
     477           0 :         return ret;
     478             :     }
     479             : 
     480          24 :     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          48 :     if (server == NULL &&
     490          24 :         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          24 :         ret = get_server(context, cache, in.client, server, TRUE, &in.server);
     495          24 :     if (ret) {
     496           0 :         krb5_warn(context, ret, "get_server");
     497           0 :         goto out;
     498             :     }
     499             : 
     500          24 :     if (renew) {
     501             :         /*
     502             :          * no need to check the error here, it's only to be
     503             :          * friendly to the user
     504             :          */
     505          24 :         (void) krb5_get_credentials(context, KRB5_GC_CACHED, cache, &in, &out);
     506             :     }
     507             : 
     508          24 :     flags.i = 0;
     509          24 :     flags.b.renewable         = flags.b.renew = renew;
     510          24 :     flags.b.validate          = validate;
     511             : 
     512          24 :     if (forwardable_flag != -1)
     513           0 :         flags.b.forwardable       = forwardable_flag;
     514          24 :     else if (out)
     515          24 :         flags.b.forwardable       = out->flags.b.forwardable;
     516             : 
     517          24 :     if (proxiable_flag != -1)
     518           0 :         flags.b.proxiable         = proxiable_flag;
     519          24 :     else if (out)
     520          24 :         flags.b.proxiable         = out->flags.b.proxiable;
     521             : 
     522          24 :     if (anonymous_flag)
     523           0 :         flags.b.request_anonymous = anonymous_flag;
     524          24 :     if (life)
     525           0 :         in.times.endtime = time(NULL) + life;
     526             : 
     527          24 :     if (out) {
     528          24 :         krb5_free_creds(context, out);
     529          24 :         out = NULL;
     530             :     }
     531             : 
     532             : 
     533          24 :     ret = krb5_get_kdc_cred(context,
     534             :                             cache,
     535             :                             flags,
     536             :                             NULL,
     537             :                             NULL,
     538             :                             &in,
     539             :                             &out);
     540          24 :     if (ret) {
     541           0 :         krb5_warn(context, ret, "krb5_get_kdc_cred");
     542           0 :         goto out;
     543             :     }
     544             : 
     545          24 :     ret = krb5_cc_new_unique(context, krb5_cc_get_type(context, cache),
     546             :                              NULL, &tempccache);
     547          24 :     if (ret) {
     548           0 :         krb5_warn(context, ret, "krb5_cc_new_unique");
     549           0 :         goto out;
     550             :     }
     551             : 
     552          24 :     ret = krb5_cc_initialize(context, tempccache, in.client);
     553          24 :     if (ret) {
     554           0 :         krb5_warn(context, ret, "krb5_cc_initialize");
     555           0 :         goto out;
     556             :     }
     557             : 
     558          24 :     ret = krb5_cc_store_cred(context, tempccache, out);
     559          24 :     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          24 :     ret = copy_configs(context, tempccache, cache, out->server);
     569          24 :     if (ret)
     570           0 :         goto out;
     571             : 
     572          24 :     ret = krb5_cc_move(context, tempccache, cache);
     573          24 :     if (ret) {
     574           0 :         krb5_warn(context, ret, "krb5_cc_move");
     575           0 :         goto out;
     576             :     }
     577          24 :     tempccache = NULL;
     578             : 
     579          24 : out:
     580          24 :     if (tempccache)
     581           0 :         krb5_cc_destroy(context, tempccache);
     582          24 :     if (out)
     583          24 :         krb5_free_creds(context, out);
     584          24 :     krb5_free_cred_contents(context, &in);
     585          24 :     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         124 : 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         124 :     krb5_deltat start_time = 0;
     735         124 :     krb5_deltat renew = 0;
     736         124 :     const char *renewstr = NULL;
     737         124 :     krb5_enctype *enctype = NULL;
     738         124 :     krb5_ccache tempccache = NULL;
     739         124 :     krb5_init_creds_context ctx = NULL;
     740         124 :     krb5_get_init_creds_opt *opt = NULL;
     741         124 :     krb5_prompter_fct prompter = krb5_prompter_posix;
     742         124 :     gss_cred_id_t gss_cred = GSS_C_NO_CREDENTIAL;
     743         124 :     gss_OID gss_mech = GSS_C_NO_OID;
     744         124 :     krb5_principal federated_name = NULL;
     745             : 
     746             : #ifndef NO_NTLM
     747             :     struct ntlm_buf ntlmkey;
     748         124 :     memset(&ntlmkey, 0, sizeof(ntlmkey));
     749             : #endif
     750         124 :     passwd[0] = '\0';
     751             : 
     752         124 :     if (!interactive)
     753           0 :         prompter = NULL;
     754             : 
     755         124 :     if (password_file) {
     756             :         FILE *f;
     757             : 
     758          93 :         if (strcasecmp("STDIN", password_file) == 0)
     759           0 :             f = stdin;
     760             :         else
     761          93 :             f = fopen(password_file, "r");
     762          93 :         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          93 :         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          93 :         if (f != stdin)
     776          93 :             fclose(f);
     777          93 :         passwd[strcspn(passwd, "\n")] = '\0';
     778             :     }
     779             : 
     780             : #ifdef HAVE_FRAMEWORK_SECURITY
     781             :     if (passwd[0] == '\0') {
     782             :         enum querykey {
     783             :             qk_class, qk_matchlimit, qk_service, qk_account, qk_secreturndata,
     784             :         };
     785             :         const void *querykeys[] = {
     786             :             [qk_class] = kSecClass,
     787             :             [qk_matchlimit] = kSecMatchLimit,
     788             :             [qk_service] = kSecAttrService,
     789             :             [qk_account] = kSecAttrAccount,
     790             :             [qk_secreturndata] = kSecReturnData,
     791             :         };
     792             :         const void *queryargs[] = {
     793             :             [qk_class] = kSecClassGenericPassword,
     794             :             [qk_matchlimit] = kSecMatchLimitOne,
     795             :             [qk_service] = NULL, /* filled in later */
     796             :             [qk_account] = NULL, /* filled in later */
     797             :             [qk_secreturndata] = kCFBooleanTrue,
     798             :         };
     799             :         CFStringRef service_ref = NULL;
     800             :         CFStringRef account_ref = NULL;
     801             :         CFDictionaryRef query_ref = NULL;
     802             :         const char *realm;
     803             :         OSStatus osret;
     804             :         char *name = NULL;
     805             :         CFTypeRef item_ref = NULL;
     806             :         CFDataRef item;
     807             :         CFIndex length;
     808             : 
     809             :         realm = krb5_principal_get_realm(context, principal);
     810             : 
     811             :         ret = krb5_unparse_name_flags(context, principal,
     812             :                                       KRB5_PRINCIPAL_UNPARSE_NO_REALM, &name);
     813             :         if (ret)
     814             :             goto fail;
     815             : 
     816             :         service_ref = CFStringCreateWithCString(kCFAllocatorDefault, realm,
     817             :             kCFStringEncodingUTF8);
     818             :         if (service_ref == NULL)
     819             :             goto fail;
     820             : 
     821             :         account_ref = CFStringCreateWithCString(kCFAllocatorDefault, name,
     822             :             kCFStringEncodingUTF8);
     823             :         if (account_ref == NULL)
     824             :             goto fail;
     825             : 
     826             :         queryargs[qk_service] = service_ref;
     827             :         queryargs[qk_account] = account_ref;
     828             :         query_ref = CFDictionaryCreate(kCFAllocatorDefault,
     829             :             querykeys, queryargs,
     830             :             /*numValues*/sizeof(querykeys)/sizeof(querykeys[0]),
     831             :             /*keyCallbacks*/NULL, /*valueCallbacks*/NULL);
     832             :         if (query_ref == NULL)
     833             :             goto fail;
     834             : 
     835             :         osret = SecItemCopyMatching(query_ref, &item_ref);
     836             :         if (osret != noErr)
     837             :             goto fail;
     838             : 
     839             :         item = item_ref;
     840             :         length = CFDataGetLength(item);
     841             :         if (length >= sizeof(passwd) - 1)
     842             :             goto fail;
     843             : 
     844             :         CFDataGetBytes(item, CFRangeMake(0, length), (UInt8 *)passwd);
     845             :         passwd[length] = '\0';
     846             : 
     847             :     fail:
     848             :         if (item_ref)
     849             :             CFRelease(item_ref);
     850             :         if (query_ref)
     851             :             CFRelease(query_ref);
     852             :         if (account_ref)
     853             :             CFRelease(account_ref);
     854             :         if (service_ref)
     855             :             CFRelease(service_ref);
     856             :         free(name);
     857             :     }
     858             : #endif
     859             : 
     860         124 :     memset(&cred, 0, sizeof(cred));
     861             : 
     862         124 :     ret = krb5_get_init_creds_opt_alloc(context, &opt);
     863         124 :     if (ret) {
     864           0 :         krb5_warn(context, ret, "krb5_get_init_creds_opt_alloc");
     865           0 :         goto out;
     866             :     }
     867             : 
     868         124 :     krb5_get_init_creds_opt_set_default_flags(context, "kinit",
     869             :         krb5_principal_get_realm(context, principal), opt);
     870             : 
     871         124 :     if (forwardable_flag != -1)
     872           4 :         krb5_get_init_creds_opt_set_forwardable(opt, forwardable_flag);
     873         124 :     if (proxiable_flag != -1)
     874           0 :         krb5_get_init_creds_opt_set_proxiable(opt, proxiable_flag);
     875         124 :     if (anonymous_flag)
     876           0 :         krb5_get_init_creds_opt_set_anonymous(opt, anonymous_flag);
     877         124 :     if (pac_flag != -1)
     878          25 :         krb5_get_init_creds_opt_set_pac_request(context, opt,
     879             :                                                 pac_flag ? TRUE : FALSE);
     880         124 :     if (canonicalize_flag)
     881           0 :         krb5_get_init_creds_opt_set_canonicalize(context, opt, TRUE);
     882         124 :     if (pk_enterprise_flag || enterprise_flag || canonicalize_flag || windows_flag)
     883          34 :         krb5_get_init_creds_opt_set_win2k(context, opt, TRUE);
     884         124 :     if (pk_user_id || ent_user_id || anonymous_pkinit) {
     885          20 :         if (pk_anon_fast_armor == -1)
     886          20 :             pk_anon_fast_armor = 0;
     887          20 :         ret = krb5_get_init_creds_opt_set_pkinit(context, opt,
     888             :                                                  principal,
     889             :                                                  pk_user_id,
     890             :                                                  pk_x509_anchors,
     891             :                                                  NULL,
     892             :                                                  NULL,
     893          20 :                                                  pk_use_enckey ? KRB5_GIC_OPT_PKINIT_USE_ENCKEY : 0 |
     894          20 :                                                  anonymous_pkinit ? KRB5_GIC_OPT_PKINIT_ANONYMOUS : 0,
     895             :                                                  prompter,
     896             :                                                  NULL,
     897             :                                                  passwd);
     898          20 :         if (ret) {
     899           0 :             krb5_warn(context, ret, "krb5_get_init_creds_opt_set_pkinit");
     900           0 :             goto out;
     901             :         }
     902          20 :         if (ent_user_id)
     903           0 :             krb5_get_init_creds_opt_set_pkinit_user_certs(context, opt, ent_user_id);
     904             :     }
     905             : 
     906         124 :     if (addrs_flag != -1)
     907           0 :         krb5_get_init_creds_opt_set_addressless(context, opt,
     908             :                                                 addrs_flag ? FALSE : TRUE);
     909             : 
     910         124 :     if (renew_life == NULL && renewable_flag)
     911          32 :         renewstr = "6 months";
     912         124 :     if (renew_life)
     913           0 :         renewstr = renew_life;
     914         124 :     if (renewstr) {
     915          32 :         renew = parse_time(renewstr, "s");
     916          32 :         if (renew < 0)
     917           0 :             errx(1, "unparsable time: %s", renewstr);
     918             : 
     919          32 :         krb5_get_init_creds_opt_set_renew_life(opt, renew);
     920             :     }
     921             : 
     922         124 :     if (ticket_life != 0)
     923           3 :         krb5_get_init_creds_opt_set_tkt_life(opt, ticket_life);
     924             : 
     925         124 :     if (start_str) {
     926           0 :         int tmp = parse_time(start_str, "s");
     927           0 :         if (tmp < 0)
     928           0 :             errx(1, N_("unparsable time: %s", ""), start_str);
     929             : 
     930           0 :         start_time = tmp;
     931             :     }
     932             : 
     933         124 :     if (etype_str.num_strings) {
     934             :         int i;
     935             : 
     936           2 :         enctype = malloc(etype_str.num_strings * sizeof(*enctype));
     937           2 :         if (enctype == NULL)
     938           0 :             errx(1, "out of memory");
     939           4 :         for(i = 0; i < etype_str.num_strings; i++) {
     940           2 :             ret = krb5_string_to_enctype(context,
     941           2 :                                          etype_str.strings[i],
     942           2 :                                          &enctype[i]);
     943           2 :             if (ret)
     944           0 :                 errx(1, "unrecognized enctype: %s", etype_str.strings[i]);
     945             :         }
     946           2 :         krb5_get_init_creds_opt_set_etype_list(opt, enctype,
     947             :                                                etype_str.num_strings);
     948             :     }
     949             : 
     950         124 :     if (gss_preauth_mech || gss_preauth_name) {
     951           0 :         ret = acquire_gss_cred(context, principal, ticket_life,
     952             :                                passwd, &gss_cred, &gss_mech);
     953           0 :         if (ret)
     954           0 :             goto out;
     955             : 
     956             :         /*
     957             :          * The principal specified on the command line is used as the GSS-API
     958             :          * initiator name, unless the --gss-name option was present, in which
     959             :          * case the initiator name is specified independently.
     960             :          */
     961           0 :         if (gss_preauth_name == NULL) {
     962           0 :             krb5_const_realm realm = krb5_principal_get_realm(context, principal);
     963             : 
     964           0 :             ret = make_wellknown_name(context, realm,
     965             :                                       KRB5_FEDERATED_NAME, &federated_name);
     966           0 :             if (ret)
     967           0 :                 goto out;
     968             : 
     969           0 :             principal = federated_name;
     970             :         }
     971             :     }
     972             : 
     973         124 :     ret = krb5_init_creds_init(context, principal, prompter, NULL, start_time, opt, &ctx);
     974         124 :     if (ret) {
     975           0 :         krb5_warn(context, ret, "krb5_init_creds_init");
     976           0 :         goto out;
     977             :     }
     978             : 
     979         124 :     if (server_str) {
     980           6 :         ret = krb5_init_creds_set_service(context, ctx, server_str);
     981           6 :         if (ret) {
     982           0 :             krb5_warn(context, ret, "krb5_init_creds_set_service");
     983           0 :             goto out;
     984             :         }
     985             :     }
     986             : 
     987         124 :     if (kdc_hostname) {
     988           0 :         ret = krb5_init_creds_set_kdc_hostname(context, ctx, kdc_hostname);
     989           0 :         if (ret) {
     990           0 :             krb5_warn(context, ret, "krb5_init_creds_set_kdc_hostname");
     991           0 :             goto out;
     992             :         }
     993             :     }
     994             : 
     995         124 :     if (anonymous_flag && pk_anon_fast_armor == -1)
     996           0 :         pk_anon_fast_armor = 0;
     997         124 :     if (!gss_preauth_mech && anonymous_flag && pk_anon_fast_armor) {
     998           0 :         krb5_warnx(context, N_("Ignoring --pk-anon-fast-armor because "
     999             :                                "--anonymous given", ""));
    1000           0 :         pk_anon_fast_armor = 0;
    1001             :     }
    1002             : 
    1003         124 :     if (fast_armor_cache_string) {
    1004           0 :         krb5_ccache fastid = NULL;
    1005             : 
    1006           0 :         if (pk_anon_fast_armor > 0)
    1007           0 :             krb5_errx(context, 1,
    1008           0 :                 N_("cannot specify FAST armor cache with FAST "
    1009             :                      "anonymous PKINIT option", ""));
    1010           0 :         pk_anon_fast_armor = 0;
    1011             : 
    1012           0 :         ret = krb5_cc_resolve(context, fast_armor_cache_string, &fastid);
    1013           0 :         if (ret) {
    1014           0 :             krb5_warn(context, ret, "krb5_cc_resolve(FAST cache)");
    1015           0 :             goto out;
    1016             :         }
    1017             : 
    1018           0 :         ret = krb5_init_creds_set_fast_ccache(context, ctx, fastid);
    1019           0 :         if (ret) {
    1020           0 :             krb5_warn(context, ret, "krb5_init_creds_set_fast_ccache");
    1021           0 :             goto out;
    1022             :         }
    1023         124 :     } else if (pk_anon_fast_armor == -1) {
    1024         104 :         ret = _krb5_init_creds_set_fast_anon_pkinit_optimistic(context, ctx);
    1025         104 :         if (ret) {
    1026           0 :             krb5_warn(context, ret, "_krb5_init_creds_set_fast_anon_pkinit_optimistic");
    1027           0 :             goto out;
    1028             :         }
    1029          20 :     } else if (pk_anon_fast_armor) {
    1030           0 :         ret = krb5_init_creds_set_fast_anon_pkinit(context, ctx);
    1031           0 :         if (ret) {
    1032           0 :             krb5_warn(context, ret, "krb5_init_creds_set_fast_anon_pkinit");
    1033           0 :             goto out;
    1034             :         }
    1035             :     }
    1036             : 
    1037         124 :     if (gss_mech != GSS_C_NO_OID) {
    1038           0 :         ret = krb5_gss_set_init_creds(context, ctx, gss_cred, gss_mech);
    1039           0 :         if (ret) {
    1040           0 :             krb5_warn(context, ret, "krb5_gss_set_init_creds");
    1041           0 :             goto out;
    1042             :         }
    1043         124 :     } else if (use_keytab || keytab_str) {
    1044           7 :         ret = krb5_init_creds_set_keytab(context, ctx, kt);
    1045           7 :         if (ret) {
    1046           0 :             krb5_warn(context, ret, "krb5_init_creds_set_keytab");
    1047           0 :             goto out;
    1048             :         }
    1049         214 :     } else if (pk_user_id || ent_user_id ||
    1050          97 :                krb5_principal_is_anonymous(context, principal, KRB5_ANON_MATCH_ANY)) {
    1051             :         /* nop */;
    1052          97 :     } else if (!interactive && passwd[0] == '\0') {
    1053             :         static int already_warned = 0;
    1054             : 
    1055           0 :         if (!already_warned)
    1056           0 :             krb5_warnx(context, "Not interactive, failed to get "
    1057             :               "initial ticket");
    1058           0 :         krb5_get_init_creds_opt_free(context, opt);
    1059           0 :         already_warned = 1;
    1060           0 :         return 0;
    1061             :     } else {
    1062             : 
    1063          97 :         if (passwd[0] == '\0') {
    1064             :             char *p, *prompt;
    1065           4 :             int aret = 0;
    1066             : 
    1067           4 :             ret = krb5_unparse_name(context, principal, &p);
    1068           4 :             if (ret)
    1069           0 :                 errx(1, "failed to generate passwd prompt: not enough memory");
    1070             : 
    1071           4 :             aret = asprintf(&prompt, N_("%s's Password: ", ""), p);
    1072           4 :             free(p);
    1073           4 :             if (aret == -1)
    1074           0 :                 errx(1, "failed to generate passwd prompt: not enough memory");
    1075             : 
    1076           4 :             if (UI_UTIL_read_pw_string(passwd, sizeof(passwd)-1, prompt, 0)){
    1077           0 :                 memset(passwd, 0, sizeof(passwd));
    1078           0 :                 errx(1, "failed to read password");
    1079             :             }
    1080           4 :             free(prompt);
    1081             :         }
    1082             : 
    1083          97 :         if (passwd[0]) {
    1084          97 :             ret = krb5_init_creds_set_password(context, ctx, passwd);
    1085          97 :             if (ret) {
    1086           0 :                 krb5_warn(context, ret, "krb5_init_creds_set_password");
    1087           0 :                 goto out;
    1088             :             }
    1089             :         }
    1090             :     }
    1091             : 
    1092         124 :     ret = krb5_init_creds_get(context, ctx);
    1093             : 
    1094             : #ifndef NO_NTLM
    1095         124 :     if (ntlm_domain && passwd[0])
    1096           0 :         heim_ntlm_nt_key(passwd, &ntlmkey);
    1097             : #endif
    1098         124 :     memset_s(passwd, sizeof(passwd), 0, sizeof(passwd));
    1099             : 
    1100         124 :     switch(ret){
    1101         113 :     case 0:
    1102         113 :         break;
    1103           0 :     case KRB5_LIBOS_PWDINTR: /* don't print anything if it was just C-c:ed */
    1104           0 :         exit(1);
    1105           3 :     case KRB5KRB_AP_ERR_BAD_INTEGRITY:
    1106             :     case KRB5KRB_AP_ERR_MODIFIED:
    1107             :     case KRB5KDC_ERR_PREAUTH_FAILED:
    1108             :     case KRB5_GET_IN_TKT_LOOP:
    1109           3 :         krb5_warnx(context, N_("Password incorrect", ""));
    1110           3 :         goto out;
    1111           0 :     case KRB5KRB_AP_ERR_V4_REPLY:
    1112           0 :         krb5_warnx(context, N_("Looks like a Kerberos 4 reply", ""));
    1113           0 :         goto out;
    1114           0 :     case KRB5KDC_ERR_KEY_EXPIRED:
    1115           0 :         krb5_warnx(context, N_("Password expired", ""));
    1116           0 :         goto out;
    1117           8 :     default:
    1118           8 :         krb5_warn(context, ret, "krb5_get_init_creds");
    1119           8 :         goto out;
    1120             :     }
    1121             : 
    1122         113 :     krb5_process_last_request(context, opt, ctx);
    1123             : 
    1124         113 :     ret = krb5_init_creds_get_creds(context, ctx, &cred);
    1125         113 :     if (ret) {
    1126           0 :         krb5_warn(context, ret, "krb5_init_creds_get_creds");
    1127           0 :         goto out;
    1128             :     }
    1129             : 
    1130         113 :     if (ticket_life != 0) {
    1131           3 :         if (labs(cred.times.endtime - cred.times.starttime - ticket_life) > 30) {
    1132             :             char life[64];
    1133           0 :             unparse_time_approx(cred.times.endtime - cred.times.starttime,
    1134             :                                 life, sizeof(life));
    1135           0 :             krb5_warnx(context, N_("NOTICE: ticket lifetime is %s", ""), life);
    1136             :         }
    1137             :     }
    1138         113 :     if (renew_life) {
    1139           0 :         if (labs(cred.times.renew_till - cred.times.starttime - renew) > 30) {
    1140             :             char life[64];
    1141           0 :             unparse_time_approx(cred.times.renew_till - cred.times.starttime,
    1142             :                                 life, sizeof(life));
    1143           0 :             krb5_warnx(context,
    1144           0 :                        N_("NOTICE: ticket renewable lifetime is %s", ""),
    1145             :                        life);
    1146             :         }
    1147             :     }
    1148         113 :     krb5_free_cred_contents(context, &cred);
    1149             : 
    1150         113 :     ret = krb5_cc_new_unique(context, krb5_cc_get_type(context, ccache),
    1151             :                              NULL, &tempccache);
    1152         113 :     if (ret) {
    1153           0 :         krb5_warn(context, ret, "krb5_cc_new_unique");
    1154           0 :         goto out;
    1155             :     }
    1156             : 
    1157         113 :     ret = krb5_init_creds_store(context, ctx, tempccache);
    1158         113 :     if (ret) {
    1159           0 :         krb5_warn(context, ret, "krb5_init_creds_store");
    1160           0 :         goto out;
    1161             :     }
    1162             : 
    1163         113 :     ret = krb5_init_creds_store_config(context, ctx, tempccache);
    1164         113 :     if (ret) {
    1165           0 :         krb5_warn(context, ret, "krb5_init_creds_store_config");
    1166           0 :         goto out;
    1167             :     }
    1168             : 
    1169         113 :     ret = krb5_init_creds_warn_user(context, ctx);
    1170         113 :     if (ret) {
    1171           0 :         krb5_warn(context, ret, "krb5_init_creds_warn_user");
    1172           0 :         goto out;
    1173             :     }
    1174             : 
    1175         113 :     krb5_init_creds_free(context, ctx);
    1176         113 :     ctx = NULL;
    1177             : 
    1178         113 :     ret = krb5_cc_move(context, tempccache, ccache);
    1179         113 :     if (ret) {
    1180           0 :         krb5_warn(context, ret, "krb5_cc_move");
    1181           0 :         goto out;
    1182             :     }
    1183         113 :     tempccache = NULL;
    1184             : 
    1185         113 :     if (switch_cache_flags)
    1186         113 :         krb5_cc_switch(context, ccache);
    1187             : 
    1188             : #ifndef NO_NTLM
    1189         113 :     if (ntlm_domain && ntlmkey.data)
    1190           0 :         store_ntlmkey(context, ccache, ntlm_domain, &ntlmkey);
    1191             : #endif
    1192             : 
    1193         113 :     if (ok_as_delegate_flag || windows_flag || use_referrals_flag) {
    1194           6 :         unsigned char d = 0;
    1195             :         krb5_data data;
    1196             : 
    1197           6 :         if (ok_as_delegate_flag || windows_flag)
    1198           6 :             d |= 1;
    1199           6 :         if (use_referrals_flag || windows_flag)
    1200           6 :             d |= 2;
    1201             : 
    1202           6 :         data.length = 1;
    1203           6 :         data.data = &d;
    1204             : 
    1205           6 :         krb5_cc_set_config(context, ccache, NULL, "realm-config", &data);
    1206             :     }
    1207             : 
    1208         113 :     if (anonymous_pkinit) {
    1209             :         krb5_data data;
    1210             : 
    1211           0 :         data.length = strlen(principal->realm);
    1212           0 :         data.data = principal->realm;
    1213             : 
    1214           0 :         krb5_cc_set_config(context, ccache, NULL, "anon_pkinit_realm", &data);
    1215             :     }
    1216             : 
    1217         113 : out:
    1218             :     {
    1219             :         OM_uint32 minor;
    1220         124 :         gss_release_cred(&minor, &gss_cred);
    1221             :     }
    1222         124 :     krb5_free_principal(context, federated_name);
    1223         124 :     krb5_get_init_creds_opt_free(context, opt);
    1224         124 :     if (ctx)
    1225          11 :         krb5_init_creds_free(context, ctx);
    1226         124 :     if (tempccache)
    1227           0 :         krb5_cc_destroy(context, tempccache);
    1228         124 :     if (enctype)
    1229           2 :         free(enctype);
    1230             : 
    1231         124 :     return ret;
    1232             : }
    1233             : 
    1234             : static time_t
    1235           0 : ticket_lifetime(krb5_context context, krb5_ccache cache, krb5_principal client,
    1236             :                 const char *server, time_t *renew)
    1237             : {
    1238             :     krb5_creds in_cred, *cred;
    1239             :     krb5_error_code ret;
    1240             :     time_t timeout;
    1241             :     time_t curtime;
    1242             : 
    1243           0 :     memset(&in_cred, 0, sizeof(in_cred));
    1244             : 
    1245           0 :     if (renew != NULL)
    1246           0 :         *renew = 0;
    1247             : 
    1248           0 :     ret = krb5_cc_get_principal(context, cache, &in_cred.client);
    1249           0 :     if (ret) {
    1250           0 :         krb5_warn(context, ret, "krb5_cc_get_principal");
    1251           0 :         return 0;
    1252             :     }
    1253             : 
    1254             :     /* Determine TGS principal without fallback */
    1255           0 :     ret = get_server(context, cache, in_cred.client, server, FALSE,
    1256             :                      &in_cred.server);
    1257           0 :     if (ret) {
    1258           0 :         krb5_free_principal(context, in_cred.client);
    1259           0 :         krb5_warn(context, ret, "get_server");
    1260           0 :         return 0;
    1261             :     }
    1262             : 
    1263           0 :     ret = krb5_get_credentials(context, KRB5_GC_CACHED,
    1264             :                                cache, &in_cred, &cred);
    1265           0 :     krb5_free_principal(context, in_cred.client);
    1266           0 :     krb5_free_principal(context, in_cred.server);
    1267           0 :     if (ret) {
    1268           0 :         krb5_warn(context, ret, "krb5_get_credentials");
    1269           0 :         return 0;
    1270             :     }
    1271           0 :     curtime = time(NULL);
    1272           0 :     timeout = cred->times.endtime - curtime;
    1273           0 :     if (timeout < 0)
    1274           0 :         timeout = 0;
    1275           0 :     if (renew) {
    1276           0 :         *renew = cred->times.renew_till - curtime;
    1277           0 :         if (*renew < 0)
    1278           0 :             *renew = 0;
    1279             :     }
    1280           0 :     krb5_free_creds(context, cred);
    1281           0 :     return timeout;
    1282             : }
    1283             : 
    1284             : static time_t expire;
    1285             : 
    1286             : static char siginfo_msg[1024] = "No credentials\n";
    1287             : 
    1288             : static void
    1289           0 : update_siginfo_msg(time_t exp, const char *srv)
    1290             : {
    1291             :     /* Note that exp is relative time */
    1292           0 :     memset(siginfo_msg, 0, sizeof(siginfo_msg));
    1293           0 :     memcpy(&siginfo_msg, "Updating...\n", sizeof("Updating...\n"));
    1294           0 :     if (exp) {
    1295           0 :         if (srv == NULL) {
    1296           0 :             snprintf(siginfo_msg, sizeof(siginfo_msg),
    1297           0 :                      N_("kinit: TGT expires in %llu seconds\n", ""),
    1298             :                      (unsigned long long)expire);
    1299             :         } else {
    1300           0 :             snprintf(siginfo_msg, sizeof(siginfo_msg),
    1301           0 :                      N_("kinit: Ticket for %s expired\n", ""), srv);
    1302             :         }
    1303           0 :         return;
    1304             :     }
    1305             : 
    1306             :     /* Expired creds */
    1307           0 :     if (srv == NULL) {
    1308           0 :         snprintf(siginfo_msg, sizeof(siginfo_msg),
    1309           0 :                  N_("kinit: TGT expired\n", ""));
    1310             :     } else {
    1311           0 :         snprintf(siginfo_msg, sizeof(siginfo_msg),
    1312           0 :                  N_("kinit: Ticket for %s expired\n", ""), srv);
    1313             :     }
    1314             : }
    1315             : 
    1316             : #ifdef HAVE_SIGACTION
    1317             : static void
    1318           0 : handler(int sig)
    1319             : {
    1320           0 :     if (sig == SIGINFO) {
    1321             :         struct iovec iov[2];
    1322             : 
    1323           0 :         iov[0].iov_base = rk_UNCONST(siginfo_msg);
    1324           0 :         iov[0].iov_len = strlen(siginfo_msg);
    1325           0 :         iov[1].iov_base = "\n";
    1326           0 :         iov[1].iov_len = 1;
    1327             : 
    1328           0 :         writev(STDERR_FILENO, iov, sizeof(iov)/sizeof(iov[0]));
    1329             :     } /* else ignore interrupts; our progeny will not ignore them */
    1330           0 : }
    1331             : #endif
    1332             : 
    1333             : struct renew_ctx {
    1334             :     krb5_context context;
    1335             :     krb5_ccache  ccache;
    1336             :     krb5_principal principal;
    1337             :     krb5_deltat ticket_life;
    1338             :     krb5_deltat timeout;
    1339             :     int anonymous_pkinit;
    1340             : };
    1341             : 
    1342             : static time_t
    1343           0 : renew_func(void *ptr)
    1344             : {
    1345             :     krb5_error_code ret;
    1346           0 :     struct renew_ctx *ctx = ptr;
    1347             :     time_t renew_expire;
    1348             :     static time_t exp_delay = 1;
    1349             : 
    1350             :     /*
    1351             :      * NOTE: We count on the ccache implementation to notice changes to the
    1352             :      * actual ccache filesystem/whatever objects.  There should be no ccache
    1353             :      * types for which this is not the case, but it might not hurt to
    1354             :      * re-krb5_cc_resolve() after each successful renew_validate()/
    1355             :      * get_new_tickets() call.
    1356             :      */
    1357             : 
    1358           0 :     expire = ticket_lifetime(ctx->context, ctx->ccache, ctx->principal,
    1359             :                              server_str, &renew_expire);
    1360             : 
    1361             :     /*
    1362             :      * When a keytab is available to obtain new tickets, if we are within
    1363             :      * half of the original ticket lifetime of the renew limit, get a new
    1364             :      * TGT instead of renewing the existing TGT.  Note, ctx->ticket_life
    1365             :      * is zero by default (without a '-l' option) and cannot be used to
    1366             :      * set the time scale on which we decide whether we're "close to the
    1367             :      * renew limit".
    1368             :      */
    1369           0 :     if (use_keytab || keytab_str)
    1370           0 :         expire += ctx->timeout;
    1371           0 :     if (renew_expire > expire) {
    1372           0 :         ret = renew_validate(ctx->context, 1, validate_flag, &ctx->ccache,
    1373             :                              NULL, FALSE, server_str, ctx->ticket_life);
    1374             :     } else {
    1375           0 :         ret = get_new_tickets(ctx->context, ctx->principal, ctx->ccache,
    1376             :                               ctx->ticket_life, 0, ctx->anonymous_pkinit);
    1377             :     }
    1378           0 :     if (ret == 0) {
    1379           0 :         expire = ticket_lifetime(ctx->context, ctx->ccache, ctx->principal,
    1380             :                                  server_str, &renew_expire);
    1381             : 
    1382             : #ifndef NO_AFS
    1383           0 :         if (server_str == NULL && do_afslog && k_hasafs())
    1384           0 :             krb5_afslog(ctx->context, ctx->ccache, NULL, NULL);
    1385             : #endif
    1386             :     }
    1387             : 
    1388           0 :     update_siginfo_msg(expire, server_str);
    1389             : 
    1390             :     /*
    1391             :      * If our tickets have expired and we been able to either renew them
    1392             :      * or obtain new tickets, then we still call this function but we use
    1393             :      * an exponential backoff.  This should take care of the case where
    1394             :      * we are using stored credentials but the KDC has been unavailable
    1395             :      * for some reason...
    1396             :      */
    1397             : 
    1398           0 :     if (expire < 1) {
    1399             :         /*
    1400             :          * We can't ask to keep spamming stderr but not syslog, so we warn
    1401             :          * only once.
    1402             :          */
    1403           0 :         if (exp_delay == 1) {
    1404           0 :             krb5_warnx(ctx->context, N_("NOTICE: Could not renew/refresh "
    1405             :                                         "tickets", ""));
    1406             :         }
    1407           0 :         if (exp_delay < 7200)
    1408           0 :             exp_delay += exp_delay / 2 + 1;
    1409           0 :         return exp_delay;
    1410             :     }
    1411           0 :     exp_delay = 1;
    1412             : 
    1413           0 :     return expire / 2 + 1;
    1414             : }
    1415             : 
    1416             : static void
    1417           0 : set_princ_realm(krb5_context context,
    1418             :                 krb5_principal principal,
    1419             :                 const char *realm)
    1420             : {
    1421             :     krb5_error_code ret;
    1422             : 
    1423           0 :     if ((ret = krb5_principal_set_realm(context, principal, realm)) != 0)
    1424           0 :         krb5_err(context, 1, ret, "krb5_principal_set_realm");
    1425           0 : }
    1426             : 
    1427             : static void
    1428         119 : parse_name_realm(krb5_context context,
    1429             :                  const char *name,
    1430             :                  int flags,
    1431             :                  const char *realm,
    1432             :                  krb5_principal *princ)
    1433             : {
    1434             :     krb5_error_code ret;
    1435             :     
    1436         119 :     if (realm)
    1437           0 :         flags |= KRB5_PRINCIPAL_PARSE_NO_DEF_REALM;
    1438         119 :     if ((ret = krb5_parse_name_flags(context, name, flags, princ)) != 0)
    1439           0 :         krb5_err(context, 1, ret, "krb5_parse_name_flags");
    1440         119 :     if (realm && krb5_principal_get_realm(context, *princ) == NULL)
    1441           0 :         set_princ_realm(context, *princ, realm);
    1442         119 : }
    1443             : 
    1444             : static char *
    1445           3 : get_default_realm(krb5_context context)
    1446             : {
    1447             :     char *realm;
    1448             :     krb5_error_code ret;
    1449             : 
    1450           3 :     if ((ret = krb5_get_default_realm(context, &realm)) != 0)
    1451           0 :         krb5_err(context, 1, ret, "krb5_get_default_realm");
    1452           3 :     return realm;
    1453             : }
    1454             : 
    1455             : static void
    1456           0 : get_default_principal(krb5_context context, krb5_principal *princ)
    1457             : {
    1458             :     krb5_error_code ret;
    1459             : 
    1460           0 :     if ((ret = krb5_get_default_principal(context, princ)) != 0)
    1461           0 :         krb5_err(context, 1, ret, "krb5_get_default_principal");
    1462           0 : }
    1463             : 
    1464             : static char *
    1465         112 : get_user_realm(krb5_context context)
    1466             : {
    1467             :     krb5_error_code ret;
    1468         112 :     char *user_realm = NULL;
    1469             : 
    1470             :     /*
    1471             :      * If memory allocation fails, we don't try to use the wrong realm,
    1472             :      * that will trigger misleading error messages complicate support.
    1473             :      */
    1474         112 :     krb5_appdefault_string(context, "kinit", NULL, "user_realm", "",
    1475             :                            &user_realm);
    1476         112 :     if (user_realm == NULL) {
    1477           0 :         ret = krb5_enomem(context);
    1478           0 :         krb5_err(context, 1, ret, "krb5_appdefault_string");
    1479             :     }
    1480             : 
    1481         112 :     if (*user_realm == 0) {
    1482         112 :         free(user_realm);
    1483         112 :         user_realm = NULL;
    1484             :     }
    1485             : 
    1486         112 :     return user_realm;
    1487             : }
    1488             : 
    1489             : static void
    1490         141 : get_princ(krb5_context context,
    1491             :           krb5_principal *principal,
    1492             :           const char *ccname,
    1493             :           const char *name)
    1494             : {
    1495         141 :     krb5_error_code ret = 0;
    1496             :     krb5_principal tmp;
    1497         141 :     int parseflags = 0;
    1498             :     char *user_realm;
    1499             : 
    1500         141 :     if (name == NULL) {
    1501          29 :         krb5_ccache ccache = NULL;
    1502             : 
    1503             :         /* If credential cache provides a client principal, use that. */
    1504          29 :         if (ccname)
    1505           0 :             ret = krb5_cc_resolve(context, ccname, &ccache);
    1506             :         else
    1507          29 :             ret = krb5_cc_default(context, &ccache);
    1508          29 :         if (ret  == 0)
    1509          29 :             ret = krb5_cc_get_principal(context, ccache, principal);
    1510          29 :         krb5_cc_close(context, ccache);
    1511          29 :         if (ret == 0)
    1512          29 :             return;
    1513             :     }
    1514             : 
    1515         112 :     user_realm = get_user_realm(context);
    1516             : 
    1517         112 :     if (name) {
    1518         112 :         if (enterprise_flag)
    1519          23 :             parseflags |= KRB5_PRINCIPAL_PARSE_ENTERPRISE;
    1520             : 
    1521         112 :         parse_name_realm(context, name, parseflags, user_realm, &tmp);
    1522             : 
    1523         112 :         if (user_realm && krb5_principal_get_num_comp(context, tmp) > 1) {
    1524             :             /* Principal is instance qualified, reparse with default realm. */
    1525           0 :             krb5_free_principal(context, tmp);
    1526           0 :             parse_name_realm(context, name, parseflags, NULL, principal);
    1527             :         } else {
    1528         112 :             *principal = tmp;
    1529             :         }
    1530             :     } else {
    1531           0 :         get_default_principal(context, principal);
    1532           0 :         if (user_realm)
    1533           0 :             set_princ_realm(context, *principal, user_realm);
    1534             :     }
    1535             : 
    1536         112 :     if (user_realm)
    1537           0 :         free(user_realm);
    1538             : }
    1539             : 
    1540             : static void
    1541           7 : get_princ_kt(krb5_context context,
    1542             :              krb5_principal *principal,
    1543             :              char *name)
    1544             : {
    1545             :     krb5_error_code ret;
    1546             :     krb5_principal tmp;
    1547             :     krb5_ccache ccache;
    1548             :     krb5_kt_cursor cursor;
    1549             :     krb5_keytab_entry entry;
    1550             :     char *def_realm;
    1551             : 
    1552           7 :     if (name == NULL) {
    1553             :         /*
    1554             :          * If the credential cache exists and specifies a client principal,
    1555             :          * use that.
    1556             :          */
    1557           0 :         if (krb5_cc_default(context, &ccache) == 0) {
    1558           0 :             ret = krb5_cc_get_principal(context, ccache, principal);
    1559           0 :             krb5_cc_close(context, ccache);
    1560           0 :             if (ret == 0)
    1561           4 :                 return;
    1562             :         }
    1563             :     }
    1564             : 
    1565           7 :     if (name) {
    1566             :         /* If the principal specifies an explicit realm, just use that. */
    1567           7 :         int parseflags = KRB5_PRINCIPAL_PARSE_NO_DEF_REALM;
    1568             : 
    1569           7 :         parse_name_realm(context, name, parseflags, NULL, &tmp);
    1570           7 :         if (krb5_principal_get_realm(context, tmp) != NULL) {
    1571           4 :             *principal = tmp;
    1572           4 :             return;
    1573             :         }
    1574             :     } else {
    1575             :         /* Otherwise, search keytab for bare name of the default principal. */
    1576           0 :         get_default_principal(context, &tmp);
    1577           0 :         set_princ_realm(context, tmp, NULL);
    1578             :     }
    1579             : 
    1580           3 :     def_realm = get_default_realm(context);
    1581             : 
    1582           3 :     ret = krb5_kt_start_seq_get(context, kt, &cursor);
    1583           3 :     if (ret)
    1584           0 :         krb5_err(context, 1, ret, "krb5_kt_start_seq_get");
    1585             : 
    1586          66 :     while (ret == 0 &&
    1587          33 :            krb5_kt_next_entry(context, kt, &entry, &cursor) == 0) {
    1588             :         const char *realm;
    1589             : 
    1590          30 :         if (!krb5_principal_compare_any_realm(context, tmp, entry.principal))
    1591          18 :             continue;
    1592          21 :         if (*principal &&
    1593           9 :             krb5_principal_compare(context, *principal, entry.principal))
    1594           9 :             continue;
    1595             :         /* The default realm takes precedence */
    1596           3 :         realm = krb5_principal_get_realm(context, entry.principal);
    1597           3 :         if (*principal && strcmp(def_realm, realm) == 0) {
    1598           0 :             krb5_free_principal(context, *principal);
    1599           0 :             ret = krb5_copy_principal(context, entry.principal, principal);
    1600           0 :             break;
    1601             :         }
    1602           3 :         if (!*principal)
    1603           3 :             ret = krb5_copy_principal(context, entry.principal, principal);
    1604             :     }
    1605           3 :     if (ret != 0 || (ret = krb5_kt_end_seq_get(context, kt, &cursor)) != 0)
    1606           0 :         krb5_err(context, 1, ret, "get_princ_kt");
    1607           3 :     if (!*principal) {
    1608           0 :         if (name)
    1609           0 :             parse_name_realm(context, name, 0, NULL, principal);
    1610             :         else
    1611           0 :             krb5_err(context, 1, KRB5_CC_NOTFOUND, "get_princ_kt");
    1612             :     }
    1613             : 
    1614           3 :     krb5_free_principal(context, tmp);
    1615           3 :     free(def_realm);
    1616             : }
    1617             : 
    1618             : static krb5_error_code
    1619           0 : get_switched_ccache(krb5_context context,
    1620             :                     const char * type,
    1621             :                     krb5_principal principal,
    1622             :                     krb5_ccache *ccache)
    1623             : {
    1624             :     krb5_error_code ret;
    1625             : 
    1626             : #ifdef _WIN32
    1627             :     if (strcmp(type, "API") == 0) {
    1628             :         /*
    1629             :          * Windows stores the default ccache name in the
    1630             :          * registry which is shared across multiple logon
    1631             :          * sessions for the same user.  The API credential
    1632             :          * cache provides a unique name space per logon
    1633             :          * session.  Therefore there is no need to generate
    1634             :          * a unique ccache name.  Instead use the principal
    1635             :          * name.  This provides a friendlier user experience.
    1636             :          */
    1637             :         char * unparsed_name;
    1638             :         char * cred_cache;
    1639             : 
    1640             :         ret = krb5_unparse_name(context, principal,
    1641             :                                 &unparsed_name);
    1642             :         if (ret)
    1643             :             krb5_err(context, 1, ret,
    1644             :                      N_("unparsing principal name", ""));
    1645             : 
    1646             :         ret = asprintf(&cred_cache, "API:%s", unparsed_name);
    1647             :         krb5_free_unparsed_name(context, unparsed_name);
    1648             :         if (ret == -1 || cred_cache == NULL)
    1649             :             krb5_err(context, 1, ret,
    1650             :                       N_("building credential cache name", ""));
    1651             : 
    1652             :         ret = krb5_cc_resolve(context, cred_cache, ccache);
    1653             :         free(cred_cache);
    1654             :     } else if (strcmp(type, "MSLSA") == 0) {
    1655             :         /*
    1656             :          * The Windows MSLSA cache when it is writeable
    1657             :          * stores tickets for multiple client principals
    1658             :          * in a single credential cache.
    1659             :          */
    1660             :         ret = krb5_cc_resolve(context, "MSLSA:", ccache);
    1661             :     } else {
    1662             :         ret = krb5_cc_new_unique(context, type, NULL, ccache);
    1663             :     }
    1664             : #else /* !_WIN32 */
    1665           0 :     ret = krb5_cc_new_unique(context, type, NULL, ccache);
    1666             : #endif /* _WIN32 */
    1667             : 
    1668           0 :     return ret;
    1669             : }
    1670             : 
    1671             : int
    1672         148 : main(int argc, char **argv)
    1673             : {
    1674             :     krb5_error_code ret;
    1675             :     krb5_context context;
    1676             :     krb5_ccache  ccache;
    1677         148 :     krb5_principal principal = NULL;
    1678         148 :     int optidx = 0;
    1679         148 :     krb5_deltat ticket_life = 0;
    1680             : #ifdef HAVE_SIGACTION
    1681             :     struct sigaction sa;
    1682             : #endif
    1683         148 :     krb5_boolean unique_ccache = FALSE;
    1684         148 :     krb5_boolean historical_anon_pkinit = FALSE;
    1685         148 :     int anonymous_pkinit = FALSE;
    1686             : 
    1687         148 :     setprogname(argv[0]);
    1688             : 
    1689         148 :     setlocale(LC_ALL, "");
    1690         148 :     bindtextdomain("heimdal_kuser", HEIMDAL_LOCALEDIR);
    1691         148 :     textdomain("heimdal_kuser");
    1692             : 
    1693         148 :     ret = krb5_init_context(&context);
    1694         148 :     if (ret == KRB5_CONFIG_BADFORMAT)
    1695           0 :         krb5_err(context, 1, ret, "Failed to parse configuration file");
    1696         148 :     else if (ret == EISDIR)
    1697             :         /* We use EISDIR to mean "not a regular file" */
    1698           0 :         krb5_errx(context, 1,
    1699             :                   "Failed to read configuration file: not a regular file");
    1700         148 :     else if (ret)
    1701           0 :         krb5_err(context, 1, ret, "Failed to read configuration file");
    1702             : 
    1703         148 :     if (getarg(args, sizeof(args) / sizeof(args[0]), argc, argv, &optidx))
    1704           0 :         usage(1);
    1705             : 
    1706         148 :     if (help_flag)
    1707           0 :         usage(0);
    1708             : 
    1709         148 :     if (version_flag) {
    1710           0 :         print_version(NULL);
    1711           0 :         exit(0);
    1712             :     }
    1713             : 
    1714         148 :     argc -= optidx;
    1715         148 :     argv += optidx;
    1716             : 
    1717         148 :     krb5_appdefault_boolean(context, "kinit", NULL, "historical_anon_pkinit",
    1718             :                             FALSE, &historical_anon_pkinit);
    1719             : 
    1720             :     /*
    1721             :      * Open the keytab now, we use the keytab to determine the principal's
    1722             :      * realm when the requested principal has no realm.
    1723             :      */
    1724         148 :     if (use_keytab || keytab_str) {
    1725           7 :         if (keytab_str)
    1726           7 :             ret = krb5_kt_resolve(context, keytab_str, &kt);
    1727             :         else
    1728           0 :             ret = krb5_kt_default(context, &kt);
    1729           7 :         if (ret)
    1730           0 :             krb5_err(context, 1, ret, "resolving keytab");
    1731             :     }
    1732             : 
    1733         148 :     if (pk_enterprise_flag) {
    1734           0 :         ret = krb5_pk_enterprise_cert(context, pk_user_id,
    1735             :                                       argv[0], &principal,
    1736             :                                       &ent_user_id);
    1737           0 :         if (ret)
    1738           0 :             krb5_err(context, 1, ret, "krb5_pk_enterprise_certs");
    1739             : 
    1740           0 :         pk_user_id = NULL;
    1741           0 :         if (pk_anon_fast_armor > 0)
    1742           0 :             krb5_warnx(context, N_("Ignoring --pk-anon-fast-armor "
    1743             :                                    "because --pk-user given", ""));
    1744           0 :         pk_anon_fast_armor = 0;
    1745         148 :     } else if (argc && argv[0][0] == '@' &&
    1746           0 :         (gss_preauth_mech || anonymous_flag)) {
    1747           0 :         const char *instance = NULL;
    1748             : 
    1749           0 :         if (gss_preauth_mech) {
    1750           0 :             instance = KRB5_FEDERATED_NAME;
    1751           0 :         } else if (anonymous_flag) {
    1752           0 :             instance = KRB5_ANON_NAME;
    1753           0 :             anonymous_pkinit = TRUE;
    1754             :         }
    1755             : 
    1756           0 :         ret = make_wellknown_name(context, &argv[0][1], instance, &principal);
    1757           0 :         if (ret)
    1758           0 :             krb5_err(context, 1, ret, "make_wellknown_name");
    1759           0 :         if (!gss_preauth_mech && pk_anon_fast_armor > 1) {
    1760           0 :             krb5_warnx(context, N_("Ignoring --pk-anon-fast-armor "
    1761             :                                    "because --anonymous given", ""));
    1762           0 :             pk_anon_fast_armor = 0;
    1763             :         }
    1764         148 :     } else if (anonymous_flag && historical_anon_pkinit) {
    1765           0 :         char *realm = argc == 0 ? get_default_realm(context) :
    1766           0 :                       argv[0][0] == '@' ? &argv[0][1] : argv[0];
    1767             : 
    1768           0 :         ret = krb5_make_principal(context, &principal, realm,
    1769             :                                   KRB5_WELLKNOWN_NAME, KRB5_ANON_NAME, NULL);
    1770           0 :         if (ret)
    1771           0 :             krb5_err(context, 1, ret, "krb5_make_principal");
    1772           0 :         krb5_principal_set_type(context, principal, KRB5_NT_WELLKNOWN);
    1773           0 :         anonymous_pkinit = TRUE;
    1774         148 :     } else if (use_keytab || keytab_str) {
    1775           7 :         get_princ_kt(context, &principal, argv[0]);
    1776         141 :     } else if (gss_preauth_mech && argc == 0 && gss_preauth_name == NULL) {
    1777             :         /*
    1778             :          * Use the federated name as a placeholder if we have neither a Kerberos
    1779             :          * nor a GSS-API client name, and we are performing GSS-API preauth.
    1780             :          */
    1781           0 :         ret = make_wellknown_name(context, get_default_realm(context),
    1782             :                                   KRB5_FEDERATED_NAME, &principal);
    1783           0 :         if (ret)
    1784           0 :             krb5_err(context, 1, ret, "make_wellknown_name");
    1785             :     } else {
    1786         141 :         get_princ(context, &principal, cred_cache, argv[0]);
    1787             :     }
    1788             : 
    1789         148 :     if (fcache_version)
    1790           0 :         krb5_set_fcache_version(context, fcache_version);
    1791             : 
    1792         148 :     if (renewable_flag == -1)
    1793             :         /* this seems somewhat pointless, but whatever */
    1794         116 :         krb5_appdefault_boolean(context, "kinit",
    1795             :                                 krb5_principal_get_realm(context, principal),
    1796             :                                 "renewable", FALSE, &renewable_flag);
    1797         148 :     if (do_afslog == -1)
    1798         148 :         krb5_appdefault_boolean(context, "kinit",
    1799             :                                 krb5_principal_get_realm(context, principal),
    1800             :                                 "afslog", TRUE, &do_afslog);
    1801             : 
    1802             :     /*
    1803             :      * Cases:
    1804             :      *
    1805             :      *  - use the given ccache
    1806             :      *  - use a new unique ccache for running a command with (in this case we
    1807             :      *    get to set KRB5CCNAME, so a new unique ccache makes sense)
    1808             :      *  - use the default ccache for the given principal as requested and do
    1809             :      *    not later switch the collection's default/primary to it
    1810             :      *  - use the default cache, possibly a new unique one that later gets
    1811             :      *    switched to it
    1812             :      *
    1813             :      * The important thing is that, except for the case where we're running a
    1814             :      * command, we _can't set KRB5CCNAME_, and we can't expect the user to read
    1815             :      * our output and figure out to set it (we could have an output-for-shell-
    1816             :      * eval mode, like ssh-agent and such, but we don't).  Therefore, in all
    1817             :      * cases where we can't set KRB5CCNAME we must do something that makes
    1818             :      * sense to the user, and that is to either initialize a given ccache, use
    1819             :      * the default, or use a subsidiary ccache named after the principal whose
    1820             :      * creds we're initializing.
    1821             :      */
    1822         148 :     if (cred_cache) {
    1823             :         /* Use the given ccache */
    1824          96 :         ret = krb5_cc_resolve(context, cred_cache, &ccache);
    1825          52 :     } else if (argc > 1) {
    1826             :         char s[1024];
    1827             : 
    1828             :         /*
    1829             :          * A command was given, so use a new unique ccache (and destroy it
    1830             :          * later).
    1831             :          */
    1832           0 :         ret = krb5_cc_new_unique(context, NULL, NULL, &ccache);
    1833           0 :         if (ret)
    1834           0 :             krb5_err(context, 1, ret, "creating cred cache");
    1835           0 :         snprintf(s, sizeof(s), "%s:%s",
    1836             :                  krb5_cc_get_type(context, ccache),
    1837             :                  krb5_cc_get_name(context, ccache));
    1838           0 :         setenv("KRB5CCNAME", s, 1);
    1839           0 :         unique_ccache = TRUE;
    1840           0 :         switch_cache_flags = 0;
    1841          52 :     } else if (default_for) {
    1842           0 :         ret = krb5_cc_default_for(context, principal, &ccache);
    1843           0 :         if (switch_cache_flags == -1)
    1844           0 :             switch_cache_flags = 0;
    1845             :     } else {
    1846          52 :         ret = krb5_cc_cache_match(context, principal, &ccache);
    1847          52 :         if (ret) {
    1848             :             const char *type;
    1849          52 :             ret = krb5_cc_default(context, &ccache);
    1850          52 :             if (ret)
    1851           0 :                 krb5_err(context, 1, ret,
    1852           0 :                          N_("resolving credentials cache", ""));
    1853             : 
    1854             :             /*
    1855             :              * Check if the type support switching, and we do,
    1856             :              * then do that instead over overwriting the current
    1857             :              * default credential
    1858             :              */
    1859          52 :             type = krb5_cc_get_type(context, ccache);
    1860          52 :             if (krb5_cc_support_switch(context, type) &&
    1861          52 :                 strcmp(type, "FILE")) {
    1862           0 :                 krb5_cc_close(context, ccache);
    1863           0 :                 ret = get_switched_ccache(context, type, principal,
    1864             :                                           &ccache);
    1865           0 :                 if (ret == 0)
    1866           0 :                     unique_ccache = TRUE;
    1867             :             }
    1868             :         }
    1869             :     }
    1870         148 :     if (ret)
    1871           0 :         krb5_err(context, 1, ret, N_("resolving credentials cache", ""));
    1872             : 
    1873             : #ifndef NO_AFS
    1874         148 :     if (argc > 1 && k_hasafs())
    1875           0 :         k_setpag();
    1876             : #endif
    1877             : 
    1878         148 :     if (lifetime) {
    1879           3 :         int tmp = parse_time(lifetime, "s");
    1880           3 :         if (tmp < 0)
    1881           0 :             errx(1, N_("unparsable time: %s", ""), lifetime);
    1882             : 
    1883           3 :         ticket_life = tmp;
    1884             :     }
    1885             : 
    1886         148 :     if (addrs_flag == 0 && extra_addresses.num_strings > 0)
    1887           0 :         krb5_errx(context, 1,
    1888           0 :                   N_("specifying both extra addresses and "
    1889             :                      "no addresses makes no sense", ""));
    1890             :     {
    1891             :         int i;
    1892             :         krb5_addresses addresses;
    1893         148 :         memset(&addresses, 0, sizeof(addresses));
    1894         148 :         for(i = 0; i < extra_addresses.num_strings; i++) {
    1895           0 :             ret = krb5_parse_address(context, extra_addresses.strings[i],
    1896             :                                      &addresses);
    1897           0 :             if (ret == 0) {
    1898           0 :                 krb5_add_extra_addresses(context, &addresses);
    1899           0 :                 krb5_free_addresses(context, &addresses);
    1900             :             }
    1901             :         }
    1902         148 :         free_getarg_strings(&extra_addresses);
    1903             :     }
    1904             : 
    1905         148 :     if (renew_flag || validate_flag) {
    1906          24 :         ret = renew_validate(context, renew_flag, validate_flag,
    1907             :                              &ccache, principal,
    1908             :                              default_for ? TRUE : FALSE, server_str,
    1909             :                              ticket_life);
    1910             : 
    1911             : #ifndef NO_AFS
    1912          24 :         if (ret == 0 && server_str == NULL && do_afslog && k_hasafs())
    1913           0 :             krb5_afslog(context, ccache, NULL, NULL);
    1914             : #endif
    1915             : 
    1916          24 :         if (unique_ccache)
    1917           0 :             krb5_cc_destroy(context, ccache);
    1918          24 :         exit(ret != 0);
    1919             :     }
    1920             : 
    1921         124 :     ret = get_new_tickets(context, principal, ccache, ticket_life,
    1922             :                           1, anonymous_pkinit);
    1923         124 :     if (ret) {
    1924          11 :         if (unique_ccache)
    1925           0 :             krb5_cc_destroy(context, ccache);
    1926          11 :         exit(1);
    1927             :     }
    1928             : 
    1929             : #ifndef NO_AFS
    1930         113 :     if (ret == 0 && server_str == NULL && do_afslog && k_hasafs())
    1931           0 :         krb5_afslog(context, ccache, NULL, NULL);
    1932             : #endif
    1933             : 
    1934         113 :     if (argc > 1) {
    1935             :         struct renew_ctx ctx;
    1936             :         time_t timeout;
    1937             : 
    1938           0 :         timeout = ticket_lifetime(context, ccache, principal,
    1939             :                                   server_str, NULL) / 2;
    1940             : 
    1941           0 :         ctx.context = context;
    1942           0 :         ctx.ccache = ccache;
    1943           0 :         ctx.principal = principal;
    1944           0 :         ctx.ticket_life = ticket_life;
    1945           0 :         ctx.timeout = timeout;
    1946           0 :         ctx.anonymous_pkinit = anonymous_pkinit;
    1947             : 
    1948             : #ifdef HAVE_SIGACTION
    1949           0 :         memset(&sa, 0, sizeof(sa));
    1950           0 :         sigemptyset(&sa.sa_mask);
    1951           0 :         sa.sa_handler = handler;
    1952             : 
    1953           0 :         sigaction(SIGINFO, &sa, NULL);
    1954           0 :         sigaction(SIGINT, &sa, NULL);
    1955           0 :         sigaction(SIGQUIT, &sa, NULL);
    1956             : #endif
    1957             : 
    1958           0 :         ret = simple_execvp_timed(argv[1], argv+1,
    1959             :                                   renew_func, &ctx, timeout);
    1960             : #define EX_NOEXEC       126
    1961             : #define EX_NOTFOUND     127
    1962           0 :         if (ret == EX_NOEXEC)
    1963           0 :             krb5_warnx(context, N_("permission denied: %s", ""), argv[1]);
    1964           0 :         else if (ret == EX_NOTFOUND)
    1965           0 :             krb5_warnx(context, N_("command not found: %s", ""), argv[1]);
    1966             : 
    1967           0 :         krb5_cc_destroy(context, ccache);
    1968             : #ifndef NO_AFS
    1969           0 :         if (k_hasafs())
    1970           0 :             k_unlog();
    1971             : #endif
    1972             :     } else {
    1973         113 :         krb5_cc_close(context, ccache);
    1974         113 :         ret = 0;
    1975             :     }
    1976         113 :     krb5_free_principal(context, principal);
    1977         113 :     if (kt)
    1978           7 :         krb5_kt_close(context, kt);
    1979         113 :     krb5_free_context(context);
    1980         113 :     return ret;
    1981             : }

Generated by: LCOV version 1.14