1 /*
   2  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
   3  * Use is subject to license terms.
   4  */
   5 
   6 #include "k5-int.h"
   7 #include "int-proto.h"
   8 
   9 /* Solaris Kerberos */
  10 extern krb5_error_code krb5_libdefault_boolean();
  11 
  12 static krb5_error_code
  13 krb5_cc_copy_creds_except(krb5_context context, krb5_ccache incc, krb5_ccache outcc, krb5_principal princ)
  14 {
  15    krb5_error_code code;
  16    krb5_flags flags;
  17    krb5_cc_cursor cur;
  18    krb5_creds creds;
  19 
  20    flags = 0;                           /* turns off OPENCLOSE mode */
  21    /* Solaris Kerberos */
  22    if ((code = krb5_cc_set_flags(context, incc, flags)) != NULL)
  23       return(code);
  24    /* Solaris Kerberos */
  25    if ((code = krb5_cc_set_flags(context, outcc, flags)) != NULL)
  26       return(code);
  27 
  28    /* Solaris Kerberos */
  29    if ((code = krb5_cc_start_seq_get(context, incc, &cur)) != NULL)
  30       goto cleanup;
  31 
  32    /* Solaris Kerberos */
  33    while ((code = krb5_cc_next_cred(context, incc, &cur, &creds)) == NULL) {
  34       if (krb5_principal_compare(context, princ, creds.server))
  35          continue;
  36 
  37       code = krb5_cc_store_cred(context, outcc, &creds);
  38       krb5_free_cred_contents(context, &creds);
  39       if (code)
  40          goto cleanup;
  41    }
  42 
  43    if (code != KRB5_CC_END)
  44       goto cleanup;
  45 
  46    code = 0;
  47 
  48 cleanup:
  49    flags = KRB5_TC_OPENCLOSE;
  50 
  51    /* Solaris Kerberos */
  52    if (code)
  53       (void) krb5_cc_set_flags(context, incc, flags);
  54    else
  55       code = krb5_cc_set_flags(context, incc, flags);
  56 
  57    /* Solaris Kerberos */
  58    if (code)
  59       (void) krb5_cc_set_flags(context, outcc, flags);
  60    else
  61       code = krb5_cc_set_flags(context, outcc, flags);
  62 
  63    return(code);
  64 }
  65 
  66 krb5_error_code KRB5_CALLCONV
  67 krb5_verify_init_creds(krb5_context context,
  68                        krb5_creds *creds,
  69                        krb5_principal server_arg,
  70                        krb5_keytab keytab_arg,
  71                        krb5_ccache *ccache_arg,
  72                        krb5_verify_init_creds_opt *options)
  73 {
  74    krb5_error_code ret;
  75    krb5_principal server;
  76    krb5_keytab keytab;
  77    krb5_ccache ccache;
  78    krb5_keytab_entry kte;
  79    krb5_creds in_creds, *out_creds;
  80    krb5_auth_context authcon;
  81    krb5_data ap_req;
  82    
  83    /* KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN */
  84 
  85    server = NULL;
  86    keytab = NULL;
  87    ccache = NULL;
  88    out_creds = NULL;
  89    authcon = NULL;
  90    ap_req.data = NULL;
  91 
  92    /* Solaris Kerberos */
  93    if (server_arg)
  94       server = server_arg;
  95    else if (ret = krb5_sname_to_principal(context, NULL, NULL, 
  96                                         KRB5_NT_SRV_HST, &server))
  97       goto cleanup;
  98       
  99    /* first, check if the server is in the keytab.  If not, there's
 100       no reason to continue.  rd_req does all this, but there's
 101       no way to know that a given error is caused by a missing
 102       keytab or key, and not by some other problem. */
 103 
 104    if (keytab_arg) {
 105       keytab = keytab_arg;
 106    } else {
 107        /* Solaris Kerberos: ignore errors here, deal with below */
 108       ret = krb5_kt_default(context, &keytab);
 109    }
 110 
 111    /*
 112     * Solaris Kerberos:
 113     * Warning: be very, very careful when modifying the logic here
 114     */
 115    if (keytab == NULL ||
 116        (ret = krb5_kt_get_entry(context, keytab, server, 0, 0, &kte))) {
 117        /* this means there is no keying material.  This is ok, as long as
 118           it is not prohibited by the configuration */
 119        /* Solaris Kerberos */
 120        int nofail = 1;  /* Solaris Kerberos: default return error if keytab problems */
 121 
 122        if (options &&
 123            (options->flags & KRB5_VERIFY_INIT_CREDS_OPT_AP_REQ_NOFAIL)) {
 124            /* first, if options are set then use the option value to set nofail */
 125             nofail = options->ap_req_nofail;
 126        } else {
 127            /* 
 128             * Solaris Kerberos:
 129             * Check verify_ap_req_nofail if set in config file.  Note this logic
 130             * assumes that krb5_libdefault_boolean will not set nofail to a
 131             * default value if verify_ap_req_nofail is not explictly set in
 132             * config file.  Don't care about the return code.
 133             */
 134            (void) krb5_libdefault_boolean(context, &creds->client->realm,
 135                                           "verify_ap_req_nofail",
 136                                           &nofail);
 137        }
 138        /* Solaris Kerberos: exit without an error ONLY if nofail is false */
 139        if (!nofail)
 140            ret = 0; 
 141 
 142        goto cleanup;
 143    }
 144 
 145    krb5_kt_free_entry(context, &kte);
 146 
 147    /* If the creds are for the server principal, we're set, just do
 148       a mk_req.  Otherwise, do a get_credentials first. */
 149 
 150    if (krb5_principal_compare(context, server, creds->server)) {
 151       /* make an ap_req */
 152       if ((ret = krb5_mk_req_extended(context, &authcon, 0, NULL, creds,
 153                                       &ap_req)))
 154          goto cleanup;
 155    } else {
 156       /* this is unclean, but it's the easiest way without ripping the
 157          library into very small pieces.  store the client's initial cred
 158          in a memory ccache, then call the library.  Later, we'll copy
 159          everything except the initial cred into the ccache we return to
 160          the user.  A clean implementation would involve library
 161          internals with a coherent idea of "in" and "out". */
 162 
 163       /* insert the initial cred into the ccache */
 164 
 165       if ((ret = krb5_cc_resolve(context, "MEMORY:rd_req", &ccache)))
 166          goto cleanup;
 167       /* Solaris Kerberos */
 168       if ((ret = krb5_cc_initialize(context, ccache, creds->client)) != NULL)
 169          goto cleanup;
 170 
 171       /* Solaris Kerberos */
 172       if ((ret = krb5_cc_store_cred(context, ccache, creds)) != NULL)
 173          goto cleanup;
 174 
 175       /* set up for get_creds */
 176       memset(&in_creds, 0, sizeof(in_creds));
 177       in_creds.client = creds->client;
 178       in_creds.server = server;
 179       if ((ret = krb5_timeofday(context, &in_creds.times.endtime)))
 180          goto cleanup;
 181       in_creds.times.endtime += 5*60;
 182 
 183       if ((ret = krb5_get_credentials(context, 0, ccache, &in_creds,
 184                                       &out_creds)))
 185          goto cleanup;
 186 
 187       /* make an ap_req */
 188       if ((ret = krb5_mk_req_extended(context, &authcon, 0, NULL, out_creds,
 189                                       &ap_req)))
 190          goto cleanup;
 191    }
 192 
 193    /* wipe the auth context for mk_req */
 194    if (authcon) {
 195       krb5_auth_con_free(context, authcon);
 196       authcon = NULL;
 197    }
 198 
 199    /* verify the ap_req */
 200 
 201    if ((ret = krb5_rd_req(context, &authcon, &ap_req, server, keytab,
 202                           NULL, NULL)))
 203       goto cleanup;
 204 
 205    /* if we get this far, then the verification succeeded.  We can
 206       still fail if the library stuff here fails, but that's it */
 207 
 208    if (ccache_arg && ccache) {
 209        if (*ccache_arg == NULL) {
 210            krb5_ccache retcc;
 211 
 212            retcc = NULL;
 213 
 214            /* Solaris Kerberos */
 215            if (((ret = krb5_cc_resolve(context, "MEMORY:rd_req2", &retcc)) != NULL) ||
 216                ((ret = krb5_cc_initialize(context, retcc, creds->client)) != NULL) ||
 217                ((ret = krb5_cc_copy_creds_except(context, ccache, retcc,
 218                                                 creds->server)) != NULL)) {
 219                /* Solaris Kerberos */
 220                if (retcc)
 221                    (void) krb5_cc_destroy(context, retcc);
 222            } else {
 223                *ccache_arg = retcc;
 224            }
 225        } else {
 226            ret = krb5_cc_copy_creds_except(context, ccache, *ccache_arg,
 227                                            server);
 228        }
 229    }
 230 
 231    /* if any of the above paths returned an errors, then ret is set
 232       accordingly.  either that, or it's zero, which is fine, too */
 233 
 234 cleanup:
 235    if (!server_arg && server)
 236       krb5_free_principal(context, server);
 237     /* Solaris Kerberos */
 238    if (!keytab_arg && keytab)
 239       (void) krb5_kt_close(context, keytab);
 240     /* Solaris Kerberos */
 241    if (ccache)
 242       (void) krb5_cc_destroy(context, ccache);
 243    if (out_creds)
 244       krb5_free_creds(context, out_creds);
 245    if (authcon)
 246       krb5_auth_con_free(context, authcon);
 247    if (ap_req.data)
 248       krb5_xfree(ap_req.data);
 249 
 250    return(ret);
 251 }