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

Generated by: LCOV version 1.14