/* Get a TGT for use at the remote host */ krb5_error_code KRB5_CALLCONV krb5_fwd_tgt_creds(krb5_context context, krb5_auth_context auth_context, char *rhost, krb5_principal client, krb5_principal server, krb5_ccache cc, int forwardable, krb5_data *outbuf) /* Should forwarded TGT also be forwardable? */ { krb5_replay_data replaydata; krb5_data * scratch = 0; krb5_address **addrs = NULL; krb5_error_code retval; krb5_creds creds, tgt; krb5_creds *pcreds; krb5_flags kdcoptions; int close_cc = 0; int free_rhost = 0; krb5_enctype enctype = 0; krb5_keyblock *session_key; krb5_boolean old_use_conf_ktypes = context->use_conf_ktypes; memset((char *)&creds, 0, sizeof(creds)); memset((char *)&tgt, 0, sizeof(creds)); if (cc == 0) { if ((retval = krb5int_cc_default(context, &cc))) goto errout; close_cc = 1; } retval = krb5_auth_con_getkey (context, auth_context, &session_key); if (retval) goto errout; if (session_key) { enctype = session_key->enctype; krb5_free_keyblock (context, session_key); session_key = NULL; } else if (server) { /* must server be non-NULL when rhost is given? */ /* Try getting credentials to see what the remote side supports. Not bulletproof, just a heuristic. */ krb5_creds in, *out = 0; memset (&in, 0, sizeof(in)); retval = krb5_copy_principal (context, server, &in.server); if (retval) goto punt; retval = krb5_copy_principal (context, client, &in.client); if (retval) goto punt; retval = krb5_get_credentials (context, 0, cc, &in, &out); if (retval) goto punt; /* Got the credentials. Okay, now record the enctype and throw them away. */ enctype = out->keyblock.enctype; krb5_free_creds (context, out); punt: krb5_free_cred_contents (context, &in); } if ((retval = krb5_copy_principal(context, client, &creds.client))) goto errout; if ((retval = krb5_build_principal_ext(context, &creds.server, client->realm.length, client->realm.data, KRB5_TGS_NAME_SIZE, KRB5_TGS_NAME, client->realm.length, client->realm.data, 0))) goto errout; /* fetch tgt directly from cache */ context->use_conf_ktypes = 1; retval = krb5_cc_retrieve_cred (context, cc, KRB5_TC_SUPPORTED_KTYPES, &creds, &tgt); context->use_conf_ktypes = old_use_conf_ktypes; if (retval) goto errout; /* tgt->client must be equal to creds.client */ if (!krb5_principal_compare(context, tgt.client, creds.client)) { /* Solaris Kerberos */ char *r_name = NULL; char *t_name = NULL; krb5_error_code r_err, t_err; t_err = krb5_unparse_name(context, tgt.client, &t_name); r_err = krb5_unparse_name(context, creds.client, &r_name); krb5_set_error_message(context, KRB5_PRINC_NOMATCH, dgettext(TEXT_DOMAIN, "Requested principal and ticket don't match: Requested principal is '%s' and TGT principal is '%s'"), r_err ? "unknown" : r_name, t_err ? "unknown" : t_name); if (r_name) krb5_free_unparsed_name(context, r_name); if (t_name) krb5_free_unparsed_name(context, t_name); retval = KRB5_PRINC_NOMATCH; goto errout; } if (!tgt.ticket.length) { retval = KRB5_NO_TKT_SUPPLIED; goto errout; } if (tgt.addresses && *tgt.addresses) { if (rhost == NULL) { if (krb5_princ_type(context, server) != KRB5_NT_SRV_HST) { retval = KRB5_FWD_BAD_PRINCIPAL; goto errout; } if (krb5_princ_size(context, server) < 2){ retval = KRB5_CC_BADNAME; goto errout; } rhost = malloc(server->data[1].length+1); if (!rhost) { retval = ENOMEM; goto errout; } free_rhost = 1; /* Solaris Kerberos */ (void) memcpy(rhost, server->data[1].data, server->data[1].length); rhost[server->data[1].length] = '\0'; } retval = krb5_os_hostaddr(context, rhost, &addrs); if (retval) goto errout; } creds.keyblock.enctype = enctype; creds.times = tgt.times; creds.times.starttime = 0; kdcoptions = flags2options(tgt.ticket_flags)|KDC_OPT_FORWARDED; if (!forwardable) /* Reset KDC_OPT_FORWARDABLE */ kdcoptions &= ~(KDC_OPT_FORWARDABLE); if ((retval = krb5_get_cred_via_tkt(context, &tgt, kdcoptions, addrs, &creds, &pcreds))) { if (enctype) { creds.keyblock.enctype = 0; if ((retval = krb5_get_cred_via_tkt(context, &tgt, kdcoptions, addrs, &creds, &pcreds))) goto errout; } else goto errout; } retval = krb5_mk_1cred(context, auth_context, pcreds, &scratch, &replaydata); krb5_free_creds(context, pcreds); /* * Solaris Kerberos: changed this logic from the MIT 1.2.1 version to be * more robust. */ if (scratch) { if (retval) krb5_free_data(context, scratch); else { *outbuf = *scratch; krb5_xfree(scratch); } } errout: if (addrs) krb5_free_addresses(context, addrs); /* Solaris Kerberos */ if (close_cc) (void) krb5_cc_close(context, cc); if (free_rhost) free(rhost); krb5_free_cred_contents(context, &creds); krb5_free_cred_contents(context, &tgt); return retval; }
static OM_uint32 copy_initiator_creds(OM_uint32 *minor_status, gss_cred_id_t input_cred_handle, const gss_OID desired_mech, OM_uint32 overwrite_cred, OM_uint32 default_cred) { OM_uint32 major_status; krb5_error_code code; krb5_gss_cred_id_t kcred = NULL; krb5_context context = NULL; krb5_ccache ccache = NULL; if (!default_cred) { *minor_status = G_STORE_NON_DEFAULT_CRED_NOSUPP; major_status = GSS_S_FAILURE; goto cleanup; } code = krb5_gss_init_context(&context); if (code != 0) { *minor_status = code; major_status = GSS_S_FAILURE; goto cleanup; } major_status = krb5_gss_validate_cred_1(minor_status, input_cred_handle, context); if (GSS_ERROR(major_status)) goto cleanup; kcred = (krb5_gss_cred_id_t)input_cred_handle; if (kcred->ccache == NULL || kcred->proxy_cred) { *minor_status = KG_CCACHE_NOMATCH; major_status = GSS_S_DEFECTIVE_CREDENTIAL; goto cleanup; } if (!overwrite_cred && has_unexpired_creds(kcred, desired_mech, default_cred)) { major_status = GSS_S_DUPLICATE_ELEMENT; goto cleanup; } code = krb5int_cc_default(context, &ccache); if (code != 0) { *minor_status = code; major_status = GSS_S_FAILURE; goto cleanup; } code = krb5_cc_copy_creds(context, kcred->ccache, ccache); if (code != 0) { *minor_status = code; major_status = GSS_S_FAILURE; goto cleanup; } *minor_status = 0; major_status = GSS_S_COMPLETE; cleanup: if (kcred != NULL) k5_mutex_unlock(&kcred->lock); if (ccache != NULL) krb5_cc_close(context, ccache); krb5_free_context(context); return major_status; }
krb5_error_code KRB5_CALLCONV krb5_sendauth(krb5_context context, krb5_auth_context *auth_context, krb5_pointer fd, char *appl_version, krb5_principal client, krb5_principal server, krb5_flags ap_req_options, krb5_data *in_data, krb5_creds *in_creds, krb5_ccache ccache, krb5_error **error, krb5_ap_rep_enc_part **rep_result, krb5_creds **out_creds) { krb5_octet result; krb5_creds creds; krb5_creds * credsp = NULL; krb5_creds * credspout = NULL; krb5_error_code retval = 0; krb5_data inbuf, outbuf; int len; krb5_ccache use_ccache = 0; if (error) *error = 0; /* * First, send over the length of the sendauth version string; * then, we send over the sendauth version. Next, we send * over the length of the application version strings followed * by the string itself. */ outbuf.length = strlen(sendauth_version) + 1; outbuf.data = (char *) sendauth_version; if ((retval = krb5_write_message(context, fd, &outbuf))) return(retval); outbuf.length = strlen(appl_version) + 1; outbuf.data = appl_version; if ((retval = krb5_write_message(context, fd, &outbuf))) return(retval); /* * Now, read back a byte: 0 means no error, 1 means bad sendauth * version, 2 means bad application version */ len = krb5_net_read(context, *((int *) fd), (char *)&result, 1); if (len != 1) return((len < 0) ? errno : ECONNABORTED); if (result == 1) return(KRB5_SENDAUTH_BADAUTHVERS); else if (result == 2) return(KRB5_SENDAUTH_BADAPPLVERS); else if (result != 0) return(KRB5_SENDAUTH_BADRESPONSE); /* * We're finished with the initial negotiations; let's get and * send over the authentication header. (The AP_REQ message) */ /* * If no credentials were provided, try getting it from the * credentials cache. */ memset((char *)&creds, 0, sizeof(creds)); /* * See if we need to access the credentials cache */ if (!in_creds || !in_creds->ticket.length) { if (ccache) use_ccache = ccache; /* Solaris Kerberos */ else if ((retval = krb5int_cc_default(context, &use_ccache)) != 0) goto error_return; } if (!in_creds) { if ((retval = krb5_copy_principal(context, server, &creds.server))) goto error_return; if (client) retval = krb5_copy_principal(context, client, &creds.client); else retval = krb5_cc_get_principal(context, use_ccache, &creds.client); if (retval) { krb5_free_principal(context, creds.server); goto error_return; } /* creds.times.endtime = 0; -- memset 0 takes care of this zero means "as long as possible" */ /* creds.keyblock.enctype = 0; -- as well as this. zero means no session enctype preference */ in_creds = &creds; } if (!in_creds->ticket.length) { /* Solaris Kerberos */ if ((retval = krb5_get_credentials(context, 0, use_ccache, in_creds, &credsp)) != 0) goto error_return; credspout = credsp; } else { credsp = in_creds; } if (ap_req_options & AP_OPTS_USE_SUBKEY) { /* Provide some more fodder for random number code. This isn't strong cryptographically; the point here is not to guarantee randomness, but to make it less likely that multiple sessions could pick the same subkey. */ char rnd_data[1024]; GETPEERNAME_ARG3_TYPE len2; krb5_data d; d.length = sizeof (rnd_data); d.data = rnd_data; len2 = sizeof (rnd_data); if (getpeername (*(int*)fd, (GETPEERNAME_ARG2_TYPE *) rnd_data, &len2) == 0) { d.length = len2; /* Solaris Kerberos */ (void) krb5_c_random_seed (context, &d); } len2 = sizeof (rnd_data); if (getsockname (*(int*)fd, (GETSOCKNAME_ARG2_TYPE *) rnd_data, &len2) == 0) { d.length = len2; /* Solaris Kerberos */ (void) krb5_c_random_seed (context, &d); } } /* Solaris Kerberos */ if ((retval = krb5_mk_req_extended(context, auth_context, ap_req_options, in_data, credsp, &outbuf)) != 0) goto error_return; /* * First write the length of the AP_REQ message, then write * the message itself. */ retval = krb5_write_message(context, fd, &outbuf); free(outbuf.data); if (retval) goto error_return; /* * Now, read back a message. If it was a null message (the * length was zero) then there was no error. If not, we the * authentication was rejected, and we need to return the * error structure. */ /* Solaris Kerberos */ if ((retval = krb5_read_message(context, fd, &inbuf)) != 0) goto error_return; if (inbuf.length) { if (error) { /* Solaris Kerberos */ if ((retval = krb5_rd_error(context, &inbuf, error)) != 0) { krb5_xfree(inbuf.data); goto error_return; } } retval = KRB5_SENDAUTH_REJECTED; krb5_xfree(inbuf.data); goto error_return; } /* * If we asked for mutual authentication, we should now get a * length field, followed by a AP_REP message */ if ((ap_req_options & AP_OPTS_MUTUAL_REQUIRED)) { krb5_ap_rep_enc_part *repl = 0; /* Solaris Kerberos */ if ((retval = krb5_read_message(context, fd, &inbuf)) != 0) goto error_return; /* Solaris Kerberos */ if ((retval = krb5_rd_rep(context, *auth_context, &inbuf, &repl)) != 0) { if (repl) krb5_free_ap_rep_enc_part(context, repl); krb5_xfree(inbuf.data); goto error_return; } krb5_xfree(inbuf.data); /* * If the user wants to look at the AP_REP message, * copy it for them. */ if (rep_result) *rep_result = repl; else krb5_free_ap_rep_enc_part(context, repl); } retval = 0; /* Normal return */ if (out_creds) { *out_creds = credsp; credspout = NULL; } error_return: krb5_free_cred_contents(context, &creds); if (credspout != NULL) krb5_free_creds(context, credspout); /* Solaris Kerberos */ if (!ccache && use_ccache) (void) krb5_cc_close(context, use_ccache); return(retval); }
static OM_uint32 copy_initiator_creds(OM_uint32 *minor_status, gss_cred_id_t input_cred_handle, const gss_OID desired_mech, OM_uint32 overwrite_cred, OM_uint32 default_cred, gss_const_key_value_set_t cred_store) { OM_uint32 major_status; krb5_error_code code; krb5_gss_cred_id_t kcred = NULL; krb5_context context = NULL; krb5_ccache ccache = NULL; const char *ccache_name; *minor_status = 0; if (!default_cred && cred_store == GSS_C_NO_CRED_STORE) { *minor_status = G_STORE_NON_DEFAULT_CRED_NOSUPP; major_status = GSS_S_FAILURE; goto cleanup; } code = krb5_gss_init_context(&context); if (code != 0) { *minor_status = code; major_status = GSS_S_FAILURE; goto cleanup; } major_status = krb5_gss_validate_cred_1(minor_status, input_cred_handle, context); if (GSS_ERROR(major_status)) goto cleanup; kcred = (krb5_gss_cred_id_t)input_cred_handle; if (kcred->ccache == NULL) { *minor_status = KG_CCACHE_NOMATCH; major_status = GSS_S_DEFECTIVE_CREDENTIAL; goto cleanup; } if (!overwrite_cred && has_unexpired_creds(kcred, desired_mech, default_cred, cred_store)) { major_status = GSS_S_DUPLICATE_ELEMENT; goto cleanup; } major_status = kg_value_from_cred_store(cred_store, KRB5_CS_CCACHE_URN, &ccache_name); if (GSS_ERROR(major_status)) goto cleanup; if (ccache_name != NULL) { code = krb5_cc_resolve(context, ccache_name, &ccache); if (code != 0) { *minor_status = code; major_status = GSS_S_CRED_UNAVAIL; goto cleanup; } code = krb5_cc_initialize(context, ccache, kcred->name->princ); if (code != 0) { *minor_status = code; major_status = GSS_S_CRED_UNAVAIL; goto cleanup; } } if (ccache == NULL) { if (!default_cred) { *minor_status = G_STORE_NON_DEFAULT_CRED_NOSUPP; major_status = GSS_S_FAILURE; goto cleanup; } code = krb5int_cc_default(context, &ccache); if (code != 0) { *minor_status = code; major_status = GSS_S_FAILURE; goto cleanup; } } code = krb5_cc_copy_creds(context, kcred->ccache, ccache); if (code != 0) { *minor_status = code; major_status = GSS_S_FAILURE; goto cleanup; } *minor_status = 0; major_status = GSS_S_COMPLETE; cleanup: if (kcred != NULL) k5_mutex_unlock(&kcred->lock); if (ccache != NULL) krb5_cc_close(context, ccache); krb5_free_context(context); return major_status; }