/* * If principal matches the given realm and service name, * and has *any* instance (hostname), return 1. * Otherwise return 0, indicating no match. */ static int realm_and_service_match(krb5_context context, krb5_principal p, const char *realm, const char *service) { #ifdef HAVE_KRB5 /* Must have two components */ if (p->length != 2) return 0; if ((strlen(realm) == p->realm.length) && (strncmp(realm, p->realm.data, p->realm.length) == 0) && (strlen(service) == p->data[0].length) && (strncmp(service, p->data[0].data, p->data[0].length) == 0)) return 1; #else const char *name, *inst; if (p->name.name_string.len != 2) return 0; name = krb5_principal_get_comp_string(context, p, 0); inst = krb5_principal_get_comp_string(context, p, 1); if (name == NULL || inst == NULL) return 0; if ((strcmp(realm, p->realm) == 0) && (strcmp(service, name) == 0)) return 1; #endif return 0; }
static krb5_boolean match_local_principals(krb5_context context, krb5_principal principal, const char *luser) { krb5_error_code ret; krb5_realm *realms, *r; krb5_boolean result = FALSE; /* multi-component principals can never match */ if(krb5_principal_get_comp_string(context, principal, 1) != NULL) return FALSE; ret = krb5_get_default_realms (context, &realms); if (ret) return FALSE; for (r = realms; *r != NULL; ++r) { if(strcmp(krb5_principal_get_realm(context, principal), *r) != 0) continue; if(strcmp(krb5_principal_get_comp_string(context, principal, 0), luser) == 0) { result = TRUE; break; } } krb5_free_host_realm (context, realms); return result; }
krb5_error_code KRB5_LIB_FUNCTION krb5_fwd_tgt_creds (krb5_context context, krb5_auth_context auth_context, const char *hostname, krb5_principal client, krb5_principal server, krb5_ccache ccache, int forwardable, krb5_data *out_data) { krb5_flags flags = 0; krb5_creds creds; krb5_error_code ret; krb5_const_realm client_realm; flags |= KDC_OPT_FORWARDED; if (forwardable) flags |= KDC_OPT_FORWARDABLE; if (hostname == NULL && krb5_principal_get_type(context, server) == KRB5_NT_SRV_HST) { const char *inst = krb5_principal_get_comp_string(context, server, 0); const char *host = krb5_principal_get_comp_string(context, server, 1); if (inst != NULL && strcmp(inst, "host") == 0 && host != NULL && krb5_principal_get_comp_string(context, server, 2) == NULL) hostname = host; } client_realm = krb5_principal_get_realm(context, client); memset (&creds, 0, sizeof(creds)); creds.client = client; ret = krb5_build_principal(context, &creds.server, strlen(client_realm), client_realm, KRB5_TGS_NAME, client_realm, NULL); if (ret) return ret; ret = krb5_get_forwarded_creds (context, auth_context, ccache, flags, hostname, &creds, out_data); return ret; }
int princ_ncomp_eq(krb5_context context, krb5_principal princ, int val) { const char *s; if (val <=0) return 0; if (!(s=krb5_principal_get_comp_string(context, princ, val-1)) || (strlen(s) == 0)) return 0; if ((s=krb5_principal_get_comp_string(context, princ, val)) && (strlen(s) > 0)) return 0; return 1; }
static krb5_error_code check_an2ln(krb5_context context, krb5_const_principal principal, const char *luser, krb5_boolean *result) { krb5_error_code ret; char *lname; #if 0 /* XXX Should we make this an option? */ /* multi-component principals can never match */ if (krb5_principal_get_comp_string(context, principal, 1) != NULL) { *result = FALSE; return 0; } #endif lname = malloc(strlen(luser) + 1); if (lname == NULL) return krb5_enomem(context); ret = krb5_aname_to_localname(context, principal, strlen(luser)+1, lname); if (ret) goto out; if (strcmp(lname, luser) == 0) *result = TRUE; else *result = FALSE; out: free(lname); return 0; }
const krb5_data *krb5_princ_component(krb5_context context, krb5_principal principal, int i ) { static krb5_data kdata; kdata.data = (char *)krb5_principal_get_comp_string(context, principal, i); kdata.length = strlen((const char *)kdata.data); return &kdata; }
static int realm_and_service_match(krb5_context context, krb5_principal p, const char *realm, const char *service) { const char *name, *inst; if (p->name.name_string.len != 2) return 0; name = krb5_principal_get_comp_string(context, p, 0); inst = krb5_principal_get_comp_string(context, p, 1); if (name == NULL || inst == NULL) return 0; if ((strcmp(realm, p->realm) == 0) && (strcmp(service, name) == 0)) return 1; return 0; }
/* * Given the krb5_principal from kadmind, convert it to the corresponding * principal in Active Directory. This may involve removing ad_base_instance * and always involves changing the realm. Returns a Kerberos error code. */ static krb5_error_code get_ad_principal(kadm5_hook_modinfo *config, krb5_context ctx, krb5_const_principal principal, krb5_principal *ad_principal) { krb5_error_code code; int ncomp; /* * Set ad_principal to NULL to start. We fall back on copy and realm * setting if we don't have to build it, and use whether it's NULL as a * flag. */ *ad_principal = NULL; /* Get the number of components. */ ncomp = krb5_principal_get_num_comp(ctx, principal); /* See if this is an ad_base_instance principal that needs a rewrite. */ if (config->ad_base_instance != NULL && ncomp == 2) { const char *base, *instance; instance = krb5_principal_get_comp_string(ctx, principal, 1); if (strcmp(instance, config->ad_base_instance) == 0) { base = krb5_principal_get_comp_string(ctx, principal, 0); code = krb5_build_principal(ctx, ad_principal, strlen(config->ad_realm), config->ad_realm, base, (char *) 0); if (code != 0) return code; } } /* Otherwise, copy the principal and set the realm. */ if (*ad_principal == NULL) { code = krb5_copy_principal(ctx, principal, ad_principal); if (code != 0) return code; krb5_principal_set_realm(ctx, *ad_principal, config->ad_realm); } return 0; }
OM_uint32 _gsskrb5_canon_name(OM_uint32 *minor_status, krb5_context context, gss_const_name_t targetname, krb5_principal *out) { krb5_const_principal p = (krb5_const_principal)targetname; krb5_error_code ret; char *hostname = NULL, *service; int type; const char *comp; *minor_status = 0; /* If its not a hostname */ type = krb5_principal_get_type(context, p); comp = krb5_principal_get_comp_string(context, p, 0); if (type == KRB5_NT_SRV_HST || type == KRB5_NT_SRV_HST_NEEDS_CANON || (type == KRB5_NT_UNKNOWN && comp != NULL && strcmp(comp, "host") == 0)) { if (p->name.name_string.len == 0) return GSS_S_BAD_NAME; else if (p->name.name_string.len > 1) hostname = p->name.name_string.val[1]; service = p->name.name_string.val[0]; ret = krb5_sname_to_principal(context, hostname, service, KRB5_NT_SRV_HST, out); } else { ret = krb5_copy_principal(context, p, out); } if (ret) { *minor_status = ret; return GSS_S_FAILURE; } return 0; }
static krb5_error_code tgs_build_reply(krb5_context context, krb5_kdc_configuration *config, KDC_REQ *req, KDC_REQ_BODY *b, hdb_entry_ex *krbtgt, krb5_enctype krbtgt_etype, krb5_ticket *ticket, krb5_data *reply, const char *from, const char **e_text, AuthorizationData *auth_data, const struct sockaddr *from_addr, int datagram_reply) { krb5_error_code ret; krb5_principal cp = NULL, sp = NULL; krb5_principal client_principal = NULL; char *spn = NULL, *cpn = NULL; hdb_entry_ex *server = NULL, *client = NULL; EncTicketPart *tgt = &ticket->ticket; KRB5SignedPathPrincipals *spp = NULL; const EncryptionKey *ekey; krb5_keyblock sessionkey; krb5_kvno kvno; krb5_data rspac; int cross_realm = 0; PrincipalName *s; Realm r; int nloop = 0; EncTicketPart adtkt; char opt_str[128]; int require_signedpath = 0; memset(&sessionkey, 0, sizeof(sessionkey)); memset(&adtkt, 0, sizeof(adtkt)); krb5_data_zero(&rspac); s = b->sname; r = b->realm; if(b->kdc_options.enc_tkt_in_skey){ Ticket *t; hdb_entry_ex *uu; krb5_principal p; Key *uukey; if(b->additional_tickets == NULL || b->additional_tickets->len == 0){ ret = KRB5KDC_ERR_BADOPTION; /* ? */ kdc_log(context, config, 0, "No second ticket present in request"); goto out; } t = &b->additional_tickets->val[0]; if(!get_krbtgt_realm(&t->sname)){ kdc_log(context, config, 0, "Additional ticket is not a ticket-granting ticket"); ret = KRB5KDC_ERR_POLICY; goto out; } _krb5_principalname2krb5_principal(context, &p, t->sname, t->realm); ret = _kdc_db_fetch(context, config, p, HDB_F_GET_CLIENT|HDB_F_GET_SERVER, NULL, &uu); krb5_free_principal(context, p); if(ret){ if (ret == HDB_ERR_NOENTRY) ret = KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN; goto out; } ret = hdb_enctype2key(context, &uu->entry, t->enc_part.etype, &uukey); if(ret){ _kdc_free_ent(context, uu); ret = KRB5KDC_ERR_ETYPE_NOSUPP; /* XXX */ goto out; } ret = krb5_decrypt_ticket(context, t, &uukey->key, &adtkt, 0); _kdc_free_ent(context, uu); if(ret) goto out; ret = verify_flags(context, config, &adtkt, spn); if (ret) goto out; s = &adtkt.cname; r = adtkt.crealm; } _krb5_principalname2krb5_principal(context, &sp, *s, r); ret = krb5_unparse_name(context, sp, &spn); if (ret) goto out; _krb5_principalname2krb5_principal(context, &cp, tgt->cname, tgt->crealm); ret = krb5_unparse_name(context, cp, &cpn); if (ret) goto out; unparse_flags (KDCOptions2int(b->kdc_options), asn1_KDCOptions_units(), opt_str, sizeof(opt_str)); if(*opt_str) kdc_log(context, config, 0, "TGS-REQ %s from %s for %s [%s]", cpn, from, spn, opt_str); else kdc_log(context, config, 0, "TGS-REQ %s from %s for %s", cpn, from, spn); /* * Fetch server */ server_lookup: ret = _kdc_db_fetch(context, config, sp, HDB_F_GET_SERVER, NULL, &server); if(ret){ const char *new_rlm; Realm req_rlm; krb5_realm *realms; if ((req_rlm = get_krbtgt_realm(&sp->name)) != NULL) { if(nloop++ < 2) { new_rlm = find_rpath(context, tgt->crealm, req_rlm); if(new_rlm) { kdc_log(context, config, 5, "krbtgt for realm %s " "not found, trying %s", req_rlm, new_rlm); krb5_free_principal(context, sp); free(spn); krb5_make_principal(context, &sp, r, KRB5_TGS_NAME, new_rlm, NULL); ret = krb5_unparse_name(context, sp, &spn); if (ret) goto out; goto server_lookup; } } } else if(need_referral(context, sp, &realms)) { if (strcmp(realms[0], sp->realm) != 0) { kdc_log(context, config, 5, "Returning a referral to realm %s for " "server %s that was not found", realms[0], spn); krb5_free_principal(context, sp); free(spn); krb5_make_principal(context, &sp, r, KRB5_TGS_NAME, realms[0], NULL); ret = krb5_unparse_name(context, sp, &spn); if (ret) goto out; krb5_free_host_realm(context, realms); goto server_lookup; } krb5_free_host_realm(context, realms); } kdc_log(context, config, 0, "Server not found in database: %s: %s", spn, krb5_get_err_text(context, ret)); if (ret == HDB_ERR_NOENTRY) ret = KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN; goto out; } ret = _kdc_db_fetch(context, config, cp, HDB_F_GET_CLIENT, NULL, &client); if(ret) { const char *krbtgt_realm; /* * If the client belongs to the same realm as our krbtgt, it * should exist in the local database. * */ krbtgt_realm = krb5_principal_get_comp_string(context, krbtgt->entry.principal, 1); if(strcmp(krb5_principal_get_realm(context, cp), krbtgt_realm) == 0) { if (ret == HDB_ERR_NOENTRY) ret = KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN; kdc_log(context, config, 1, "Client no longer in database: %s", cpn); goto out; } kdc_log(context, config, 1, "Client not found in database: %s: %s", cpn, krb5_get_err_text(context, ret)); cross_realm = 1; } /* * Check that service is in the same realm as the krbtgt. If its * not the same, its someone that is using a uni-directional trust * backward. */ if (strcmp(krb5_principal_get_realm(context, sp), krb5_principal_get_comp_string(context, krbtgt->entry.principal, 1)) != 0) { char *tpn; ret = krb5_unparse_name(context, krbtgt->entry.principal, &tpn); kdc_log(context, config, 0, "Request with wrong krbtgt: %s", (ret == 0) ? tpn : "<unknown>"); if(ret == 0) free(tpn); ret = KRB5KRB_AP_ERR_NOT_US; goto out; } /* * */ client_principal = cp; if (client) { const PA_DATA *sdata; int i = 0; sdata = _kdc_find_padata(req, &i, KRB5_PADATA_S4U2SELF); if (sdata) { krb5_crypto crypto; krb5_data datack; PA_S4U2Self self; char *selfcpn = NULL; const char *str; ret = decode_PA_S4U2Self(sdata->padata_value.data, sdata->padata_value.length, &self, NULL); if (ret) { kdc_log(context, config, 0, "Failed to decode PA-S4U2Self"); goto out; } ret = _krb5_s4u2self_to_checksumdata(context, &self, &datack); if (ret) goto out; ret = krb5_crypto_init(context, &tgt->key, 0, &crypto); if (ret) { free_PA_S4U2Self(&self); krb5_data_free(&datack); kdc_log(context, config, 0, "krb5_crypto_init failed: %s", krb5_get_err_text(context, ret)); goto out; } ret = krb5_verify_checksum(context, crypto, KRB5_KU_OTHER_CKSUM, datack.data, datack.length, &self.cksum); krb5_data_free(&datack); krb5_crypto_destroy(context, crypto); if (ret) { free_PA_S4U2Self(&self); kdc_log(context, config, 0, "krb5_verify_checksum failed for S4U2Self: %s", krb5_get_err_text(context, ret)); goto out; } ret = _krb5_principalname2krb5_principal(context, &client_principal, self.name, self.realm); free_PA_S4U2Self(&self); if (ret) goto out; ret = krb5_unparse_name(context, client_principal, &selfcpn); if (ret) goto out; /* * Check that service doing the impersonating is * requesting a ticket to it-self. */ if (krb5_principal_compare(context, cp, sp) != TRUE) { kdc_log(context, config, 0, "S4U2Self: %s is not allowed " "to impersonate some other user " "(tried for user %s to service %s)", cpn, selfcpn, spn); free(selfcpn); ret = KRB5KDC_ERR_BADOPTION; /* ? */ goto out; } /* * If the service isn't trusted for authentication to * delegation, remove the forward flag. */ if (client->entry.flags.trusted_for_delegation) { str = "[forwardable]"; } else { b->kdc_options.forwardable = 0; str = ""; } kdc_log(context, config, 0, "s4u2self %s impersonating %s to " "service %s %s", cpn, selfcpn, spn, str); free(selfcpn); } } /* * Constrained delegation */ if (client != NULL && b->additional_tickets != NULL && b->additional_tickets->len != 0 && b->kdc_options.enc_tkt_in_skey == 0) { Key *clientkey; Ticket *t; char *str; t = &b->additional_tickets->val[0]; ret = hdb_enctype2key(context, &client->entry, t->enc_part.etype, &clientkey); if(ret){ ret = KRB5KDC_ERR_ETYPE_NOSUPP; /* XXX */ goto out; } ret = krb5_decrypt_ticket(context, t, &clientkey->key, &adtkt, 0); if (ret) { kdc_log(context, config, 0, "failed to decrypt ticket for " "constrained delegation from %s to %s ", spn, cpn); goto out; } /* check that ticket is valid */ if (adtkt.flags.forwardable == 0) { kdc_log(context, config, 0, "Missing forwardable flag on ticket for " "constrained delegation from %s to %s ", spn, cpn); ret = KRB5KDC_ERR_ETYPE_NOSUPP; /* XXX */ goto out; } ret = check_constrained_delegation(context, config, client, sp); if (ret) { kdc_log(context, config, 0, "constrained delegation from %s to %s not allowed", spn, cpn); goto out; } ret = _krb5_principalname2krb5_principal(context, &client_principal, adtkt.cname, adtkt.crealm); if (ret) goto out; ret = krb5_unparse_name(context, client_principal, &str); if (ret) goto out; ret = verify_flags(context, config, &adtkt, str); if (ret) { free(str); goto out; } /* * Check KRB5SignedPath in authorization data and add new entry to * make sure servers can't fake a ticket to us. */ ret = check_KRB5SignedPath(context, config, krbtgt, &adtkt, &spp, 1); if (ret) { kdc_log(context, config, 0, "KRB5SignedPath check from service %s failed " "for delegation to %s for client %s " "from %s failed with %s", spn, str, cpn, from, krb5_get_err_text(context, ret)); free(str); goto out; } kdc_log(context, config, 0, "constrained delegation for %s " "from %s to %s", str, cpn, spn); free(str); /* * Also require that the KDC have issue the service's krbtgt * used to do the request. */ require_signedpath = 1; } /* * Check flags */ ret = _kdc_check_flags(context, config, client, cpn, server, spn, FALSE); if(ret) goto out; if((b->kdc_options.validate || b->kdc_options.renew) && !krb5_principal_compare(context, krbtgt->entry.principal, server->entry.principal)){ kdc_log(context, config, 0, "Inconsistent request."); ret = KRB5KDC_ERR_SERVER_NOMATCH; goto out; } /* check for valid set of addresses */ if(!_kdc_check_addresses(context, config, tgt->caddr, from_addr)) { ret = KRB5KRB_AP_ERR_BADADDR; kdc_log(context, config, 0, "Request from wrong address"); goto out; } /* * Select enctype, return key and kvno. */ { krb5_enctype etype; if(b->kdc_options.enc_tkt_in_skey) { int i; ekey = &adtkt.key; for(i = 0; i < b->etype.len; i++) if (b->etype.val[i] == adtkt.key.keytype) break; if(i == b->etype.len) { krb5_clear_error_string(context); return KRB5KDC_ERR_ETYPE_NOSUPP; } etype = b->etype.val[i]; kvno = 0; } else { Key *skey; ret = _kdc_find_etype(context, server, b->etype.val, b->etype.len, &skey, &etype); if(ret) { kdc_log(context, config, 0, "Server (%s) has no support for etypes", spp); return ret; } ekey = &skey->key; kvno = server->entry.kvno; } ret = krb5_generate_random_keyblock(context, etype, &sessionkey); if (ret) goto out; } /* check PAC if not cross realm and if there is one */ if (!cross_realm) { Key *tkey; ret = hdb_enctype2key(context, &krbtgt->entry, krbtgt_etype, &tkey); if(ret) { kdc_log(context, config, 0, "Failed to find key for krbtgt PAC check"); goto out; } ret = check_PAC(context, config, client_principal, client, server, ekey, &tkey->key, tgt, &rspac, &require_signedpath); if (ret) { kdc_log(context, config, 0, "Verify PAC failed for %s (%s) from %s with %s", spn, cpn, from, krb5_get_err_text(context, ret)); goto out; } } /* also check the krbtgt for signature */ ret = check_KRB5SignedPath(context, config, krbtgt, tgt, &spp, require_signedpath); if (ret) { kdc_log(context, config, 0, "KRB5SignedPath check failed for %s (%s) from %s with %s", spn, cpn, from, krb5_get_err_text(context, ret)); goto out; } /* * */ ret = tgs_make_reply(context, config, b, client_principal, tgt, ekey, &sessionkey, kvno, auth_data, server, spn, client, cp, krbtgt, krbtgt_etype, spp, &rspac, e_text, reply); out: free(spn); free(cpn); krb5_data_free(&rspac); krb5_free_keyblock_contents(context, &sessionkey); if(server) _kdc_free_ent(context, server); if(client) _kdc_free_ent(context, client); if (client_principal && client_principal != cp) krb5_free_principal(context, client_principal); if (cp) krb5_free_principal(context, cp); if (sp) krb5_free_principal(context, sp); free_EncTicketPart(&adtkt); return ret; }
static krb5_error_code get_cache_principal(krb5_context context, krb5_ccache *id, krb5_principal *client) { krb5_error_code ret; const char *name, *inst; krb5_principal p1, p2; ret = krb5_cc_default(context, id); if(ret) { *id = NULL; return ret; } ret = krb5_cc_get_principal(context, *id, &p1); if(ret) { krb5_cc_close(context, *id); *id = NULL; return ret; } ret = krb5_make_principal(context, &p2, NULL, "kadmin", "admin", NULL); if (ret) { krb5_cc_close(context, *id); *id = NULL; krb5_free_principal(context, p1); return ret; } { krb5_creds in, *out; krb5_kdc_flags flags; flags.i = 0; memset(&in, 0, sizeof(in)); in.client = p1; in.server = p2; /* check for initial ticket kadmin/admin */ ret = krb5_get_credentials_with_flags(context, KRB5_GC_CACHED, flags, *id, &in, &out); krb5_free_principal(context, p2); if (ret == 0) { if (out->flags.b.initial) { *client = p1; krb5_free_creds(context, out); return 0; } krb5_free_creds(context, out); } } krb5_cc_close(context, *id); *id = NULL; name = krb5_principal_get_comp_string(context, p1, 0); inst = krb5_principal_get_comp_string(context, p1, 1); if(inst == NULL || strcmp(inst, "admin") != 0) { ret = krb5_make_principal(context, &p2, NULL, name, "admin", NULL); krb5_free_principal(context, p1); if(ret != 0) return ret; *client = p2; return 0; } *client = p1; return 0; }
int main(int argc, char **argv) { krb5_error_code ret; krb5_context context; krb5_ccache cache; krb5_creds *out; int optidx = 0; int32_t nametype = KRB5_NT_UNKNOWN; krb5_get_creds_opt opt; krb5_principal server = NULL; krb5_principal impersonate; setprogname(argv[0]); ret = krb5_init_context(&context); if (ret) errx(1, "krb5_init_context failed: %d", ret); if (getarg(args, sizeof(args) / sizeof(args[0]), argc, argv, &optidx)) usage(1); if (help_flag) usage (0); if (version_flag) { print_version(NULL); exit(0); } argc -= optidx; argv += optidx; if (debug_flag) { ret = krb5_set_debug_dest(context, getprogname(), "STDERR"); if (ret) krb5_warn(context, ret, "krb5_set_debug_dest"); } if (cache_str) { ret = krb5_cc_resolve(context, cache_str, &cache); if (ret) krb5_err(context, 1, ret, "%s", cache_str); } else { ret = krb5_cc_default (context, &cache); if (ret) krb5_err(context, 1, ret, "krb5_cc_resolve"); } ret = krb5_get_creds_opt_alloc(context, &opt); if (ret) krb5_err(context, 1, ret, "krb5_get_creds_opt_alloc"); if (etype_str) { krb5_enctype enctype; ret = krb5_string_to_enctype(context, etype_str, &enctype); if (ret) krb5_errx(context, 1, N_("unrecognized enctype: %s", ""), etype_str); krb5_get_creds_opt_set_enctype(context, opt, enctype); } if (impersonate_str) { ret = krb5_parse_name(context, impersonate_str, &impersonate); if (ret) krb5_err(context, 1, ret, "krb5_parse_name %s", impersonate_str); krb5_get_creds_opt_set_impersonate(context, opt, impersonate); krb5_get_creds_opt_add_options(context, opt, KRB5_GC_NO_STORE); krb5_free_principal(context, impersonate); } if (out_cache_str) krb5_get_creds_opt_add_options(context, opt, KRB5_GC_NO_STORE); if (forwardable_flag) krb5_get_creds_opt_add_options(context, opt, KRB5_GC_FORWARDABLE); if (!transit_flag) krb5_get_creds_opt_add_options(context, opt, KRB5_GC_NO_TRANSIT_CHECK); if (canonicalize_flag) krb5_get_creds_opt_add_options(context, opt, KRB5_GC_CANONICALIZE); if (!store_flag) krb5_get_creds_opt_add_options(context, opt, KRB5_GC_NO_STORE); if (cached_only_flag) krb5_get_creds_opt_add_options(context, opt, KRB5_GC_CACHED); if (delegation_cred_str) { krb5_ccache id; krb5_creds c, mc; Ticket ticket; krb5_cc_clear_mcred(&mc); ret = krb5_cc_get_principal(context, cache, &mc.server); if (ret) krb5_err(context, 1, ret, "krb5_cc_get_principal"); ret = krb5_cc_resolve(context, delegation_cred_str, &id); if(ret) krb5_err(context, 1, ret, "krb5_cc_resolve"); ret = krb5_cc_retrieve_cred(context, id, 0, &mc, &c); if(ret) krb5_err(context, 1, ret, "krb5_cc_retrieve_cred"); ret = decode_Ticket(c.ticket.data, c.ticket.length, &ticket, NULL); if (ret) { krb5_clear_error_message(context); krb5_err(context, 1, ret, "decode_Ticket"); } krb5_free_cred_contents(context, &c); ret = krb5_get_creds_opt_set_ticket(context, opt, &ticket); if(ret) krb5_err(context, 1, ret, "krb5_get_creds_opt_set_ticket"); free_Ticket(&ticket); krb5_cc_close(context, id); krb5_free_principal(context, mc.server); krb5_get_creds_opt_add_options(context, opt, KRB5_GC_CONSTRAINED_DELEGATION); } if (nametype_str != NULL) { ret = krb5_parse_nametype(context, nametype_str, &nametype); if (ret) krb5_err(context, 1, ret, "krb5_parse_nametype"); } if (nametype == KRB5_NT_SRV_HST || nametype == KRB5_NT_SRV_HST_NEEDS_CANON) is_hostbased_flag = 1; if (is_hostbased_flag) { const char *sname = NULL; const char *hname = NULL; if (nametype_str != NULL && nametype != KRB5_NT_SRV_HST && nametype != KRB5_NT_SRV_HST_NEEDS_CANON) krb5_errx(context, 1, "--hostbased not compatible with " "non-hostbased --name-type"); if (is_canonical_flag) nametype = KRB5_NT_SRV_HST; else nametype = KRB5_NT_SRV_HST_NEEDS_CANON; /* * Host-based service names can have more than one component. * * RFC5179 did not, but should have, assign a Kerberos name-type * corresponding to GSS_C_NT_DOMAINBASED. But it's basically a * host-based service name type with one additional component. * * So that's how we're treating host-based service names here: * two or more components. */ if (argc == 0) { usage(1); } else if (argc == 1) { krb5_principal server2; /* * In this case the one argument is a principal name, not the * service name. * * We parse the argument as a principal name, extract the service * and hostname components, use krb5_sname_to_principal(), then * extract the service and hostname components from that. */ ret = krb5_parse_name(context, argv[0], &server); if (ret) krb5_err(context, 1, ret, "krb5_parse_name %s", argv[0]); sname = krb5_principal_get_comp_string(context, server, 0); /* * If a single-component principal name is given, then we'll * default the hostname, as krb5_principal_get_comp_string() * returns NULL in this case. */ hname = krb5_principal_get_comp_string(context, server, 1); ret = krb5_sname_to_principal(context, hname, sname, KRB5_NT_SRV_HST, &server2); sname = krb5_principal_get_comp_string(context, server2, 0); hname = krb5_principal_get_comp_string(context, server2, 1); /* * Modify the original with the new sname/hname. This way we * retain any additional principal name components from the given * principal name. * * The name-type is set further below. */ ret = krb5_principal_set_comp_string(context, server, 0, sname); if (ret) krb5_err(context, 1, ret, "krb5_principal_set_comp_string %s", argv[0]); ret = krb5_principal_set_comp_string(context, server, 1, hname); if (ret) krb5_err(context, 1, ret, "krb5_principal_set_comp_string %s", argv[0]); krb5_free_principal(context, server2); } else { size_t i; /* * In this case the arguments are principal name components. * * The service and hostname components can be defaulted by passing * empty strings. */ sname = argv[0]; if (*sname == '\0') sname = NULL; hname = argv[1]; if (hname == NULL || *hname == '\0') hname = NULL; ret = krb5_sname_to_principal(context, hname, sname, KRB5_NT_SRV_HST, &server); if (ret) krb5_err(context, 1, ret, "krb5_sname_to_principal"); for (i = 2; i < argc; i++) { ret = krb5_principal_set_comp_string(context, server, i, argv[i]); if (ret) krb5_err(context, 1, ret, "krb5_principal_set_comp_string"); } } } else if (argc == 1) { ret = krb5_parse_name(context, argv[0], &server); if (ret) krb5_err(context, 1, ret, "krb5_parse_name %s", argv[0]); } else { usage(1); } if (nametype != KRB5_NT_UNKNOWN) server->name.name_type = (NAME_TYPE)nametype; ret = krb5_get_creds(context, opt, cache, server, &out); if (ret) krb5_err(context, 1, ret, "krb5_get_creds"); if (out_cache_str) { krb5_ccache id; ret = krb5_cc_resolve(context, out_cache_str, &id); if(ret) krb5_err(context, 1, ret, "krb5_cc_resolve"); ret = krb5_cc_initialize(context, id, out->client); if(ret) krb5_err(context, 1, ret, "krb5_cc_initialize"); ret = krb5_cc_store_cred(context, id, out); if(ret) krb5_err(context, 1, ret, "krb5_cc_store_cred"); krb5_cc_close(context, id); } krb5_free_creds(context, out); krb5_free_principal(context, server); krb5_get_creds_opt_free(context, opt); krb5_cc_close (context, cache); krb5_free_context (context); return 0; }
/* * Log to a cell. If the cell has already been logged to, return without * doing anything. Otherwise, log to it and mark that it has been logged * to. */ static int auth_to_cell(krb5_context context, char *cell, char *realm) { int status = AKLOG_SUCCESS; char username[BUFSIZ]; /* To hold client username structure */ char name[ANAME_SZ]; /* Name of afs key */ char instance[INST_SZ]; /* Instance of afs key */ char realm_of_user[REALM_SZ]; /* Kerberos realm of user */ char realm_of_cell[REALM_SZ]; /* Kerberos realm of cell */ char local_cell[MAXCELLCHARS+1]; char cell_to_use[MAXCELLCHARS+1]; /* Cell to authenticate to */ krb5_creds *v5cred = NULL; #ifdef HAVE_KRB4 CREDENTIALS c; #endif struct ktc_principal aserver; struct ktc_principal aclient; struct ktc_token atoken, btoken; struct afsconf_cell ak_cellconfig; /* General information about the cell */ int i; int getLinkedCell = 0; int flags = 0; char * smbname = getenv("AFS_SMBNAME"); /* try to avoid an expensive call to get_cellconfig */ if (cell && ll_string_check(&authedcells, cell)) { if (dflag) printf("Already authenticated to %s (or tried to)\n", cell); return(AKLOG_SUCCESS); } memset(name, 0, sizeof(name)); memset(instance, 0, sizeof(instance)); memset(realm_of_user, 0, sizeof(realm_of_user)); memset(realm_of_cell, 0, sizeof(realm_of_cell)); memset(&ak_cellconfig, 0, sizeof(ak_cellconfig)); /* NULL or empty cell returns information on local cell */ if (status = get_cellconfig(cell, &ak_cellconfig, local_cell)) return(status); linkedCell: if (getLinkedCell) strncpy(cell_to_use, ak_cellconfig.linkedCell, MAXCELLCHARS); else strncpy(cell_to_use, ak_cellconfig.name, MAXCELLCHARS); cell_to_use[MAXCELLCHARS] = 0; if (ll_string_check(&authedcells, cell_to_use)) { if (dflag) printf("Already authenticated to %s (or tried to)\n", cell_to_use); status = AKLOG_SUCCESS; goto done2; } /* * Record that we have attempted to log to this cell. We do this * before we try rather than after so that we will not try * and fail repeatedly for one cell. */ (void)ll_add_string(&authedcells, cell_to_use); if (dflag) printf("Authenticating to cell %s.\n", cell_to_use); /* We use the afs.<cellname> convention here... */ strcpy(name, AFSKEY); strncpy(instance, cell_to_use, sizeof(instance)); instance[sizeof(instance)-1] = '\0'; /* * Extract the session key from the ticket file and hand-frob an * afs style authenticator. */ if (usev5) { /* using krb5 */ int retry = 1; int realm_fallback = 0; if ((status = get_v5_user_realm(context, realm_of_user)) != KSUCCESS) { char * msg; msg = krb5_get_error_message(context, status); fprintf(stderr, "%s: Couldn't determine realm of user: %s\n", progname, msg); krb5_free_error_message(context, msg); status = AKLOG_KERBEROS; goto done; } if ( strchr(name,'.') != NULL && !accept_dotted_usernames()) { fprintf(stderr, "%s: Can't support principal names including a dot.\n", progname); status = AKLOG_MISC; goto done; } try_v5: if (realm && realm[0]) { if (dflag) printf("Getting v5 tickets: %s/%s@%s\n", name, instance, realm); status = get_v5cred(context, name, instance, realm, #ifdef HAVE_KRB4 use524 ? &c : NULL, #else NULL, #endif &v5cred); strcpy(realm_of_cell, realm); } else { strcpy(realm_of_cell, afs_realm_of_cell5(context, &ak_cellconfig, realm_fallback)); if (retry == 1 && realm_fallback == 0) { /* Only try the realm_of_user once */ status = -1; if (dflag) printf("Getting v5 tickets: %s/%s@%s\n", name, instance, realm_of_user); status = get_v5cred(context, name, instance, realm_of_user, #ifdef HAVE_KRB4 use524 ? &c : NULL, #else NULL, #endif &v5cred); if (status == 0) { /* we have determined that the client realm * is a valid cell realm */ strcpy(realm_of_cell, realm_of_user); } } if (status != 0 && (!retry || retry && strcmp(realm_of_user,realm_of_cell))) { if (dflag) printf("Getting v5 tickets: %s/%s@%s\n", name, instance, realm_of_cell); status = get_v5cred(context, name, instance, realm_of_cell, #ifdef HAVE_KRB4 use524 ? &c : NULL, #else NULL, #endif &v5cred); if (!status && !strlen(realm_of_cell)) copy_realm_of_ticket(context, realm_of_cell, sizeof(realm_of_cell), v5cred); } } if (!realm_fallback && status == KRB5_ERR_HOST_REALM_UNKNOWN) { realm_fallback = 1; goto try_v5; } else if (status == KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN) { if (!realm_fallback && !realm_of_cell[0]) { realm_fallback = 1; goto try_v5; } if (dflag) printf("Getting v5 tickets: %s@%s\n", name, realm_of_cell); status = get_v5cred(context, name, "", realm_of_cell, #ifdef HAVE_KRB4 use524 ? &c : NULL, #else NULL, #endif &v5cred); if (!status && !strlen(realm_of_cell)) copy_realm_of_ticket(context, realm_of_cell, sizeof(realm_of_cell), v5cred); } if ( status == KRB5KRB_AP_ERR_MSG_TYPE && retry ) { retry = 0; realm_fallback = 0; goto try_v5; } } else { #ifdef HAVE_KRB4 if (realm && realm[0]) strcpy(realm_of_cell, realm); else strcpy(realm_of_cell, afs_realm_of_cell(&ak_cellconfig)); /* * Try to obtain AFS tickets. Because there are two valid service * names, we will try both, but trying the more specific first. * * afs.<cell>@<realm> * afs@<realm> */ if (dflag) printf("Getting tickets: %s.%s@%s\n", name, instance, realm_of_cell); status = get_cred(name, instance, realm_of_cell, &c); if (status == KDC_PR_UNKNOWN) { if (dflag) printf("Getting tickets: %s@%s\n", name, realm_of_cell); status = get_cred(name, "", realm_of_cell, &c); } #else status = AKLOG_MISC; goto done; #endif } if (status != KSUCCESS) { char * msg = NULL; if (dflag) printf("Kerberos error code returned by get_cred: %d\n", status); if (usev5) { msg = krb5_get_error_message(context, status); } #ifdef HAVE_KRB4 else msg = krb_err_text(status); #endif fprintf(stderr, "%s: Couldn't get %s AFS tickets: %s\n", progname, cell_to_use, msg?msg:"(unknown error)"); if (usev5) krb5_free_error_message(context, msg); status = AKLOG_KERBEROS; goto done; } strncpy(aserver.name, AFSKEY, MAXKTCNAMELEN - 1); strncpy(aserver.instance, AFSINST, MAXKTCNAMELEN - 1); strncpy(aserver.cell, cell_to_use, MAXKTCREALMLEN - 1); if (usev5 && !use524) { /* This code inserts the entire K5 ticket into the token * No need to perform a krb524 translation which is * commented out in the code below */ char * p; int len; const char *un; un = krb5_principal_get_comp_string(context, v5cred->client, 0); strncpy(username, un, MAXKTCNAMELEN - 1); username[MAXKTCNAMELEN - 1] = '\0'; if ( krb5_principal_get_num_comp(context, v5cred->client) > 1 ) { strcat(username, "."); p = username + strlen(username); len = (unsigned int)(MAXKTCNAMELEN - strlen(username) - 1); strncpy(p, krb5_principal_get_comp_string(context, v5cred->client, 1), len); p[len] = '\0'; } memset(&atoken, '\0', sizeof(atoken)); atoken.kvno = RXKAD_TKT_TYPE_KERBEROS_V5; atoken.startTime = v5cred->times.starttime; atoken.endTime = v5cred->times.endtime; if (tkt_DeriveDesKey(v5cred->session.keytype, v5cred->session.keyvalue.data, v5cred->session.keyvalue.length, &atoken.sessionKey)) { status = AKLOG_MISC; goto done; } atoken.ticketLen = v5cred->ticket.length; memcpy(atoken.ticket, v5cred->ticket.data, atoken.ticketLen); } else { #ifdef HAVE_KRB4 strcpy (username, c.pname); if (c.pinst[0]) { strcat(username, "."); strcat(username, c.pinst); } atoken.kvno = c.kvno; atoken.startTime = c.issue_date; /* ticket lifetime is in five-minutes blocks. */ atoken.endTime = c.issue_date + ((unsigned char)c.lifetime * 5 * 60); memcpy(&atoken.sessionKey, c.session, 8); atoken.ticketLen = c.ticket_st.length; memcpy(atoken.ticket, c.ticket_st.dat, atoken.ticketLen); #else status = AKLOG_MISC; goto done; #endif } if (!force && !smbname && !ktc_GetToken(&aserver, &btoken, sizeof(btoken), &aclient) && atoken.kvno == btoken.kvno && atoken.ticketLen == btoken.ticketLen && !memcmp(&atoken.sessionKey, &btoken.sessionKey, sizeof(atoken.sessionKey)) && !memcmp(atoken.ticket, btoken.ticket, atoken.ticketLen)) { if (dflag) printf("Identical tokens already exist; skipping.\n"); status = AKLOG_SUCCESS; goto done2; } if (noprdb) { if (dflag) printf("Not resolving name %s to id (-noprdb set)\n", username); } else { if (!usev5) { #ifdef HAVE_KRB4 if ((status = krb_get_tf_realm(TKT_FILE, realm_of_user)) != KSUCCESS) { fprintf(stderr, "%s: Couldn't determine realm of user: %s)", progname, krb_err_text(status)); status = AKLOG_KERBEROS; goto done; } #else status = AKLOG_MISC; goto done; #endif } /* For Network Identity Manager append the realm to the name */ strcat(username, "@"); strcat(username, realm_of_user); ViceIDToUsername(username, realm_of_user, realm_of_cell, cell_to_use, #ifdef HAVE_KRB4 &c, #else NULL, #endif &status, &aclient, &aserver, &atoken); } if (dflag) printf("Set username to %s\n", username); /* Reset the "aclient" structure before we call ktc_SetToken. * This structure was first set by the ktc_GetToken call when * we were comparing whether identical tokens already existed. */ strncpy(aclient.name, username, MAXKTCNAMELEN - 1); strcpy(aclient.instance, ""); if (usev5 && !use524) { strncpy(aclient.cell, krb5_principal_get_realm(context, v5cred->client), MAXKTCNAMELEN - 1); aclient.cell[MAXKTCNAMELEN - 1] = '\0'; } #ifdef HAVE_KRB4 else strncpy(aclient.cell, c.realm, MAXKTCREALMLEN - 1); #endif for ( i=0; aclient.cell[i]; i++ ) { if ( islower(aclient.cell[i]) ) aclient.cell[i] = toupper(aclient.cell[i]); } if ( smbname ) { if (dflag) printf("Setting tokens for %s.\n", smbname); strncpy(aclient.smbname, smbname, MAXKTCNAMELEN - 1); aclient.smbname[MAXKTCNAMELEN - 1] = '\0'; flags = AFS_SETTOK_LOGON; } else { if (dflag) printf("Setting tokens.\n"); } if (status = ktc_SetToken(&aserver, &atoken, &aclient, flags)) { afs_com_err(progname, status, "while setting token for cell %s\n", cell_to_use); status = AKLOG_TOKEN; } done2: if (ak_cellconfig.linkedCell && !getLinkedCell) { getLinkedCell = 1; goto linkedCell; } done: #if 0 /* * intentionally leak the linkedCell field because it was allocated * using a different C RTL version. */ if (ak_cellconfig.linkedCell) free(ak_cellconfig.linkedCell); #endif return(status); }
krb5_error_code _kadm5_c_get_cred_cache(krb5_context context, const char *client_name, const char *server_name, const char *password, krb5_prompter_fct prompter, const char *keytab, krb5_ccache ccache, krb5_ccache *ret_cache) { krb5_error_code ret; krb5_ccache id = NULL; krb5_principal default_client = NULL, client = NULL; /* treat empty password as NULL */ if(password && *password == '\0') password = NULL; if(server_name == NULL) server_name = KADM5_ADMIN_SERVICE; if(client_name != NULL) { ret = krb5_parse_name(context, client_name, &client); if(ret) return ret; } if(password != NULL || prompter != NULL) { /* get principal from default cache, ok if this doesn't work */ ret = krb5_cc_default(context, &id); if(ret == 0) { ret = krb5_cc_get_principal(context, id, &default_client); if(ret) { krb5_cc_close(context, id); id = NULL; } else { const char *name, *inst; krb5_principal tmp; name = krb5_principal_get_comp_string(context, default_client, 0); inst = krb5_principal_get_comp_string(context, default_client, 1); if(inst == NULL || strcmp(inst, "admin") != 0) { ret = krb5_make_principal(context, &tmp, NULL, name, "admin", NULL); if(ret != 0) { krb5_free_principal(context, default_client); if (client) krb5_free_principal(context, client); krb5_cc_close(context, id); return ret; } krb5_free_principal(context, default_client); default_client = tmp; krb5_cc_close(context, id); id = NULL; } } } if (client != NULL) { /* A client was specified by the caller. */ if (default_client != NULL) { krb5_free_principal(context, default_client); default_client = NULL; } } else if (default_client != NULL) /* No client was specified by the caller, but we have a * client from the default credentials cache. */ client = default_client; else { /* No client was specified by the caller and we cannot determine * the client from a credentials cache. */ const char *user; user = get_default_username (); if(user == NULL) return KADM5_FAILURE; ret = krb5_make_principal(context, &client, NULL, user, "admin", NULL); if(ret) return ret; if (id != NULL) { krb5_cc_close(context, id); id = NULL; } } } else if(ccache != NULL) { id = ccache; ret = krb5_cc_get_principal(context, id, &client); if(ret) return ret; } if(id && (default_client == NULL || krb5_principal_compare(context, client, default_client))) { ret = get_kadm_ticket(context, id, client, server_name); if(ret == 0) { *ret_cache = id; krb5_free_principal(context, default_client); if (default_client != client) krb5_free_principal(context, client); return 0; } if(ccache != NULL) /* couldn't get ticket from cache */ return -1; } /* get creds via AS request */ if(id && (id != ccache)) krb5_cc_close(context, id); if (client != default_client) krb5_free_principal(context, default_client); ret = get_new_cache(context, client, password, prompter, keytab, server_name, ret_cache); krb5_free_principal(context, client); return ret; }