KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL krb5_principal_compare(krb5_context context, krb5_const_principal princ1, krb5_const_principal princ2) { if(!krb5_realm_compare(context, princ1, princ2)) return FALSE; return krb5_principal_compare_any_realm(context, princ1, princ2); }
static krb5_error_code check_reply_server(krb5_context context, krb5_flags kdcoptions, krb5_creds *in_cred, krb5_kdc_rep *dec_rep) { if (!krb5_principal_compare(context, dec_rep->ticket->server, dec_rep->enc_part2->server)) return KRB5_KDCREP_MODIFIED; /* Reply is self-consistent. */ if (krb5_principal_compare(context, dec_rep->ticket->server, in_cred->server)) return 0; /* Server in reply differs from what we requested. */ if (kdcoptions & KDC_OPT_CANONICALIZE) { /* in_cred server differs from ticket returned, but ticket returned is consistent and we requested canonicalization. */ #if 0 #ifdef DEBUG_REFERRALS printf("gc_via_tkt: in_cred and encoding don't match but referrals requested\n"); krb5int_dbgref_dump_principal("gc_via_tkt: in_cred",in_cred->server); krb5int_dbgref_dump_principal("gc_via_tkt: encoded server",dec_rep->enc_part2->server); #endif #endif return 0; } /* We didn't request canonicalization. */ if (!IS_TGS_PRINC(context, in_cred->server) || !IS_TGS_PRINC(context, dec_rep->ticket->server)) { /* Canonicalization not requested, and not a TGS referral. */ return KRB5_KDCREP_MODIFIED; } #if 0 /* * Is this check needed? find_nxt_kdc() in gc_frm_kdc.c already * effectively checks this. */ if (krb5_realm_compare(context, in_cred->client, in_cred->server) && data_eq(*in_cred->server->data[1], *in_cred->client->realm)) { /* Attempted to rewrite local TGS. */ return KRB5_KDCREP_MODIFIED; } #endif return 0; }
static krb5_error_code check_reply_server(krb5_context context, krb5_flags kdcoptions, krb5_creds *in_cred, krb5_kdc_rep *dec_rep) { if (!krb5_principal_compare(context, dec_rep->ticket->server, dec_rep->enc_part2->server)) return KRB5_KDCREP_MODIFIED; /* Reply is self-consistent. */ if (krb5_principal_compare(context, dec_rep->ticket->server, in_cred->server)) return 0; /* Server in reply differs from what we requested. */ if (kdcoptions & KDC_OPT_CANONICALIZE) { /* in_cred server differs from ticket returned, but ticket returned is consistent and we requested canonicalization. */ TRACE_CHECK_REPLY_SERVER_DIFFERS(context, in_cred->server, dec_rep->enc_part2->server); return 0; } /* We didn't request canonicalization. */ if (!IS_TGS_PRINC(in_cred->server) || !IS_TGS_PRINC(dec_rep->ticket->server)) { /* Canonicalization not requested, and not a TGS referral. */ return KRB5_KDCREP_MODIFIED; } #if 0 /* * Is this check needed? find_nxt_kdc() in gc_frm_kdc.c already * effectively checks this. */ if (krb5_realm_compare(context, in_cred->client, in_cred->server) && data_eq(*in_cred->server->data[1], *in_cred->client->realm)) { /* Attempted to rewrite local TGS. */ return KRB5_KDCREP_MODIFIED; } #endif return 0; }
krb5_boolean KRB5_CALLCONV krb5_principal_compare(krb5_context context, krb5_const_principal princ1, krb5_const_principal princ2) { register int i; krb5_int32 nelem; nelem = krb5_princ_size(context, princ1); if (nelem != krb5_princ_size(context, princ2)) return FALSE; if (! krb5_realm_compare(context, princ1, princ2)) return FALSE; for (i = 0; i < (int) nelem; i++) { register const krb5_data *p1 = krb5_princ_component(context, princ1, i); register const krb5_data *p2 = krb5_princ_component(context, princ2, i); if (!data_eq(*p1, *p2)) return FALSE; } return TRUE; }
static krb5_error_code get_cred_kdc_capath_worker(krb5_context context, krb5_kdc_flags flags, krb5_ccache ccache, krb5_creds *in_creds, krb5_const_realm try_realm, krb5_principal impersonate_principal, Ticket *second_ticket, krb5_creds **out_creds, krb5_creds ***ret_tgts) { krb5_error_code ret; krb5_creds *tgt, tmp_creds; krb5_const_realm client_realm, server_realm; int ok_as_delegate = 1; *out_creds = NULL; client_realm = krb5_principal_get_realm(context, in_creds->client); server_realm = krb5_principal_get_realm(context, in_creds->server); memset(&tmp_creds, 0, sizeof(tmp_creds)); ret = krb5_copy_principal(context, in_creds->client, &tmp_creds.client); if(ret) return ret; ret = krb5_make_principal(context, &tmp_creds.server, try_realm, KRB5_TGS_NAME, server_realm, NULL); if(ret){ krb5_free_principal(context, tmp_creds.client); return ret; } { krb5_creds tgts; ret = find_cred(context, ccache, tmp_creds.server, *ret_tgts, &tgts); if(ret == 0){ if (strcmp(try_realm, client_realm) != 0) ok_as_delegate = tgts.flags.b.ok_as_delegate; *out_creds = calloc(1, sizeof(**out_creds)); if(*out_creds == NULL) { ret = ENOMEM; krb5_set_error_message(context, ret, N_("malloc: out of memory", "")); } else { ret = get_cred_kdc_address(context, ccache, flags, NULL, in_creds, &tgts, impersonate_principal, second_ticket, *out_creds); if (ret) { free (*out_creds); *out_creds = NULL; } else if (ok_as_delegate == 0) (*out_creds)->flags.b.ok_as_delegate = 0; } krb5_free_cred_contents(context, &tgts); krb5_free_principal(context, tmp_creds.server); krb5_free_principal(context, tmp_creds.client); return ret; } } if(krb5_realm_compare(context, in_creds->client, in_creds->server)) return not_found(context, in_creds->server, KRB5_CC_NOTFOUND); /* XXX this can loop forever */ while(1){ heim_general_string tgt_inst; ret = get_cred_kdc_capath(context, flags, ccache, &tmp_creds, NULL, NULL, &tgt, ret_tgts); if(ret) { krb5_free_principal(context, tmp_creds.server); krb5_free_principal(context, tmp_creds.client); return ret; } /* * if either of the chain or the ok_as_delegate was stripped * by the kdc, make sure we strip it too. */ if (ok_as_delegate == 0 || tgt->flags.b.ok_as_delegate == 0) { ok_as_delegate = 0; tgt->flags.b.ok_as_delegate = 0; } ret = add_cred(context, tgt, ret_tgts); if(ret) { krb5_free_principal(context, tmp_creds.server); krb5_free_principal(context, tmp_creds.client); return ret; } tgt_inst = tgt->server->name.name_string.val[1]; if(strcmp(tgt_inst, server_realm) == 0) break; krb5_free_principal(context, tmp_creds.server); ret = krb5_make_principal(context, &tmp_creds.server, tgt_inst, KRB5_TGS_NAME, server_realm, NULL); if(ret) { krb5_free_principal(context, tmp_creds.server); krb5_free_principal(context, tmp_creds.client); return ret; } ret = krb5_free_creds(context, tgt); if(ret) { krb5_free_principal(context, tmp_creds.server); krb5_free_principal(context, tmp_creds.client); return ret; } } krb5_free_principal(context, tmp_creds.server); krb5_free_principal(context, tmp_creds.client); *out_creds = calloc(1, sizeof(**out_creds)); if(*out_creds == NULL) { ret = ENOMEM; krb5_set_error_message(context, ret, N_("malloc: out of memory", "")); } else { ret = get_cred_kdc_address (context, ccache, flags, NULL, in_creds, tgt, impersonate_principal, second_ticket, *out_creds); if (ret) { free (*out_creds); *out_creds = NULL; } } krb5_free_creds(context, tgt); return ret; }
/*ARGSUSED*/ krb5_error_code process_tgs_req(struct server_handle *handle, krb5_data *pkt, const krb5_fulladdr *from, krb5_data **response) { krb5_keyblock * subkey = 0; krb5_keyblock *header_key = NULL; krb5_kdc_req *request = 0; krb5_db_entry *server = NULL; krb5_db_entry *stkt_server = NULL; krb5_kdc_rep reply; krb5_enc_kdc_rep_part reply_encpart; krb5_ticket ticket_reply, *header_ticket = 0; int st_idx = 0; krb5_enc_tkt_part enc_tkt_reply; int newtransited = 0; krb5_error_code retval = 0; krb5_keyblock encrypting_key; krb5_timestamp kdc_time, authtime = 0; krb5_keyblock session_key; krb5_keyblock *reply_key = NULL; krb5_key_data *server_key; krb5_principal cprinc = NULL, sprinc = NULL, altcprinc = NULL; krb5_last_req_entry *nolrarray[2], nolrentry; int errcode; const char *status = 0; krb5_enc_tkt_part *header_enc_tkt = NULL; /* TGT */ krb5_enc_tkt_part *subject_tkt = NULL; /* TGT or evidence ticket */ krb5_db_entry *client = NULL, *header_server = NULL; krb5_db_entry *local_tgt, *local_tgt_storage = NULL; krb5_pa_s4u_x509_user *s4u_x509_user = NULL; /* protocol transition request */ krb5_authdata **kdc_issued_auth_data = NULL; /* auth data issued by KDC */ unsigned int c_flags = 0, s_flags = 0; /* client/server KDB flags */ krb5_boolean is_referral; const char *emsg = NULL; krb5_kvno ticket_kvno = 0; struct kdc_request_state *state = NULL; krb5_pa_data *pa_tgs_req; /*points into request*/ krb5_data scratch; krb5_pa_data **e_data = NULL; kdc_realm_t *kdc_active_realm = NULL; krb5_audit_state *au_state = NULL; krb5_data **auth_indicators = NULL; memset(&reply, 0, sizeof(reply)); memset(&reply_encpart, 0, sizeof(reply_encpart)); memset(&ticket_reply, 0, sizeof(ticket_reply)); memset(&enc_tkt_reply, 0, sizeof(enc_tkt_reply)); session_key.contents = NULL; retval = decode_krb5_tgs_req(pkt, &request); if (retval) return retval; /* Save pointer to client-requested service principal, in case of * errors before a successful call to search_sprinc(). */ sprinc = request->server; if (request->msg_type != KRB5_TGS_REQ) { krb5_free_kdc_req(handle->kdc_err_context, request); return KRB5_BADMSGTYPE; } /* * setup_server_realm() sets up the global realm-specific data pointer. */ kdc_active_realm = setup_server_realm(handle, request->server); if (kdc_active_realm == NULL) { krb5_free_kdc_req(handle->kdc_err_context, request); return KRB5KDC_ERR_WRONG_REALM; } errcode = kdc_make_rstate(kdc_active_realm, &state); if (errcode !=0) { krb5_free_kdc_req(handle->kdc_err_context, request); return errcode; } /* Initialize audit state. */ errcode = kau_init_kdc_req(kdc_context, request, from, &au_state); if (errcode) { krb5_free_kdc_req(handle->kdc_err_context, request); return errcode; } /* Seed the audit trail with the request ID and basic information. */ kau_tgs_req(kdc_context, TRUE, au_state); errcode = kdc_process_tgs_req(kdc_active_realm, request, from, pkt, &header_ticket, &header_server, &header_key, &subkey, &pa_tgs_req); if (header_ticket && header_ticket->enc_part2) cprinc = header_ticket->enc_part2->client; if (errcode) { status = "PROCESS_TGS"; goto cleanup; } if (!header_ticket) { errcode = KRB5_NO_TKT_SUPPLIED; /* XXX? */ status="UNEXPECTED NULL in header_ticket"; goto cleanup; } errcode = kau_make_tkt_id(kdc_context, header_ticket, &au_state->tkt_in_id); if (errcode) { status = "GENERATE_TICKET_ID"; goto cleanup; } scratch.length = pa_tgs_req->length; scratch.data = (char *) pa_tgs_req->contents; errcode = kdc_find_fast(&request, &scratch, subkey, header_ticket->enc_part2->session, state, NULL); /* Reset sprinc because kdc_find_fast() can replace request. */ sprinc = request->server; if (errcode !=0) { status = "FIND_FAST"; goto cleanup; } errcode = get_local_tgt(kdc_context, &sprinc->realm, header_server, &local_tgt, &local_tgt_storage); if (errcode) { status = "GET_LOCAL_TGT"; goto cleanup; } /* Ignore (for now) the request modification due to FAST processing. */ au_state->request = request; /* * Pointer to the encrypted part of the header ticket, which may be * replaced to point to the encrypted part of the evidence ticket * if constrained delegation is used. This simplifies the number of * special cases for constrained delegation. */ header_enc_tkt = header_ticket->enc_part2; /* * We've already dealt with the AP_REQ authentication, so we can * use header_ticket freely. The encrypted part (if any) has been * decrypted with the session key. */ au_state->stage = SRVC_PRINC; /* XXX make sure server here has the proper realm...taken from AP_REQ header? */ setflag(s_flags, KRB5_KDB_FLAG_ALIAS_OK); if (isflagset(request->kdc_options, KDC_OPT_CANONICALIZE)) { setflag(c_flags, KRB5_KDB_FLAG_CANONICALIZE); setflag(s_flags, KRB5_KDB_FLAG_CANONICALIZE); } errcode = search_sprinc(kdc_active_realm, request, s_flags, &server, &status); if (errcode != 0) goto cleanup; sprinc = server->princ; /* If we got a cross-realm TGS which is not the requested server, we are * issuing a referral (or alternate TGT, which we treat similarly). */ is_referral = is_cross_tgs_principal(server->princ) && !krb5_principal_compare(kdc_context, request->server, server->princ); au_state->stage = VALIDATE_POL; if ((errcode = krb5_timeofday(kdc_context, &kdc_time))) { status = "TIME_OF_DAY"; goto cleanup; } if ((retval = validate_tgs_request(kdc_active_realm, request, *server, header_ticket, kdc_time, &status, &e_data))) { if (!status) status = "UNKNOWN_REASON"; if (retval == KDC_ERR_POLICY || retval == KDC_ERR_BADOPTION) au_state->violation = PROT_CONSTRAINT; errcode = retval + ERROR_TABLE_BASE_krb5; goto cleanup; } if (!is_local_principal(kdc_active_realm, header_enc_tkt->client)) setflag(c_flags, KRB5_KDB_FLAG_CROSS_REALM); /* Check for protocol transition */ errcode = kdc_process_s4u2self_req(kdc_active_realm, request, header_enc_tkt->client, server, subkey, header_enc_tkt->session, kdc_time, &s4u_x509_user, &client, &status); if (s4u_x509_user != NULL || errcode != 0) { if (s4u_x509_user != NULL) au_state->s4u2self_user = s4u_x509_user->user_id.user; if (errcode == KDC_ERR_POLICY || errcode == KDC_ERR_BADOPTION) au_state->violation = PROT_CONSTRAINT; au_state->status = status; kau_s4u2self(kdc_context, errcode ? FALSE : TRUE, au_state); au_state->s4u2self_user = NULL; } if (errcode) goto cleanup; if (s4u_x509_user != NULL) { setflag(c_flags, KRB5_KDB_FLAG_PROTOCOL_TRANSITION); if (is_referral) { /* The requesting server appears to no longer exist, and we found * a referral instead. Treat this as a server lookup failure. */ errcode = KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN; status = "LOOKING_UP_SERVER"; goto cleanup; } } /* Deal with user-to-user and constrained delegation */ errcode = decrypt_2ndtkt(kdc_active_realm, request, c_flags, &stkt_server, &status); if (errcode) goto cleanup; if (isflagset(request->kdc_options, KDC_OPT_CNAME_IN_ADDL_TKT)) { /* Do constrained delegation protocol and authorization checks */ errcode = kdc_process_s4u2proxy_req(kdc_active_realm, request, request->second_ticket[st_idx]->enc_part2, stkt_server, header_ticket->enc_part2->client, request->server, &status); if (errcode == KDC_ERR_POLICY || errcode == KDC_ERR_BADOPTION) au_state->violation = PROT_CONSTRAINT; else if (errcode) au_state->violation = LOCAL_POLICY; au_state->status = status; retval = kau_make_tkt_id(kdc_context, request->second_ticket[st_idx], &au_state->evid_tkt_id); if (retval) { status = "GENERATE_TICKET_ID"; errcode = retval; goto cleanup; } kau_s4u2proxy(kdc_context, errcode ? FALSE : TRUE, au_state); if (errcode) goto cleanup; setflag(c_flags, KRB5_KDB_FLAG_CONSTRAINED_DELEGATION); assert(krb5_is_tgs_principal(header_ticket->server)); assert(client == NULL); /* assured by kdc_process_s4u2self_req() */ client = stkt_server; stkt_server = NULL; } else if (request->kdc_options & KDC_OPT_ENC_TKT_IN_SKEY) { krb5_db_free_principal(kdc_context, stkt_server); stkt_server = NULL; } else assert(stkt_server == NULL); au_state->stage = ISSUE_TKT; errcode = gen_session_key(kdc_active_realm, request, server, &session_key, &status); if (errcode) goto cleanup; /* * subject_tkt will refer to the evidence ticket (for constrained * delegation) or the TGT. The distinction from header_enc_tkt is * necessary because the TGS signature only protects some fields: * the others could be forged by a malicious server. */ if (isflagset(c_flags, KRB5_KDB_FLAG_CONSTRAINED_DELEGATION)) subject_tkt = request->second_ticket[st_idx]->enc_part2; else subject_tkt = header_enc_tkt; authtime = subject_tkt->times.authtime; /* Extract auth indicators from the subject ticket, except for S4U2Proxy * requests (where the client didn't authenticate). */ if (s4u_x509_user == NULL) { errcode = get_auth_indicators(kdc_context, subject_tkt, local_tgt, &auth_indicators); if (errcode) { status = "GET_AUTH_INDICATORS"; goto cleanup; } } errcode = check_indicators(kdc_context, server, auth_indicators); if (errcode) { status = "HIGHER_AUTHENTICATION_REQUIRED"; goto cleanup; } if (is_referral) ticket_reply.server = server->princ; else ticket_reply.server = request->server; /* XXX careful for realm... */ enc_tkt_reply.flags = OPTS2FLAGS(request->kdc_options); enc_tkt_reply.flags |= COPY_TKT_FLAGS(header_enc_tkt->flags); enc_tkt_reply.times.starttime = 0; if (isflagset(server->attributes, KRB5_KDB_OK_AS_DELEGATE)) setflag(enc_tkt_reply.flags, TKT_FLG_OK_AS_DELEGATE); /* Indicate support for encrypted padata (RFC 6806). */ setflag(enc_tkt_reply.flags, TKT_FLG_ENC_PA_REP); /* don't use new addresses unless forwarded, see below */ enc_tkt_reply.caddrs = header_enc_tkt->caddrs; /* noaddrarray[0] = 0; */ reply_encpart.caddrs = 0;/* optional...don't put it in */ reply_encpart.enc_padata = NULL; /* * It should be noted that local policy may affect the * processing of any of these flags. For example, some * realms may refuse to issue renewable tickets */ if (isflagset(request->kdc_options, KDC_OPT_FORWARDABLE)) { if (isflagset(c_flags, KRB5_KDB_FLAG_PROTOCOL_TRANSITION)) { /* * If S4U2Self principal is not forwardable, then mark ticket as * unforwardable. This behaviour matches Windows, but it is * different to the MIT AS-REQ path, which returns an error * (KDC_ERR_POLICY) if forwardable tickets cannot be issued. * * Consider this block the S4U2Self equivalent to * validate_forwardable(). */ if (client != NULL && isflagset(client->attributes, KRB5_KDB_DISALLOW_FORWARDABLE)) clear(enc_tkt_reply.flags, TKT_FLG_FORWARDABLE); /* * Forwardable flag is propagated along referral path. */ else if (!isflagset(header_enc_tkt->flags, TKT_FLG_FORWARDABLE)) clear(enc_tkt_reply.flags, TKT_FLG_FORWARDABLE); /* * OK_TO_AUTH_AS_DELEGATE must be set on the service requesting * S4U2Self in order for forwardable tickets to be returned. */ else if (!is_referral && !isflagset(server->attributes, KRB5_KDB_OK_TO_AUTH_AS_DELEGATE)) clear(enc_tkt_reply.flags, TKT_FLG_FORWARDABLE); } } if (isflagset(request->kdc_options, KDC_OPT_FORWARDED) || isflagset(request->kdc_options, KDC_OPT_PROXY)) { /* include new addresses in ticket & reply */ enc_tkt_reply.caddrs = request->addresses; reply_encpart.caddrs = request->addresses; } /* We don't currently handle issuing anonymous tickets based on * non-anonymous ones, so just ignore the option. */ if (isflagset(request->kdc_options, KDC_OPT_REQUEST_ANONYMOUS) && !isflagset(header_enc_tkt->flags, TKT_FLG_ANONYMOUS)) clear(enc_tkt_reply.flags, TKT_FLG_ANONYMOUS); if (isflagset(request->kdc_options, KDC_OPT_POSTDATED)) { setflag(enc_tkt_reply.flags, TKT_FLG_INVALID); enc_tkt_reply.times.starttime = request->from; } else enc_tkt_reply.times.starttime = kdc_time; if (isflagset(request->kdc_options, KDC_OPT_VALIDATE)) { assert(isflagset(c_flags, KRB5_KDB_FLAGS_S4U) == 0); /* BEWARE of allocation hanging off of ticket & enc_part2, it belongs to the caller */ ticket_reply = *(header_ticket); enc_tkt_reply = *(header_ticket->enc_part2); enc_tkt_reply.authorization_data = NULL; clear(enc_tkt_reply.flags, TKT_FLG_INVALID); } if (isflagset(request->kdc_options, KDC_OPT_RENEW)) { krb5_timestamp old_starttime; krb5_deltat old_life; assert(isflagset(c_flags, KRB5_KDB_FLAGS_S4U) == 0); /* BEWARE of allocation hanging off of ticket & enc_part2, it belongs to the caller */ ticket_reply = *(header_ticket); enc_tkt_reply = *(header_ticket->enc_part2); enc_tkt_reply.authorization_data = NULL; old_starttime = enc_tkt_reply.times.starttime ? enc_tkt_reply.times.starttime : enc_tkt_reply.times.authtime; old_life = enc_tkt_reply.times.endtime - old_starttime; enc_tkt_reply.times.starttime = kdc_time; enc_tkt_reply.times.endtime = min(header_ticket->enc_part2->times.renew_till, kdc_time + old_life); } else { /* not a renew request */ enc_tkt_reply.times.starttime = kdc_time; kdc_get_ticket_endtime(kdc_active_realm, enc_tkt_reply.times.starttime, header_enc_tkt->times.endtime, request->till, client, server, &enc_tkt_reply.times.endtime); } kdc_get_ticket_renewtime(kdc_active_realm, request, header_enc_tkt, client, server, &enc_tkt_reply); /* * Set authtime to be the same as header or evidence ticket's */ enc_tkt_reply.times.authtime = authtime; /* starttime is optional, and treated as authtime if not present. so we can nuke it if it matches */ if (enc_tkt_reply.times.starttime == enc_tkt_reply.times.authtime) enc_tkt_reply.times.starttime = 0; if (isflagset(c_flags, KRB5_KDB_FLAG_PROTOCOL_TRANSITION)) { altcprinc = s4u_x509_user->user_id.user; } else if (isflagset(c_flags, KRB5_KDB_FLAG_CONSTRAINED_DELEGATION)) { altcprinc = subject_tkt->client; } else { altcprinc = NULL; } if (isflagset(request->kdc_options, KDC_OPT_ENC_TKT_IN_SKEY)) { krb5_enc_tkt_part *t2enc = request->second_ticket[st_idx]->enc_part2; encrypting_key = *(t2enc->session); } else { /* * Find the server key */ if ((errcode = krb5_dbe_find_enctype(kdc_context, server, -1, /* ignore keytype */ -1, /* Ignore salttype */ 0, /* Get highest kvno */ &server_key))) { status = "FINDING_SERVER_KEY"; goto cleanup; } /* * Convert server.key into a real key * (it may be encrypted in the database) */ if ((errcode = krb5_dbe_decrypt_key_data(kdc_context, NULL, server_key, &encrypting_key, NULL))) { status = "DECRYPT_SERVER_KEY"; goto cleanup; } } if (isflagset(c_flags, KRB5_KDB_FLAG_CONSTRAINED_DELEGATION)) { /* * Don't allow authorization data to be disabled if constrained * delegation is requested. We don't want to deny the server * the ability to validate that delegation was used. */ clear(server->attributes, KRB5_KDB_NO_AUTH_DATA_REQUIRED); } if (isflagset(server->attributes, KRB5_KDB_NO_AUTH_DATA_REQUIRED) == 0) { /* * If we are not doing protocol transition/constrained delegation * try to lookup the client principal so plugins can add additional * authorization information. * * Always validate authorization data for constrained delegation * because we must validate the KDC signatures. */ if (!isflagset(c_flags, KRB5_KDB_FLAGS_S4U)) { /* Generate authorization data so we can include it in ticket */ setflag(c_flags, KRB5_KDB_FLAG_INCLUDE_PAC); /* Map principals from foreign (possibly non-AD) realms */ setflag(c_flags, KRB5_KDB_FLAG_MAP_PRINCIPALS); assert(client == NULL); /* should not have been set already */ errcode = krb5_db_get_principal(kdc_context, subject_tkt->client, c_flags, &client); } } if (isflagset(c_flags, KRB5_KDB_FLAG_PROTOCOL_TRANSITION) && !isflagset(c_flags, KRB5_KDB_FLAG_CROSS_REALM)) enc_tkt_reply.client = s4u_x509_user->user_id.user; else enc_tkt_reply.client = subject_tkt->client; enc_tkt_reply.session = &session_key; enc_tkt_reply.transited.tr_type = KRB5_DOMAIN_X500_COMPRESS; enc_tkt_reply.transited.tr_contents = empty_string; /* equivalent of "" */ /* * Only add the realm of the presented tgt to the transited list if * it is different than the local realm (cross-realm) and it is different * than the realm of the client (since the realm of the client is already * implicitly part of the transited list and should not be explicitly * listed). */ /* realm compare is like strcmp, but knows how to deal with these args */ if (krb5_realm_compare(kdc_context, header_ticket->server, tgs_server) || krb5_realm_compare(kdc_context, header_ticket->server, enc_tkt_reply.client)) { /* tgt issued by local realm or issued by realm of client */ enc_tkt_reply.transited = header_enc_tkt->transited; } else { /* tgt issued by some other realm and not the realm of the client */ /* assemble new transited field into allocated storage */ if (header_enc_tkt->transited.tr_type != KRB5_DOMAIN_X500_COMPRESS) { status = "VALIDATE_TRANSIT_TYPE"; errcode = KRB5KDC_ERR_TRTYPE_NOSUPP; goto cleanup; } memset(&enc_tkt_reply.transited, 0, sizeof(enc_tkt_reply.transited)); enc_tkt_reply.transited.tr_type = KRB5_DOMAIN_X500_COMPRESS; if ((errcode = add_to_transited(&header_enc_tkt->transited.tr_contents, &enc_tkt_reply.transited.tr_contents, header_ticket->server, enc_tkt_reply.client, request->server))) { status = "ADD_TO_TRANSITED_LIST"; goto cleanup; } newtransited = 1; } if (isflagset(c_flags, KRB5_KDB_FLAG_CROSS_REALM)) { errcode = validate_transit_path(kdc_context, header_enc_tkt->client, server, header_server); if (errcode) { status = "NON_TRANSITIVE"; goto cleanup; } } if (!isflagset (request->kdc_options, KDC_OPT_DISABLE_TRANSITED_CHECK)) { errcode = kdc_check_transited_list (kdc_active_realm, &enc_tkt_reply.transited.tr_contents, krb5_princ_realm (kdc_context, header_enc_tkt->client), krb5_princ_realm (kdc_context, request->server)); if (errcode == 0) { setflag (enc_tkt_reply.flags, TKT_FLG_TRANSIT_POLICY_CHECKED); } else { log_tgs_badtrans(kdc_context, cprinc, sprinc, &enc_tkt_reply.transited.tr_contents, errcode); } } else krb5_klog_syslog(LOG_INFO, _("not checking transit path")); if (kdc_active_realm->realm_reject_bad_transit && !isflagset(enc_tkt_reply.flags, TKT_FLG_TRANSIT_POLICY_CHECKED)) { errcode = KRB5KDC_ERR_POLICY; status = "BAD_TRANSIT"; au_state->violation = LOCAL_POLICY; goto cleanup; } errcode = handle_authdata(kdc_context, c_flags, client, server, header_server, local_tgt, subkey != NULL ? subkey : header_ticket->enc_part2->session, &encrypting_key, /* U2U or server key */ header_key, pkt, request, s4u_x509_user ? s4u_x509_user->user_id.user : NULL, subject_tkt, auth_indicators, &enc_tkt_reply); if (errcode) { krb5_klog_syslog(LOG_INFO, _("TGS_REQ : handle_authdata (%d)"), errcode); status = "HANDLE_AUTHDATA"; goto cleanup; } ticket_reply.enc_part2 = &enc_tkt_reply; /* * If we are doing user-to-user authentication, then make sure * that the client for the second ticket matches the request * server, and then encrypt the ticket using the session key of * the second ticket. */ if (isflagset(request->kdc_options, KDC_OPT_ENC_TKT_IN_SKEY)) { /* * Make sure the client for the second ticket matches * requested server. */ krb5_enc_tkt_part *t2enc = request->second_ticket[st_idx]->enc_part2; krb5_principal client2 = t2enc->client; if (!krb5_principal_compare(kdc_context, request->server, client2)) { altcprinc = client2; errcode = KRB5KDC_ERR_SERVER_NOMATCH; status = "2ND_TKT_MISMATCH"; au_state->status = status; kau_u2u(kdc_context, FALSE, au_state); goto cleanup; } ticket_kvno = 0; ticket_reply.enc_part.enctype = t2enc->session->enctype; kau_u2u(kdc_context, TRUE, au_state); st_idx++; } else { ticket_kvno = server_key->key_data_kvno; } errcode = krb5_encrypt_tkt_part(kdc_context, &encrypting_key, &ticket_reply); if (!isflagset(request->kdc_options, KDC_OPT_ENC_TKT_IN_SKEY)) krb5_free_keyblock_contents(kdc_context, &encrypting_key); if (errcode) { status = "ENCRYPT_TICKET"; goto cleanup; } ticket_reply.enc_part.kvno = ticket_kvno; /* Start assembling the response */ au_state->stage = ENCR_REP; reply.msg_type = KRB5_TGS_REP; if (isflagset(c_flags, KRB5_KDB_FLAG_PROTOCOL_TRANSITION) && krb5int_find_pa_data(kdc_context, request->padata, KRB5_PADATA_S4U_X509_USER) != NULL) { errcode = kdc_make_s4u2self_rep(kdc_context, subkey, header_ticket->enc_part2->session, s4u_x509_user, &reply, &reply_encpart); if (errcode) { status = "MAKE_S4U2SELF_PADATA"; au_state->status = status; } kau_s4u2self(kdc_context, errcode ? FALSE : TRUE, au_state); if (errcode) goto cleanup; } reply.client = enc_tkt_reply.client; reply.enc_part.kvno = 0;/* We are using the session key */ reply.ticket = &ticket_reply; reply_encpart.session = &session_key; reply_encpart.nonce = request->nonce; /* copy the time fields */ reply_encpart.times = enc_tkt_reply.times; nolrentry.lr_type = KRB5_LRQ_NONE; nolrentry.value = 0; nolrentry.magic = 0; nolrarray[0] = &nolrentry; nolrarray[1] = 0; reply_encpart.last_req = nolrarray; /* not available for TGS reqs */ reply_encpart.key_exp = 0;/* ditto */ reply_encpart.flags = enc_tkt_reply.flags; reply_encpart.server = ticket_reply.server; /* use the session key in the ticket, unless there's a subsession key in the AP_REQ */ reply.enc_part.enctype = subkey ? subkey->enctype : header_ticket->enc_part2->session->enctype; errcode = kdc_fast_response_handle_padata(state, request, &reply, subkey ? subkey->enctype : header_ticket->enc_part2->session->enctype); if (errcode !=0 ) { status = "MAKE_FAST_RESPONSE"; goto cleanup; } errcode =kdc_fast_handle_reply_key(state, subkey?subkey:header_ticket->enc_part2->session, &reply_key); if (errcode) { status = "MAKE_FAST_REPLY_KEY"; goto cleanup; } errcode = return_enc_padata(kdc_context, pkt, request, reply_key, server, &reply_encpart, is_referral && isflagset(s_flags, KRB5_KDB_FLAG_CANONICALIZE)); if (errcode) { status = "KDC_RETURN_ENC_PADATA"; goto cleanup; } errcode = kau_make_tkt_id(kdc_context, &ticket_reply, &au_state->tkt_out_id); if (errcode) { status = "GENERATE_TICKET_ID"; goto cleanup; } if (kdc_fast_hide_client(state)) reply.client = (krb5_principal)krb5_anonymous_principal(); errcode = krb5_encode_kdc_rep(kdc_context, KRB5_TGS_REP, &reply_encpart, subkey ? 1 : 0, reply_key, &reply, response); if (errcode) { status = "ENCODE_KDC_REP"; } else { status = "ISSUE"; } memset(ticket_reply.enc_part.ciphertext.data, 0, ticket_reply.enc_part.ciphertext.length); free(ticket_reply.enc_part.ciphertext.data); /* these parts are left on as a courtesy from krb5_encode_kdc_rep so we can use them in raw form if needed. But, we don't... */ memset(reply.enc_part.ciphertext.data, 0, reply.enc_part.ciphertext.length); free(reply.enc_part.ciphertext.data); cleanup: assert(status != NULL); if (reply_key) krb5_free_keyblock(kdc_context, reply_key); if (errcode) emsg = krb5_get_error_message (kdc_context, errcode); au_state->status = status; if (!errcode) au_state->reply = &reply; kau_tgs_req(kdc_context, errcode ? FALSE : TRUE, au_state); kau_free_kdc_req(au_state); log_tgs_req(kdc_context, from, request, &reply, cprinc, sprinc, altcprinc, authtime, c_flags, status, errcode, emsg); if (errcode) { krb5_free_error_message (kdc_context, emsg); emsg = NULL; } if (errcode) { int got_err = 0; if (status == 0) { status = krb5_get_error_message (kdc_context, errcode); got_err = 1; } errcode -= ERROR_TABLE_BASE_krb5; if (errcode < 0 || errcode > KRB_ERR_MAX) errcode = KRB_ERR_GENERIC; retval = prepare_error_tgs(state, request, header_ticket, errcode, (server != NULL) ? server->princ : NULL, response, status, e_data); if (got_err) { krb5_free_error_message (kdc_context, status); status = 0; } } if (header_ticket != NULL) krb5_free_ticket(kdc_context, header_ticket); if (request != NULL) krb5_free_kdc_req(kdc_context, request); if (state) kdc_free_rstate(state); krb5_db_free_principal(kdc_context, server); krb5_db_free_principal(kdc_context, header_server); krb5_db_free_principal(kdc_context, client); krb5_db_free_principal(kdc_context, local_tgt_storage); if (session_key.contents != NULL) krb5_free_keyblock_contents(kdc_context, &session_key); if (newtransited) free(enc_tkt_reply.transited.tr_contents.data); if (s4u_x509_user != NULL) krb5_free_pa_s4u_x509_user(kdc_context, s4u_x509_user); if (kdc_issued_auth_data != NULL) krb5_free_authdata(kdc_context, kdc_issued_auth_data); if (subkey != NULL) krb5_free_keyblock(kdc_context, subkey); if (header_key != NULL) krb5_free_keyblock(kdc_context, header_key); if (reply.padata) krb5_free_pa_data(kdc_context, reply.padata); if (reply_encpart.enc_padata) krb5_free_pa_data(kdc_context, reply_encpart.enc_padata); if (enc_tkt_reply.authorization_data != NULL) krb5_free_authdata(kdc_context, enc_tkt_reply.authorization_data); krb5_free_pa_data(kdc_context, e_data); k5_free_data_ptr_list(auth_indicators); return retval; }
/* * Convert 'identifier' into canonical form. * Returns a pointer to a static buffer containing the canonical form * or NULL if 'identifier' is invalid. */ static char *afspts_canonifyid(const char *identifier, size_t len) { static char *retbuf = NULL; char *tmp = NULL; krb5_context context; krb5_principal princ, princ_dummy; char *realm; char *realmbegin; int striprealm = 0; char *identifier2; if(retbuf) free(retbuf); retbuf = NULL; if(!identifier) return NULL; if(!len) len = strlen(identifier); if (strcasecmp(identifier, "anonymous") == 0) return "anonymous"; if (strcasecmp(identifier, "anyone") == 0) return "anyone"; identifier2 = strdup(identifier); if (tmp = strchr(identifier2, '+')) { syslog(LOG_DEBUG, "afspts_canonifyid stripping: %s", identifier2); tmp[0] = 0; syslog(LOG_DEBUG, "afspts_canonifyid stripped: %s", identifier2); } if (krb5_init_context(&context)) { syslog(LOG_ERR, "afspts_canonifyid krb5_init_context failed"); return NULL; } if (krb5_parse_name(context,identifier2,&princ)) { krb5_free_context(context); free(identifier2); syslog(LOG_ERR, "afspts_canonifyid krb5_parse_name failed"); return NULL; } free(identifier2); if(config_getswitch(IMAPOPT_PTSKRB5_STRIP_DEFAULT_REALM)) { /* get local realm */ if (krb5_get_default_realm(context,&realm)) { krb5_free_principal(context,princ); krb5_free_context(context); syslog(LOG_ERR, "afspts_canonifyid krb5_get_default_realm failed"); return NULL; } /* build dummy princ to compare realms */ if (krb5_build_principal(context,&princ_dummy, strlen(realm),realm,"dummy",0)) { krb5_free_principal(context,princ); krb5_free_context(context); free(realm); syslog(LOG_ERR, "afspts_canonifyid krb5_build_principal failed"); return NULL; } /* is this principal local ? */ if (krb5_realm_compare(context,princ,princ_dummy)) { striprealm = 1; } /* done w/ dummy princ free it & realm */ krb5_free_principal(context,princ_dummy); free(realm); } if (config_getswitch(IMAPOPT_PTSKRB5_CONVERT524)) { char nbuf[64], ibuf[64], rbuf[64]; if (krb5_524_conv_principal(context, princ, nbuf, ibuf, rbuf)) { krb5_free_principal(context,princ); krb5_free_context(context); return NULL; } retbuf = xmalloc(3*64 + 3); sprintf(retbuf, "%s%s%s%s%s", nbuf, ibuf[0] ? "." : "", ibuf, rbuf[0] ? "@" : "", rbuf); } else { /* get the text version of princ */ if (krb5_unparse_name(context,princ,&retbuf)) { krb5_free_principal(context,princ); krb5_free_context(context); syslog(LOG_ERR, "afspts_canonifyid krb5_unparse_name failed"); return NULL; } } /* we have the canonical name pointed to by p -- strip realm if local */ realmbegin = strrchr(retbuf, '@'); if(realmbegin) { if(!striprealm) { realm = realmbegin+1; if(is_local_realm(realm)) striprealm = 1; } if(striprealm) { *realmbegin = '\0'; } else { /* Force realm to uppercase */ while(*(++realmbegin)) { *realmbegin = toupper(*realmbegin); } } } krb5_free_principal(context,princ); krb5_free_context(context); return retbuf; }
static krb5_error_code check_server_referral(krb5_context context, krb5_kdc_rep *rep, unsigned flags, krb5_const_principal requested, krb5_const_principal returned, krb5_keyblock * key) { krb5_error_code ret; PA_ServerReferralData ref; krb5_crypto session; EncryptedData ed; size_t len; krb5_data data; PA_DATA *pa; int i = 0, cmp; if (rep->kdc_rep.padata == NULL) goto noreferral; pa = krb5_find_padata(rep->kdc_rep.padata->val, rep->kdc_rep.padata->len, KRB5_PADATA_SERVER_REFERRAL, &i); if (pa == NULL) goto noreferral; memset(&ed, 0, sizeof(ed)); memset(&ref, 0, sizeof(ref)); ret = decode_EncryptedData(pa->padata_value.data, pa->padata_value.length, &ed, &len); if (ret) return ret; if (len != pa->padata_value.length) { free_EncryptedData(&ed); krb5_set_error_message(context, KRB5KRB_AP_ERR_MODIFIED, N_("Referral EncryptedData wrong for realm %s", "realm"), requested->realm); return KRB5KRB_AP_ERR_MODIFIED; } ret = krb5_crypto_init(context, key, 0, &session); if (ret) { free_EncryptedData(&ed); return ret; } ret = krb5_decrypt_EncryptedData(context, session, KRB5_KU_PA_SERVER_REFERRAL, &ed, &data); free_EncryptedData(&ed); krb5_crypto_destroy(context, session); if (ret) return ret; ret = decode_PA_ServerReferralData(data.data, data.length, &ref, &len); if (ret) { krb5_data_free(&data); return ret; } krb5_data_free(&data); if (strcmp(requested->realm, returned->realm) != 0) { free_PA_ServerReferralData(&ref); krb5_set_error_message(context, KRB5KRB_AP_ERR_MODIFIED, N_("server ref realm mismatch, " "requested realm %s got back %s", ""), requested->realm, returned->realm); return KRB5KRB_AP_ERR_MODIFIED; } if (krb5_principal_is_krbtgt(context, returned)) { const char *realm = returned->name.name_string.val[1]; if (ref.referred_realm == NULL || strcmp(*ref.referred_realm, realm) != 0) { free_PA_ServerReferralData(&ref); krb5_set_error_message(context, KRB5KRB_AP_ERR_MODIFIED, N_("tgt returned with wrong ref", "")); return KRB5KRB_AP_ERR_MODIFIED; } } else if (krb5_principal_compare(context, returned, requested) == 0) { free_PA_ServerReferralData(&ref); krb5_set_error_message(context, KRB5KRB_AP_ERR_MODIFIED, N_("req princ no same as returned", "")); return KRB5KRB_AP_ERR_MODIFIED; } if (ref.requested_principal_name) { cmp = _krb5_principal_compare_PrincipalName(context, requested, ref.requested_principal_name); if (!cmp) { free_PA_ServerReferralData(&ref); krb5_set_error_message(context, KRB5KRB_AP_ERR_MODIFIED, N_("referred principal not same " "as requested", "")); return KRB5KRB_AP_ERR_MODIFIED; } } else if (flags & EXTRACT_TICKET_AS_REQ) { free_PA_ServerReferralData(&ref); krb5_set_error_message(context, KRB5KRB_AP_ERR_MODIFIED, N_("Requested principal missing on AS-REQ", "")); return KRB5KRB_AP_ERR_MODIFIED; } free_PA_ServerReferralData(&ref); return ret; noreferral: /* * Expect excact match or that we got a krbtgt */ if (krb5_principal_compare(context, requested, returned) != TRUE && (krb5_realm_compare(context, requested, returned) != TRUE && krb5_principal_is_krbtgt(context, returned) != TRUE)) { krb5_set_error_message(context, KRB5KRB_AP_ERR_MODIFIED, N_("Not same server principal returned " "as requested", "")); return KRB5KRB_AP_ERR_MODIFIED; } return 0; }
static krb5_error_code get_cred_from_kdc_flags(krb5_context context, krb5_kdc_flags flags, krb5_ccache ccache, krb5_creds *in_creds, krb5_creds **out_creds, krb5_creds ***ret_tgts) { krb5_error_code ret; krb5_creds *tgt, tmp_creds; krb5_const_realm client_realm, server_realm, try_realm; *out_creds = NULL; client_realm = krb5_principal_get_realm(context, in_creds->client); server_realm = krb5_principal_get_realm(context, in_creds->server); memset(&tmp_creds, 0, sizeof(tmp_creds)); ret = krb5_copy_principal(context, in_creds->client, &tmp_creds.client); if(ret) return ret; try_realm = krb5_config_get_string(context, NULL, "capaths", client_realm, server_realm, NULL); #if 1 /* XXX remove in future release */ if(try_realm == NULL) try_realm = krb5_config_get_string(context, NULL, "libdefaults", "capath", server_realm, NULL); #endif if (try_realm == NULL) try_realm = client_realm; ret = krb5_make_principal(context, &tmp_creds.server, try_realm, KRB5_TGS_NAME, server_realm, NULL); if(ret){ krb5_free_principal(context, tmp_creds.client); return ret; } { krb5_creds tgts; /* XXX try krb5_cc_retrieve_cred first? */ ret = find_cred(context, ccache, tmp_creds.server, *ret_tgts, &tgts); if(ret == 0){ *out_creds = calloc(1, sizeof(**out_creds)); if(*out_creds == NULL) { krb5_set_error_string(context, "malloc: out of memory"); ret = ENOMEM; } else { krb5_boolean noaddr; krb5_appdefault_boolean(context, NULL, tgts.server->realm, "no-addresses", FALSE, &noaddr); if (noaddr) ret = get_cred_kdc(context, ccache, flags, NULL, in_creds, &tgts, *out_creds); else ret = get_cred_kdc_la(context, ccache, flags, in_creds, &tgts, *out_creds); if (ret) { free (*out_creds); *out_creds = NULL; } } krb5_free_cred_contents(context, &tgts); krb5_free_principal(context, tmp_creds.server); krb5_free_principal(context, tmp_creds.client); return ret; } } if(krb5_realm_compare(context, in_creds->client, in_creds->server)) { krb5_clear_error_string (context); return KRB5_CC_NOTFOUND; } /* XXX this can loop forever */ while(1){ heim_general_string tgt_inst; ret = get_cred_from_kdc_flags(context, flags, ccache, &tmp_creds, &tgt, ret_tgts); if(ret) { krb5_free_principal(context, tmp_creds.server); krb5_free_principal(context, tmp_creds.client); return ret; } ret = add_cred(context, ret_tgts, tgt); if(ret) { krb5_free_principal(context, tmp_creds.server); krb5_free_principal(context, tmp_creds.client); return ret; } tgt_inst = tgt->server->name.name_string.val[1]; if(strcmp(tgt_inst, server_realm) == 0) break; krb5_free_principal(context, tmp_creds.server); ret = krb5_make_principal(context, &tmp_creds.server, tgt_inst, KRB5_TGS_NAME, server_realm, NULL); if(ret) { krb5_free_principal(context, tmp_creds.server); krb5_free_principal(context, tmp_creds.client); return ret; } ret = krb5_free_creds(context, tgt); if(ret) { krb5_free_principal(context, tmp_creds.server); krb5_free_principal(context, tmp_creds.client); return ret; } } krb5_free_principal(context, tmp_creds.server); krb5_free_principal(context, tmp_creds.client); *out_creds = calloc(1, sizeof(**out_creds)); if(*out_creds == NULL) { krb5_set_error_string(context, "malloc: out of memory"); ret = ENOMEM; } else { krb5_boolean noaddr; krb5_appdefault_boolean(context, NULL, tgt->server->realm, "no-addresses", FALSE, &noaddr); if (noaddr) ret = get_cred_kdc (context, ccache, flags, NULL, in_creds, tgt, *out_creds); else ret = get_cred_kdc_la(context, ccache, flags, in_creds, tgt, *out_creds); if (ret) { free (*out_creds); *out_creds = NULL; } } krb5_free_creds(context, tgt); return ret; }
static void cc_test(krb5_context context, const char *name, krb5_flags flags) { krb5_ccache id, id2; krb5_creds creds; krb5_error_code kret; krb5_cc_cursor cursor; krb5_principal tmp; const char *c_name; char newcache[300]; char *save_type; kret = init_test_cred(context); CHECK(kret, "init_creds"); kret = krb5_cc_resolve(context, name, &id); CHECK(kret, "resolve"); kret = krb5_cc_initialize(context, id, test_creds.client); CHECK(kret, "initialize"); c_name = krb5_cc_get_name(context, id); CHECK_STR(c_name, "get_name"); c_name = krb5_cc_get_type(context, id); CHECK_STR(c_name, "get_type"); save_type=strdup(c_name); CHECK_STR(save_type, "copying type"); kret = krb5_cc_store_cred(context, id, &test_creds); CHECK(kret, "store"); kret = krb5_cc_get_principal(context, id, &tmp); CHECK(kret, "get_principal"); CHECK_BOOL(krb5_realm_compare(context, tmp, test_creds.client) != TRUE, "realms do not match", "realm_compare"); CHECK_BOOL(krb5_principal_compare(context, tmp, test_creds.client) != TRUE, "principals do not match", "principal_compare"); krb5_free_principal(context, tmp); kret = krb5_cc_set_flags (context, id, flags); CHECK(kret, "set_flags"); kret = krb5_cc_start_seq_get(context, id, &cursor); CHECK(kret, "start_seq_get"); kret = 0; while (kret != KRB5_CC_END) { if(debug) printf("Calling next_cred\n"); kret = krb5_cc_next_cred(context, id, &cursor, &creds); if(kret == KRB5_CC_END) { if(debug) printf("next_cred: ok at end\n"); } else { CHECK(kret, "next_cred"); krb5_free_cred_contents(context, &creds); } } kret = krb5_cc_end_seq_get(context, id, &cursor); CHECK(kret, "end_seq_get"); kret = krb5_cc_close(context, id); CHECK(kret, "close"); /* ------------------------------------------------- */ kret = krb5_cc_resolve(context, name, &id); CHECK(kret, "resolve2"); { /* Copy the cache test*/ snprintf(newcache, sizeof(newcache), "%s.new", name); kret = krb5_cc_resolve(context, newcache, &id2); CHECK(kret, "resolve of new cache"); /* This should fail as the new creds are not initialized */ kret = krb5_cc_copy_creds(context, id, id2); CHECK_FAIL(KRB5_FCC_NOFILE, kret, "copy_creds"); kret = krb5_cc_initialize(context, id2, test_creds.client); CHECK(kret, "initialize of id2"); kret = krb5_cc_copy_creds(context, id, id2); CHECK(kret, "copy_creds"); kret = krb5_cc_destroy(context, id2); CHECK(kret, "destroy new cache"); } /* Destroy the first cache */ kret = krb5_cc_destroy(context, id); CHECK(kret, "destroy"); /* ----------------------------------------------------- */ /* Tests the generate new code */ kret = krb5_cc_new_unique(context, save_type, NULL, &id2); CHECK(kret, "new_unique"); kret = krb5_cc_initialize(context, id2, test_creds.client); CHECK(kret, "initialize"); kret = krb5_cc_store_cred(context, id2, &test_creds); CHECK(kret, "store"); kret = krb5_cc_destroy(context, id2); CHECK(kret, "destroy id2"); free(save_type); free_test_cred(context); }
krb5_error_code sss_krb5_unparse_name_flags(krb5_context context, krb5_const_principal principal, int flags, char **name) { #ifdef HAVE_KRB5_UNPARSE_NAME_FLAGS return krb5_unparse_name_flags(context, principal, flags, name); #else char *cp, *q; int i; int length; krb5_int32 nelem; unsigned int totalsize = 0; char *default_realm = NULL; krb5_error_code ret = 0; if (name != NULL) *name = NULL; if (!principal || !name) return KRB5_PARSE_MALFORMED; if (flags & KRB5_PRINCIPAL_UNPARSE_SHORT) { /* omit realm if local realm */ krb5_principal_data p; ret = krb5_get_default_realm(context, &default_realm); if (ret != 0) goto cleanup; krb5_princ_realm(context, &p)->length = strlen(default_realm); krb5_princ_realm(context, &p)->data = default_realm; if (krb5_realm_compare(context, &p, principal)) flags |= KRB5_PRINCIPAL_UNPARSE_NO_REALM; } if ((flags & KRB5_PRINCIPAL_UNPARSE_NO_REALM) == 0) { totalsize += sss_krb5_component_length_quoted(krb5_princ_realm(context, principal), flags); totalsize++; } nelem = krb5_princ_size(context, principal); for (i = 0; i < (int) nelem; i++) { cp = krb5_princ_component(context, principal, i)->data; totalsize += sss_krb5_component_length_quoted(krb5_princ_component(context, principal, i), flags); totalsize++; } if (nelem == 0) totalsize++; *name = malloc(totalsize); if (!*name) { ret = ENOMEM; goto cleanup; } q = *name; for (i = 0; i < (int) nelem; i++) { cp = krb5_princ_component(context, principal, i)->data; length = krb5_princ_component(context, principal, i)->length; q += sss_krb5_copy_component_quoting(q, krb5_princ_component(context, principal, i), flags); *q++ = COMPONENT_SEP; } if (i > 0) q--; if ((flags & KRB5_PRINCIPAL_UNPARSE_NO_REALM) == 0) { *q++ = REALM_SEP; q += sss_krb5_copy_component_quoting(q, krb5_princ_realm(context, principal), flags); } *q++ = '\0'; cleanup: free(default_realm); return ret; #endif /* HAVE_KRB5_UNPARSE_NAME_FLAGS */ }
/* does the ccache have a valid TGT? */ static time_t get_tgt_time(const char *ccname) { krb5_context context; krb5_ccache ccache; krb5_cc_cursor cur; krb5_creds creds; krb5_principal principal; time_t credtime = 0; char *realm = NULL; if (krb5_init_context(&context)) { syslog(LOG_DEBUG, "%s: unable to init krb5 context", __func__); return 0; } if (krb5_cc_resolve(context, ccname, &ccache)) { syslog(LOG_DEBUG, "%s: unable to resolve krb5 cache", __func__); goto err_cache; } if (krb5_cc_set_flags(context, ccache, 0)) { syslog(LOG_DEBUG, "%s: unable to set flags", __func__); goto err_cache; } if (krb5_cc_get_principal(context, ccache, &principal)) { syslog(LOG_DEBUG, "%s: unable to get principal", __func__); goto err_princ; } if (krb5_cc_start_seq_get(context, ccache, &cur)) { syslog(LOG_DEBUG, "%s: unable to seq start", __func__); goto err_ccstart; } if ((realm = cifs_krb5_principal_get_realm(context, principal)) == NULL) { syslog(LOG_DEBUG, "%s: unable to get realm", __func__); goto err_ccstart; } while (!credtime && !krb5_cc_next_cred(context, ccache, &cur, &creds)) { char *name; if (krb5_unparse_name(context, creds.server, &name)) { syslog(LOG_DEBUG, "%s: unable to unparse name", __func__); goto err_endseq; } if (krb5_realm_compare(context, creds.server, principal) && !strncasecmp(name, KRB5_TGS_NAME, KRB5_TGS_NAME_SIZE) && !strncasecmp(name + KRB5_TGS_NAME_SIZE + 1, realm, strlen(realm)) && creds.times.endtime > time(NULL)) credtime = creds.times.endtime; krb5_free_cred_contents(context, &creds); krb5_free_unparsed_name(context, name); } err_endseq: krb5_cc_end_seq_get(context, ccache, &cur); err_ccstart: krb5_free_principal(context, principal); err_princ: #if defined(KRB5_TC_OPENCLOSE) krb5_cc_set_flags(context, ccache, KRB5_TC_OPENCLOSE); #endif krb5_cc_close(context, ccache); err_cache: krb5_free_context(context); return credtime; }
static krb5_error_code k5_unparse_name(krb5_context context, krb5_const_principal principal, int flags, char **name, unsigned int *size) { char *cp, *q; int i; int length; krb5_int32 nelem; unsigned int totalsize = 0; char *default_realm = NULL; krb5_error_code ret = 0; if (!principal || !name) return KRB5_PARSE_MALFORMED; if (flags & KRB5_PRINCIPAL_UNPARSE_SHORT) { /* omit realm if local realm */ krb5_principal_data p; ret = krb5_get_default_realm(context, &default_realm); if (ret != 0) goto cleanup; krb5_princ_realm(context, &p)->length = strlen(default_realm); krb5_princ_realm(context, &p)->data = default_realm; if (krb5_realm_compare(context, &p, principal)) flags |= KRB5_PRINCIPAL_UNPARSE_NO_REALM; } if ((flags & KRB5_PRINCIPAL_UNPARSE_NO_REALM) == 0) { totalsize += component_length_quoted(krb5_princ_realm(context, principal), flags); totalsize++; /* This is for the separator */ } nelem = krb5_princ_size(context, principal); for (i = 0; i < (int) nelem; i++) { cp = krb5_princ_component(context, principal, i)->data; totalsize += component_length_quoted(krb5_princ_component(context, principal, i), flags); totalsize++; /* This is for the separator */ } if (nelem == 0) totalsize++; /* * Allocate space for the ascii string; if space has been * provided, use it, realloc'ing it if necessary. * * We need only n-1 seperators for n components, but we need * an extra byte for the NUL at the end. */ if (size) { if (*name && (*size < totalsize)) { *name = realloc(*name, totalsize); } else { *name = malloc(totalsize); } *size = totalsize; } else { *name = malloc(totalsize); } if (!*name) { ret = ENOMEM; goto cleanup; } q = *name; for (i = 0; i < (int) nelem; i++) { cp = krb5_princ_component(context, principal, i)->data; length = krb5_princ_component(context, principal, i)->length; q += copy_component_quoting(q, krb5_princ_component(context, principal, i), flags); *q++ = COMPONENT_SEP; } if (i > 0) q--; /* Back up last component separator */ if ((flags & KRB5_PRINCIPAL_UNPARSE_NO_REALM) == 0) { *q++ = REALM_SEP; q += copy_component_quoting(q, krb5_princ_realm(context, principal), flags); } *q++ = '\0'; cleanup: if (default_realm != NULL) krb5_free_default_realm(context, default_realm); return ret; }
/* * Convert 'identifier' into canonical form. * Returns a pointer to a static buffer containing the canonical form * or NULL if 'identifier' is invalid. */ static const char *mycanonifyid(const char *identifier, size_t len) { static char *retbuf = NULL; krb5_context context; krb5_principal princ, princ_dummy; char *realm; int striprealm = 0; if(retbuf) free(retbuf); retbuf = NULL; if(!identifier) return NULL; if(!len) len = strlen(identifier); if (strcasecmp(identifier, "anonymous") == 0) return "anonymous"; if (strcasecmp(identifier, "anyone") == 0) return "anyone"; if (krb5_init_context(&context)) return NULL; if (krb5_parse_name(context,identifier,&princ)) { krb5_free_context(context); return NULL; } /* get local realm */ if (krb5_get_default_realm(context,&realm)) { krb5_free_principal(context,princ); krb5_free_context(context); return NULL; } /* build dummy princ to compare realms */ if (krb5_build_principal(context,&princ_dummy, strlen(realm),realm,"dummy",NULL)) { krb5_free_principal(context,princ); krb5_free_context(context); free(realm); return NULL; } /* is this principal local ? */ if (krb5_realm_compare(context,princ,princ_dummy)) { striprealm = 1; } /* done w/ dummy princ free it & realm */ krb5_free_principal(context,princ_dummy); free(realm); /* get the text version of princ */ if (krb5_unparse_name(context,princ,&retbuf)) { krb5_free_principal(context,princ); krb5_free_context(context); return NULL; } /* we have the canonical name pointed to by p -- strip realm if local */ if (striprealm) { char *realmbegin = strrchr(retbuf, '@'); if(realmbegin) *realmbegin = '\0'; } krb5_free_principal(context,princ); krb5_free_context(context); return retbuf; }