BOOL smb_krb5_principal_compare_any_realm(krb5_context context, krb5_const_principal princ1, krb5_const_principal princ2) { #ifdef HAVE_KRB5_PRINCIPAL_COMPARE_ANY_REALM return krb5_principal_compare_any_realm(context, princ1, princ2); /* krb5_princ_size is a macro in MIT */ #elif defined(HAVE_KRB5_PRINC_SIZE) || defined(krb5_princ_size) int i, len1, len2; const krb5_data *p1, *p2; len1 = krb5_princ_size(context, princ1); len2 = krb5_princ_size(context, princ2); if (len1 != len2) return False; for (i = 0; i < len1; i++) { p1 = krb5_princ_component(context, CONST_DISCARD(krb5_principal, princ1), i); p2 = krb5_princ_component(context, CONST_DISCARD(krb5_principal, princ2), i); if (p1->length != p2->length || memcmp(p1->data, p2->data, p1->length)) return False; } return True; #else #error NO_SUITABLE_PRINCIPAL_COMPARE_FUNCTION #endif }
/* * Convert a krb5_principal into the default salt for that principal. */ krb5_error_code ipa_krb5_principal2salt_norealm(krb5_context context, krb5_const_principal pr, krb5_data *ret) { unsigned int size = 0, offset=0; krb5_int32 nelem; register int i; if (pr == NULL) { ret->length = 0; ret->data = NULL; return 0; } nelem = krb5_princ_size(context, pr); for (i = 0; i < (int) nelem; i++) size += krb5_princ_component(context, pr, i)->length; ret->length = size; if (!(ret->data = malloc (size))) return ENOMEM; for (i = 0; i < (int) nelem; i++) { memcpy(&ret->data[offset], krb5_princ_component(context, pr, i)->data, krb5_princ_component(context, pr, i)->length); offset += krb5_princ_component(context, pr, i)->length; } return 0; }
static krb5_error_code hesiod_check(krb5_context context, krb5_pwqual_moddata data, const char *password, const char *policy_name, krb5_principal princ, const char **languages) { #ifdef HESIOD extern struct passwd *hes_getpwnam(); struct passwd *ent; int i, n; const char *cp; /* Don't check for principals with no password policy. */ if (policy_name == NULL) return 0; n = krb5_princ_size(handle->context, princ); for (i = 0; i < n; i++) { ent = hes_getpwnam(cp); if (ent && ent->pw_gecos && str_check_gecos(ent->pw_gecos, password)) { k5_setmsg(context, KADM5_PASS_Q_DICT, _("Password may not match user information.")); return KADM5_PASS_Q_DICT; } } #endif /* HESIOD */ return 0; }
/* * init_rtree() * * Populate KDC_LIST with the output of krb5_walk_realm_tree(). */ static krb5_error_code init_rtree(struct tr_state *ts, krb5_principal client, krb5_principal server) { krb5_error_code retval; ts->kdc_list = NULL; retval = krb5_walk_realm_tree(ts->ctx, krb5_princ_realm(ts->ctx, client), krb5_princ_realm(ts->ctx, server), &ts->kdc_list, KRB5_REALM_BRANCH_CHAR); if (retval) return retval; for (ts->nkdcs = 0; ts->kdc_list[ts->nkdcs]; ts->nkdcs++) { assert(krb5_princ_size(ts->ctx, ts->kdc_list[ts->nkdcs]) == 2); TR_DBG_RTREE(ts, "init_rtree", ts->kdc_list[ts->nkdcs]); } assert(ts->nkdcs > 1); ts->lst_kdc = ts->kdc_list + ts->nkdcs - 1; ts->kdc_tgts = calloc(ts->nkdcs + 1, sizeof(krb5_creds)); if (ts->kdc_tgts == NULL) return ENOMEM; return 0; }
/* * find_nxt_kdc() * * A NXT_TGT gotten from an intermediate KDC might actually be a * referral. Search KDC_LIST forward starting from CUR_KDC, looking * for the KDC with the same remote realm as NXT_TGT. If we don't * find it, the intermediate KDC is leading us off the transit path. * * Match on CUR_KDC's remote realm, not local realm, because, among * other reasons, we can get a referral to the final realm; e.g., * given * * KDC_LIST == { krbtgt/R1@R1, krbtgt/R2@R1, krbtgt/R3@R2, * krbtgt/R4@R3, NULL } * CUR_TGT->SERVER == krbtgt/R2@R1 * NXT_TGT->SERVER == krbtgt/R4@R2 * * i.e., we got a ticket issued by R2 with remote realm R4, we want to * find krbtgt/R4@R3, not krbtgt/R3@R2, even though we have no TGT * with R3 as its local realm. * * Set up for next iteration of do_traversal() loop by pointing * NXT_KDC to one entry forward of the match. */ static krb5_error_code find_nxt_kdc(struct tr_state *ts) { krb5_data *r1, *r2; krb5_principal *kdcptr; TR_DBG(ts, "find_nxt_kdc"); assert(ts->ntgts > 0); assert(ts->nxt_tgt == ts->kdc_tgts[ts->ntgts-1]); if (krb5_princ_size(ts->ctx, ts->nxt_tgt->server) != 2) return KRB5_KDCREP_MODIFIED; r1 = krb5_princ_component(ts->ctx, ts->nxt_tgt->server, 1); for (kdcptr = ts->cur_kdc + 1; *kdcptr != NULL; kdcptr++) { r2 = krb5_princ_component(ts->ctx, *kdcptr, 1); if (r1 != NULL && r2 != NULL && data_eq(*r1, *r2)) { break; } } if (*kdcptr != NULL) { ts->nxt_kdc = kdcptr; TR_DBG_RET(ts, "find_nxt_kdc", 0); return 0; } r2 = krb5_princ_component(ts->ctx, ts->kdc_list[0], 1); if (r1 != NULL && r2 != NULL && r1->length == r2->length && !memcmp(r1->data, r2->data, r1->length)) { TR_DBG_RET(ts, "find_nxt_kdc: looped back to local", KRB5_KDCREP_MODIFIED); return KRB5_KDCREP_MODIFIED; } /* * Realm is not in our list; we probably got an unexpected realm * referral. */ ts->offpath_tgt = ts->nxt_tgt; if (ts->cur_kdc == ts->kdc_list) { /* * Local KDC referred us off path; trust it for caching * purposes. */ return 0; } /* * Unlink the off-path TGT from KDC_TGTS but don't free it, * because we should return it. */ ts->kdc_tgts[--ts->ntgts] = NULL; ts->nxt_tgt = ts->cur_tgt; TR_DBG_RET(ts, "find_nxt_kdc", 0); 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; }
kim_error kim_identity_get_number_of_components (kim_identity in_identity, kim_count *out_number_of_components) { kim_error err = KIM_NO_ERROR; if (!err && !in_identity ) { err = check_error (KIM_NULL_PARAMETER_ERR); } if (!err && !out_number_of_components) { err = check_error (KIM_NULL_PARAMETER_ERR); } if (!err) { *out_number_of_components = krb5_princ_size (in_identity->context, in_identity->principal); } return check_error (err); }
static krb5_error_code ks_is_tgs_principal(struct mit_samba_context *ctx, krb5_const_principal principal) { char *p; int eq = -1; p = smb_krb5_principal_get_comp_string(ctx, ctx->context, principal, 0); eq = krb5_princ_size(ctx->context, principal) == 2 && (strcmp(p, KRB5_TGS_NAME) == 0); talloc_free(p); return eq; }
/* * Check whether the request satisfies the conditions for generating a referral * TGT. The caller checks whether the hostname component looks like a FQDN. */ static krb5_boolean is_referral_req(kdc_realm_t *kdc_active_realm, krb5_kdc_req *request) { krb5_boolean ret = FALSE; char *stype = NULL; char *ref_services = kdc_active_realm->realm_host_based_services; char *nonref_services = kdc_active_realm->realm_no_host_referral; if (!(request->kdc_options & KDC_OPT_CANONICALIZE)) return FALSE; if (request->kdc_options & KDC_OPT_ENC_TKT_IN_SKEY) return FALSE; if (krb5_princ_size(kdc_context, request->server) != 2) return FALSE; stype = data2string(krb5_princ_component(kdc_context, request->server, 0)); if (stype == NULL) return FALSE; switch (krb5_princ_type(kdc_context, request->server)) { case KRB5_NT_UNKNOWN: /* Allow referrals for NT-UNKNOWN principals, if configured. */ if (kdc_active_realm->realm_host_based_services != NULL) { if (!krb5_match_config_pattern(ref_services, stype) && !krb5_match_config_pattern(ref_services, KRB5_CONF_ASTERISK)) goto cleanup; } else goto cleanup; /* FALLTHROUGH */ case KRB5_NT_SRV_HST: case KRB5_NT_SRV_INST: /* Deny referrals for specific service types, if configured. */ if (kdc_active_realm->realm_no_host_referral != NULL) { if (krb5_match_config_pattern(nonref_services, stype)) goto cleanup; if (krb5_match_config_pattern(nonref_services, KRB5_CONF_ASTERISK)) goto cleanup; } ret = TRUE; break; default: goto cleanup; } cleanup: free(stype); return ret; }
void KRB5_CALLCONV krb5_free_principal(krb5_context context, krb5_principal val) { register krb5_int32 i; if (!val) return; if (val->data) { i = krb5_princ_size(context, val); while(--i >= 0) free(krb5_princ_component(context, val, i)->data); free(val->data); } free(val->realm.data); free(val); }
/* * Copy a principal structure, with fresh allocation. */ krb5_error_code KRB5_CALLCONV krb5_copy_principal(krb5_context context, krb5_const_principal inprinc, krb5_principal *outprinc) { register krb5_principal tempprinc; register int i, nelems; tempprinc = (krb5_principal)malloc(sizeof(krb5_principal_data)); if (tempprinc == 0) return ENOMEM; *tempprinc = *inprinc; nelems = (int) krb5_princ_size(context, inprinc); tempprinc->data = malloc(nelems * sizeof(krb5_data)); if (tempprinc->data == 0) { free((char *)tempprinc); return ENOMEM; } for (i = 0; i < nelems; i++) { if (krb5int_copy_data_contents(context, krb5_princ_component(context, inprinc, i), krb5_princ_component(context, tempprinc, i)) != 0) { while (--i >= 0) free(krb5_princ_component(context, tempprinc, i)->data); free (tempprinc->data); free (tempprinc); return ENOMEM; } } if (krb5int_copy_data_contents_add0(context, &inprinc->realm, &tempprinc->realm) != 0) { for (i = 0; i < nelems; i++) free(krb5_princ_component(context, tempprinc, i)->data); free(tempprinc->data); free(tempprinc); return ENOMEM; } *outprinc = tempprinc; return 0; }
kim_error kim_identity_is_tgt_service (kim_identity in_identity, kim_boolean *out_is_tgt_service) { kim_error err = KIM_NO_ERROR; if (!err && !in_identity ) { err = check_error (KIM_NULL_PARAMETER_ERR); } if (!err && !out_is_tgt_service) { err = check_error (KIM_NULL_PARAMETER_ERR); } if (!err) { kim_count count = krb5_princ_size (in_identity->context, in_identity->principal); krb5_data *name = krb5_princ_name (in_identity->context, in_identity->principal); /* krbtgt/<REALM1>@<REALM2> (usually REALM1 == REALM2, but not always) */ *out_is_tgt_service = ((count == 2) && (strlen (KRB5_TGS_NAME) == name->length) && (strncmp (name->data, KRB5_TGS_NAME, name->length) == 0)); } return check_error (err); }
/* * Check whether the request satisfies the conditions for generating a referral * TGT. The caller checks whether the hostname component looks like a FQDN. */ static krb5_boolean is_referral_req(kdc_realm_t *kdc_active_realm, krb5_kdc_req *request) { krb5_boolean ret = FALSE; char *stype = NULL; char *hostbased = kdc_active_realm->realm_hostbased; char *no_referral = kdc_active_realm->realm_no_referral; if (!(request->kdc_options & KDC_OPT_CANONICALIZE)) return FALSE; if (request->kdc_options & KDC_OPT_ENC_TKT_IN_SKEY) return FALSE; if (krb5_princ_size(kdc_context, request->server) != 2) return FALSE; stype = data2string(krb5_princ_component(kdc_context, request->server, 0)); if (stype == NULL) return FALSE; switch (krb5_princ_type(kdc_context, request->server)) { case KRB5_NT_UNKNOWN: /* Allow referrals for NT-UNKNOWN principals, if configured. */ if (!in_list(hostbased, stype) && !in_list(hostbased, "*")) goto cleanup; /* FALLTHROUGH */ case KRB5_NT_SRV_HST: case KRB5_NT_SRV_INST: /* Deny referrals for specific service types, if configured. */ if (in_list(no_referral, stype) || in_list(no_referral, "*")) goto cleanup; ret = TRUE; break; default: goto cleanup; } cleanup: free(stype); return ret; }
/*ARGSUSED*/ static krb5_error_code krb5_principal2salt_internal(krb5_context context, register krb5_const_principal pr, krb5_data *ret, int use_realm) { unsigned int size = 0, offset = 0; krb5_int32 nelem; register int i; if (pr == 0) { ret->length = 0; ret->data = 0; return 0; } nelem = krb5_princ_size(context, pr); if (use_realm) size += krb5_princ_realm(context, pr)->length; for (i = 0; i < (int) nelem; i++) size += krb5_princ_component(context, pr, i)->length; ret->length = size; if (!(ret->data = malloc (size))) return ENOMEM; if (use_realm) { offset = krb5_princ_realm(context, pr)->length; memcpy(ret->data, krb5_princ_realm(context, pr)->data, offset); } for (i = 0; i < (int) nelem; i++) { memcpy(&ret->data[offset], krb5_princ_component(context, pr, i)->data, krb5_princ_component(context, pr, i)->length); offset += krb5_princ_component(context, pr, i)->length; } return 0; }
krb5_error_code KRB5_CALLCONV krb5_524_conv_principal(krb5_context context, krb5_const_principal princ, char *name, char *inst, char *realm) { const struct krb_convert *p; const krb5_data *compo; char *c, *tmp_realm, *tmp_prealm; unsigned int tmp_realm_len; int retval; *name = *inst = '\0'; switch (krb5_princ_size(context, princ)) { case 2: /* Check if this principal is listed in the table */ compo = krb5_princ_component(context, princ, 0); p = sconv_list; while (p->v4_str) { if (p->len == compo->length && memcmp(p->v5_str, compo->data, compo->length) == 0) { /* * It is, so set the new name now, and chop off * instance's domain name if requested. */ if (strlcpy(name, p->v4_str, ANAME_SZ) >= ANAME_SZ) return KRB5_INVALID_PRINCIPAL; if (p->flags & DO_REALM_CONVERSION) { compo = krb5_princ_component(context, princ, 1); c = strnchr(compo->data, '.', compo->length); if (!c || (c - compo->data) >= INST_SZ - 1) return KRB5_INVALID_PRINCIPAL; memcpy(inst, compo->data, (size_t) (c - compo->data)); inst[c - compo->data] = '\0'; } break; } p++; } /* If inst isn't set, the service isn't listed in the table, */ /* so just copy it. */ if (*inst == '\0') { compo = krb5_princ_component(context, princ, 1); if (compo->length >= INST_SZ - 1) return KRB5_INVALID_PRINCIPAL; memcpy(inst, compo->data, compo->length); inst[compo->length] = '\0'; } /* fall through */ case 1: /* name may have been set above; otherwise, just copy it */ if (*name == '\0') { compo = krb5_princ_component(context, princ, 0); if (compo->length >= ANAME_SZ) return KRB5_INVALID_PRINCIPAL; memcpy(name, compo->data, compo->length); name[compo->length] = '\0'; } break; default: return KRB5_INVALID_PRINCIPAL; } compo = krb5_princ_realm(context, princ); tmp_prealm = malloc(compo->length + 1); if (tmp_prealm == NULL) return ENOMEM; strncpy(tmp_prealm, compo->data, compo->length); tmp_prealm[compo->length] = '\0'; /* Ask for v4_realm corresponding to krb5 principal realm from krb5.conf realms stanza */ if (context->profile == 0) return KRB5_CONFIG_CANTOPEN; retval = profile_get_string(context->profile, "realms", tmp_prealm, "v4_realm", 0, &tmp_realm); free(tmp_prealm); if (retval) { return retval; } else { if (tmp_realm == 0) { if (compo->length > REALM_SZ - 1) return KRB5_INVALID_PRINCIPAL; strncpy(realm, compo->data, compo->length); realm[compo->length] = '\0'; } else { tmp_realm_len = strlen(tmp_realm); if (tmp_realm_len > REALM_SZ - 1) return KRB5_INVALID_PRINCIPAL; strncpy(realm, tmp_realm, tmp_realm_len); realm[tmp_realm_len] = '\0'; profile_release_string(tmp_realm); } } return 0; }
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; }
krb5_error_code sam_get_db_entry(krb5_context context, krb5_principal client, int *sam_type, struct _krb5_db_entry_new **db_entry) { struct _krb5_db_entry_new *assoc = NULL; krb5_principal newp = NULL; int probeslot; void *ptr = NULL; krb5_error_code retval; if (db_entry) *db_entry = NULL; retval = krb5_copy_principal(context, client, &newp); if (retval) { com_err("krb5kdc", retval, "copying client name for preauth probe"); return retval; } probeslot = krb5_princ_size(context, newp)++; ptr = realloc(krb5_princ_name(context, newp), krb5_princ_size(context, newp) * sizeof(krb5_data)); if (ptr == NULL) { retval = ENOMEM; goto cleanup; } krb5_princ_name(context, newp) = ptr; for(sam_ptr = sam_inst_map; sam_ptr->name; sam_ptr++) { if (*sam_type && *sam_type != sam_ptr->sam_type) continue; krb5_princ_component(context,newp,probeslot)->data = sam_ptr->name; krb5_princ_component(context,newp,probeslot)->length = strlen(sam_ptr->name); retval = krb5_db_get_principal(context, newp, 0, &assoc); if (!retval) break; } cleanup: if (ptr) { krb5_princ_component(context,newp,probeslot)->data = 0; krb5_princ_component(context,newp,probeslot)->length = 0; krb5_free_principal(context, newp); } if (probeslot) krb5_princ_size(context, newp)--; if (retval) return retval; if (sam_ptr->sam_type) { /* Found entry of type sam_ptr->sam_type */ if (sam_type) *sam_type = sam_ptr->sam_type; if (db_entry) *db_entry = assoc; else krb5_db_free_principal(context, assoc); return 0; } else { return KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN; } }
static WERROR DsCrackNameSPNAlias(struct ldb_context *sam_ctx, TALLOC_CTX *mem_ctx, struct smb_krb5_context *smb_krb5_context, uint32_t format_flags, enum drsuapi_DsNameFormat format_offered, enum drsuapi_DsNameFormat format_desired, const char *name, struct drsuapi_DsNameInfo1 *info1) { WERROR wret; krb5_error_code ret; krb5_principal principal; const krb5_data *component; const char *service, *dns_name; char *new_service; char *new_princ; enum drsuapi_DsNameStatus namestatus; /* parse principal */ ret = krb5_parse_name_flags(smb_krb5_context->krb5_context, name, KRB5_PRINCIPAL_PARSE_NO_REALM, &principal); if (ret) { DEBUG(2, ("Could not parse principal: %s: %s\n", name, smb_get_krb5_error_message(smb_krb5_context->krb5_context, ret, mem_ctx))); return WERR_NOMEM; } /* grab cifs/, http/ etc */ /* This is checked for in callers, but be safe */ if (krb5_princ_size(smb_krb5_context->krb5_context, principal) < 2) { info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND; krb5_free_principal(smb_krb5_context->krb5_context, principal); return WERR_OK; } component = krb5_princ_component(smb_krb5_context->krb5_context, principal, 0); service = (const char *)component->data; component = krb5_princ_component(smb_krb5_context->krb5_context, principal, 1); dns_name = (const char *)component->data; /* MAP it */ namestatus = LDB_lookup_spn_alias(smb_krb5_context->krb5_context, sam_ctx, mem_ctx, service, &new_service); if (namestatus == DRSUAPI_DS_NAME_STATUS_NOT_FOUND) { wret = WERR_OK; info1->status = DRSUAPI_DS_NAME_STATUS_DOMAIN_ONLY; info1->dns_domain_name = talloc_strdup(mem_ctx, dns_name); if (!info1->dns_domain_name) { wret = WERR_NOMEM; } krb5_free_principal(smb_krb5_context->krb5_context, principal); return wret; } else if (namestatus != DRSUAPI_DS_NAME_STATUS_OK) { info1->status = namestatus; krb5_free_principal(smb_krb5_context->krb5_context, principal); return WERR_OK; } /* reform principal */ new_princ = talloc_asprintf(mem_ctx, "%s/%s", new_service, dns_name); if (!new_princ) { krb5_free_principal(smb_krb5_context->krb5_context, principal); return WERR_NOMEM; } wret = DsCrackNameOneName(sam_ctx, mem_ctx, format_flags, format_offered, format_desired, new_princ, info1); talloc_free(new_princ); if (W_ERROR_IS_OK(wret) && (info1->status == DRSUAPI_DS_NAME_STATUS_NOT_FOUND)) { info1->status = DRSUAPI_DS_NAME_STATUS_DOMAIN_ONLY; info1->dns_domain_name = talloc_strdup(mem_ctx, dns_name); if (!info1->dns_domain_name) { wret = WERR_NOMEM; } } krb5_free_principal(smb_krb5_context->krb5_context, principal); return wret; }
/*ARGSUSED*/ krb5_error_code process_tgs_req(krb5_data *pkt, const krb5_fulladdr *from, krb5_data **response) { krb5_keyblock * subkey = 0; krb5_kdc_req *request = 0; krb5_db_entry server; 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; krb5_transited enc_tkt_transited; int newtransited = 0; krb5_error_code retval = 0; krb5_keyblock encrypting_key; int nprincs = 0; krb5_boolean more; krb5_timestamp kdc_time, authtime=0; krb5_keyblock session_key; krb5_timestamp until, rtime; krb5_keyblock *reply_key = NULL; krb5_keyblock *mkey_ptr; krb5_key_data *server_key; char *cname = 0, *sname = 0, *altcname = 0; krb5_last_req_entry *nolrarray[2], nolrentry; krb5_enctype useenctype; int errcode, errcode2; register int i; int firstpass = 1; const char *status = 0; krb5_enc_tkt_part *header_enc_tkt = NULL; /* ticket granting or evidence ticket */ krb5_db_entry client, krbtgt; int c_nprincs = 0, k_nprincs = 0; 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 */ char *s4u_name = NULL; krb5_boolean is_referral, db_ref_done = FALSE; const char *emsg = NULL; krb5_data *tgs_1 =NULL, *server_1 = NULL; krb5_principal krbtgt_princ; krb5_kvno ticket_kvno = 0; struct kdc_request_state *state = NULL; krb5_pa_data *pa_tgs_req; /*points into request*/ krb5_data scratch; session_key.contents = NULL; retval = decode_krb5_tgs_req(pkt, &request); if (retval) return retval; /* * setup_server_realm() sets up the global realm-specific data pointer. */ if ((retval = setup_server_realm(request->server))) { krb5_free_kdc_req(kdc_context, request); return retval; } errcode = kdc_process_tgs_req(request, from, pkt, &header_ticket, &krbtgt, &k_nprincs, &subkey, &pa_tgs_req); if (header_ticket && header_ticket->enc_part2 && (errcode2 = krb5_unparse_name(kdc_context, header_ticket->enc_part2->client, &cname))) { status = "UNPARSING CLIENT"; errcode = errcode2; goto cleanup; } limit_string(cname); 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 = kdc_make_rstate(&state); if (errcode !=0) { status = "making state"; 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); if (errcode !=0) { status = "kdc_find_fast"; goto cleanup; } /* * 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. */ /* XXX make sure server here has the proper realm...taken from AP_REQ header? */ if (isflagset(request->kdc_options, KDC_OPT_CANONICALIZE)) { setflag(c_flags, KRB5_KDB_FLAG_CANONICALIZE); setflag(s_flags, KRB5_KDB_FLAG_CANONICALIZE); } db_ref_done = FALSE; ref_tgt_again: nprincs = 1; if ((errcode = krb5_unparse_name(kdc_context, request->server, &sname))) { status = "UNPARSING SERVER"; goto cleanup; } limit_string(sname); errcode = krb5_db_get_principal_ext(kdc_context, request->server, s_flags, &server, &nprincs, &more); if (errcode) { status = "LOOKING_UP_SERVER"; nprincs = 0; goto cleanup; } tgt_again: if (more) { status = "NON_UNIQUE_PRINCIPAL"; errcode = KRB5KDC_ERR_PRINCIPAL_NOT_UNIQUE; goto cleanup; } else if (nprincs != 1) { /* * might be a request for a TGT for some other realm; we * should do our best to find such a TGS in this db */ if (firstpass ) { if ( krb5_is_tgs_principal(request->server) == TRUE) { /* Principal is a name of krb ticket service */ if (krb5_princ_size(kdc_context, request->server) == 2) { server_1 = krb5_princ_component(kdc_context, request->server, 1); tgs_1 = krb5_princ_component(kdc_context, tgs_server, 1); if (!tgs_1 || !data_eq(*server_1, *tgs_1)) { krb5_db_free_principal(kdc_context, &server, nprincs); find_alternate_tgs(request, &server, &more, &nprincs); firstpass = 0; goto tgt_again; } } krb5_db_free_principal(kdc_context, &server, nprincs); status = "UNKNOWN_SERVER"; errcode = KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN; goto cleanup; } else if ( db_ref_done == FALSE) { retval = prep_reprocess_req(request, &krbtgt_princ); if (!retval) { krb5_free_principal(kdc_context, request->server); retval = krb5_copy_principal(kdc_context, krbtgt_princ, &(request->server)); if (!retval) { db_ref_done = TRUE; if (sname != NULL) free(sname); goto ref_tgt_again; } } } } krb5_db_free_principal(kdc_context, &server, nprincs); status = "UNKNOWN_SERVER"; errcode = KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN; goto cleanup; } if ((errcode = krb5_timeofday(kdc_context, &kdc_time))) { status = "TIME_OF_DAY"; goto cleanup; } if ((retval = validate_tgs_request(request, server, header_ticket, kdc_time, &status))) { if (!status) status = "UNKNOWN_REASON"; errcode = retval + ERROR_TABLE_BASE_krb5; goto cleanup; } if (!is_local_principal(header_enc_tkt->client)) setflag(c_flags, KRB5_KDB_FLAG_CROSS_REALM); is_referral = krb5_is_tgs_principal(server.princ) && !krb5_principal_compare(kdc_context, tgs_server, server.princ); /* Check for protocol transition */ errcode = kdc_process_s4u2self_req(kdc_context, request, header_enc_tkt->client, &server, subkey, header_enc_tkt->session, kdc_time, &s4u_x509_user, &client, &c_nprincs, &status); if (errcode) goto cleanup; if (s4u_x509_user != NULL) setflag(c_flags, KRB5_KDB_FLAG_PROTOCOL_TRANSITION); /* * We pick the session keytype here.... * * Some special care needs to be taken in the user-to-user * case, since we don't know what keytypes the application server * which is doing user-to-user authentication can support. We * know that it at least must be able to support the encryption * type of the session key in the TGT, since otherwise it won't be * able to decrypt the U2U ticket! So we use that in preference * to anything else. */ useenctype = 0; if (isflagset(request->kdc_options, KDC_OPT_ENC_TKT_IN_SKEY | KDC_OPT_CNAME_IN_ADDL_TKT)) { krb5_keyblock * st_sealing_key; krb5_kvno st_srv_kvno; krb5_enctype etype; krb5_db_entry st_client; int st_nprincs = 0; /* * Get the key for the second ticket, and decrypt it. */ if ((errcode = kdc_get_server_key(request->second_ticket[st_idx], c_flags, TRUE, /* match_enctype */ &st_client, &st_nprincs, &st_sealing_key, &st_srv_kvno))) { status = "2ND_TKT_SERVER"; goto cleanup; } errcode = krb5_decrypt_tkt_part(kdc_context, st_sealing_key, request->second_ticket[st_idx]); krb5_free_keyblock(kdc_context, st_sealing_key); if (errcode) { status = "2ND_TKT_DECRYPT"; krb5_db_free_principal(kdc_context, &st_client, st_nprincs); goto cleanup; } etype = request->second_ticket[st_idx]->enc_part2->session->enctype; if (!krb5_c_valid_enctype(etype)) { status = "BAD_ETYPE_IN_2ND_TKT"; errcode = KRB5KDC_ERR_ETYPE_NOSUPP; krb5_db_free_principal(kdc_context, &st_client, st_nprincs); goto cleanup; } for (i = 0; i < request->nktypes; i++) { if (request->ktype[i] == etype) { useenctype = etype; break; } } if (isflagset(request->kdc_options, KDC_OPT_CNAME_IN_ADDL_TKT)) { /* Do constrained delegation protocol and authorization checks */ errcode = kdc_process_s4u2proxy_req(kdc_context, request, request->second_ticket[st_idx]->enc_part2, &st_client, header_ticket->enc_part2->client, request->server, &status); if (errcode) goto cleanup; setflag(c_flags, KRB5_KDB_FLAG_CONSTRAINED_DELEGATION); assert(krb5_is_tgs_principal(header_ticket->server)); /* From now on, use evidence ticket as header ticket */ header_enc_tkt = request->second_ticket[st_idx]->enc_part2; assert(c_nprincs == 0); /* assured by kdc_process_s4u2self_req() */ client = st_client; c_nprincs = st_nprincs; } else { /* "client" is not used for user2user */ krb5_db_free_principal(kdc_context, &st_client, st_nprincs); } } /* * Select the keytype for the ticket session key. */ if ((useenctype == 0) && (useenctype = select_session_keytype(kdc_context, &server, request->nktypes, request->ktype)) == 0) { /* unsupported ktype */ status = "BAD_ENCRYPTION_TYPE"; errcode = KRB5KDC_ERR_ETYPE_NOSUPP; goto cleanup; } errcode = krb5_c_make_random_key(kdc_context, useenctype, &session_key); if (errcode) { /* random key failed */ status = "RANDOM_KEY_FAILED"; goto cleanup; } authtime = header_enc_tkt->times.authtime; if (is_referral) ticket_reply.server = server.princ; else ticket_reply.server = request->server; /* XXX careful for realm... */ enc_tkt_reply.flags = 0; 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); /* * Fix header_ticket's starttime; if it's zero, fill in the * authtime's value. */ if (!(header_enc_tkt->times.starttime)) header_enc_tkt->times.starttime = header_enc_tkt->times.authtime; /* 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)) { setflag(enc_tkt_reply.flags, TKT_FLG_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 (c_nprincs && isflagset(client.attributes, KRB5_KDB_DISALLOW_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)) { setflag(enc_tkt_reply.flags, TKT_FLG_FORWARDED); /* include new addresses in ticket & reply */ enc_tkt_reply.caddrs = request->addresses; reply_encpart.caddrs = request->addresses; } if (isflagset(header_enc_tkt->flags, TKT_FLG_FORWARDED)) setflag(enc_tkt_reply.flags, TKT_FLG_FORWARDED); if (isflagset(request->kdc_options, KDC_OPT_PROXIABLE)) setflag(enc_tkt_reply.flags, TKT_FLG_PROXIABLE); if (isflagset(request->kdc_options, KDC_OPT_PROXY)) { setflag(enc_tkt_reply.flags, TKT_FLG_PROXY); /* include new addresses in ticket & reply */ enc_tkt_reply.caddrs = request->addresses; reply_encpart.caddrs = request->addresses; } if (isflagset(request->kdc_options, KDC_OPT_ALLOW_POSTDATE)) setflag(enc_tkt_reply.flags, TKT_FLG_MAY_POSTDATE); if (isflagset(request->kdc_options, KDC_OPT_POSTDATED)) { setflag(enc_tkt_reply.flags, TKT_FLG_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); clear(enc_tkt_reply.flags, TKT_FLG_INVALID); } if (isflagset(request->kdc_options, KDC_OPT_RENEW)) { 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); old_life = enc_tkt_reply.times.endtime - enc_tkt_reply.times.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; until = (request->till == 0) ? kdc_infinity : request->till; enc_tkt_reply.times.endtime = min(until, min(enc_tkt_reply.times.starttime + server.max_life, min(enc_tkt_reply.times.starttime + max_life_for_realm, header_enc_tkt->times.endtime))); if (isflagset(request->kdc_options, KDC_OPT_RENEWABLE_OK) && (enc_tkt_reply.times.endtime < request->till) && isflagset(header_enc_tkt->flags, TKT_FLG_RENEWABLE)) { setflag(request->kdc_options, KDC_OPT_RENEWABLE); request->rtime = min(request->till, header_enc_tkt->times.renew_till); } } rtime = (request->rtime == 0) ? kdc_infinity : request->rtime; if (isflagset(request->kdc_options, KDC_OPT_RENEWABLE)) { /* already checked above in policy check to reject request for a renewable ticket using a non-renewable ticket */ setflag(enc_tkt_reply.flags, TKT_FLG_RENEWABLE); enc_tkt_reply.times.renew_till = min(rtime, min(header_enc_tkt->times.renew_till, enc_tkt_reply.times.starttime + min(server.max_renewable_life, max_renewable_life_for_realm))); } else { enc_tkt_reply.times.renew_till = 0; } /* * Set authtime to be the same as header_ticket's */ enc_tkt_reply.times.authtime = header_enc_tkt->times.authtime; /* * Propagate the preauthentication flags through to the returned ticket. */ if (isflagset(header_enc_tkt->flags, TKT_FLG_PRE_AUTH)) setflag(enc_tkt_reply.flags, TKT_FLG_PRE_AUTH); if (isflagset(header_enc_tkt->flags, TKT_FLG_HW_AUTH)) setflag(enc_tkt_reply.flags, TKT_FLG_HW_AUTH); /* 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)) { errcode = krb5_unparse_name(kdc_context, s4u_x509_user->user_id.user, &s4u_name); } else if (isflagset(c_flags, KRB5_KDB_FLAG_CONSTRAINED_DELEGATION)) { errcode = krb5_unparse_name(kdc_context, header_enc_tkt->client, &s4u_name); } else { errcode = 0; } if (errcode) { status = "UNPARSING S4U CLIENT"; goto cleanup; } 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; } if ((errcode = krb5_dbe_find_mkey(kdc_context, master_keylist, &server, &mkey_ptr))) { krb5_keylist_node *tmp_mkey_list; /* try refreshing master key list */ /* XXX it would nice if we had the mkvno here for optimization */ if (krb5_db_fetch_mkey_list(kdc_context, master_princ, &master_keyblock, 0, &tmp_mkey_list) == 0) { krb5_dbe_free_key_list(kdc_context, master_keylist); master_keylist = tmp_mkey_list; if ((errcode = krb5_dbe_find_mkey(kdc_context, master_keylist, &server, &mkey_ptr))) { status = "FINDING_MASTER_KEY"; goto cleanup; } } else { status = "FINDING_MASTER_KEY"; goto cleanup; } } /* convert server.key into a real key (it may be encrypted * in the database) */ if ((errcode = krb5_dbekd_decrypt_key_data(kdc_context, mkey_ptr, 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 * and there was no authorization data included, try to lookup * the client principal as it may be mapped to a local account. * * Always validate authorization data for constrained delegation * because we must validate the KDC signatures. */ if (!isflagset(c_flags, KRB5_KDB_FLAGS_S4U) && header_enc_tkt->authorization_data == NULL) { /* 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(c_nprincs == 0); /* should not have been looked up already */ c_nprincs = 1; errcode = krb5_db_get_principal_ext(kdc_context, header_enc_tkt->client, c_flags, &client, &c_nprincs, &more); /* * We can ignore errors because the principal may be a * valid cross-realm principal for which we have no local * mapping. But we do want to check that at most one entry * was returned. */ if (errcode == 0 && (more || c_nprincs > 1)) { errcode = KRB5KDC_ERR_PRINCIPAL_NOT_UNIQUE; goto cleanup; } else if (errcode) { c_nprincs = 0; } } } enc_tkt_reply.authorization_data = NULL; 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 = header_enc_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 "" */ errcode = handle_authdata(kdc_context, c_flags, (c_nprincs != 0) ? &client : NULL, &server, (k_nprincs != 0) ? &krbtgt : NULL, subkey != NULL ? subkey : header_ticket->enc_part2->session, &encrypting_key, /* U2U or server key */ pkt, request, s4u_x509_user ? s4u_x509_user->user_id.user : NULL, header_enc_tkt, &enc_tkt_reply); if (errcode) { krb5_klog_syslog(LOG_INFO, "TGS_REQ : handle_authdata (%d)", errcode); status = "HANDLE_AUTHDATA"; goto cleanup; } if (is_referral && isflagset(s_flags, KRB5_KDB_FLAG_CANONICALIZE)) { errcode = return_svr_referral_data(kdc_context, &server, &reply_encpart); if (errcode) { status = "KDC_RETURN_ENC_PADATA"; goto cleanup; } } /* * 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 (realm_compare(header_ticket->server, tgs_server) || realm_compare(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 = "BAD_TRTYPE"; errcode = KRB5KDC_ERR_TRTYPE_NOSUPP; goto cleanup; } enc_tkt_transited.tr_type = KRB5_DOMAIN_X500_COMPRESS; enc_tkt_transited.magic = 0; enc_tkt_transited.tr_contents.magic = 0; enc_tkt_transited.tr_contents.data = 0; enc_tkt_transited.tr_contents.length = 0; enc_tkt_reply.transited = enc_tkt_transited; 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_TR_FAIL"; goto cleanup; } newtransited = 1; } if (isflagset(c_flags, KRB5_KDB_FLAG_CROSS_REALM)) { errcode = validate_transit_path(kdc_context, header_enc_tkt->client, &server, (k_nprincs != 0) ? &krbtgt : NULL); if (errcode) { status = "NON_TRANSITIVE"; goto cleanup; } } if (!isflagset (request->kdc_options, KDC_OPT_DISABLE_TRANSITED_CHECK)) { unsigned int tlen; char *tdots; errcode = kdc_check_transited_list (kdc_context, &enc_tkt_reply.transited.tr_contents, krb5_princ_realm (kdc_context, header_enc_tkt->client), krb5_princ_realm (kdc_context, request->server)); tlen = enc_tkt_reply.transited.tr_contents.length; tdots = tlen > 125 ? "..." : ""; tlen = tlen > 125 ? 125 : tlen; if (errcode == 0) { setflag (enc_tkt_reply.flags, TKT_FLG_TRANSIT_POLICY_CHECKED); } else if (errcode == KRB5KRB_AP_ERR_ILL_CR_TKT) krb5_klog_syslog (LOG_INFO, "bad realm transit path from '%s' to '%s' " "via '%.*s%s'", cname ? cname : "<unknown client>", sname ? sname : "<unknown server>", tlen, enc_tkt_reply.transited.tr_contents.data, tdots); else { emsg = krb5_get_error_message(kdc_context, errcode); krb5_klog_syslog (LOG_ERR, "unexpected error checking transit from " "'%s' to '%s' via '%.*s%s': %s", cname ? cname : "<unknown client>", sname ? sname : "<unknown server>", tlen, enc_tkt_reply.transited.tr_contents.data, tdots, emsg); krb5_free_error_message(kdc_context, emsg); emsg = NULL; } } else krb5_klog_syslog (LOG_INFO, "not checking transit path"); if (reject_bad_transit && !isflagset (enc_tkt_reply.flags, TKT_FLG_TRANSIT_POLICY_CHECKED)) { errcode = KRB5KDC_ERR_POLICY; status = "BAD_TRANSIT"; 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)) { if ((errcode = krb5_unparse_name(kdc_context, client2, &altcname))) altcname = 0; if (altcname != NULL) limit_string(altcname); errcode = KRB5KDC_ERR_SERVER_NOMATCH; status = "2ND_TKT_MISMATCH"; goto cleanup; } ticket_kvno = 0; ticket_reply.enc_part.enctype = t2enc->session->enctype; 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 = "TKT_ENCRYPT"; goto cleanup; } ticket_reply.enc_part.kvno = ticket_kvno; /* Start assembling the response */ reply.msg_type = KRB5_TGS_REP; reply.padata = 0;/* always */ if (isflagset(c_flags, KRB5_KDB_FLAG_PROTOCOL_TRANSITION) && find_pa_data(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 = "KDC_RETURN_S4U2SELF_PADATA"; 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 EXCEPT for authtime; its location is used for ktime */ reply_encpart.times = enc_tkt_reply.times; reply_encpart.times.authtime = header_enc_tkt->times.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; nolrentry.lr_type = KRB5_LRQ_NONE; nolrentry.value = 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 = "Preparing FAST padata"; goto cleanup; } errcode =kdc_fast_handle_reply_key(state, subkey?subkey:header_ticket->enc_part2->session, &reply_key); if (errcode) { status = "generating reply key"; goto cleanup; } 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); log_tgs_req(from, request, &reply, cname, sname, altcname, authtime, c_flags, s4u_name, 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 > 128) errcode = KRB_ERR_GENERIC; retval = prepare_error_tgs(state, request, header_ticket, errcode, nprincs ? server.princ : NULL, response, status); 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); if (cname != NULL) free(cname); if (sname != NULL) free(sname); if (nprincs != 0) krb5_db_free_principal(kdc_context, &server, 1); if (session_key.contents != NULL) krb5_free_keyblock_contents(kdc_context, &session_key); if (newtransited) free(enc_tkt_reply.transited.tr_contents.data); if (k_nprincs) krb5_db_free_principal(kdc_context, &krbtgt, k_nprincs); if (c_nprincs) krb5_db_free_principal(kdc_context, &client, c_nprincs); 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 (s4u_name != NULL) free(s4u_name); if (subkey != NULL) krb5_free_keyblock(kdc_context, subkey); 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); return retval; }
/* * find_nxt_kdc() * * A NXT_TGT gotten from an intermediate KDC might actually be a * referral. Search KDC_LIST forward starting from CUR_KDC, looking * for the KDC with the same remote realm as NXT_TGT. If we don't * find it, the intermediate KDC is leading us off the transit path. * * Match on CUR_KDC's remote realm, not local realm, because, among * other reasons, we can get a referral to the final realm; e.g., * given * * KDC_LIST == { krbtgt/R1@R1, krbtgt/R2@R1, krbtgt/R3@R2, * krbtgt/R4@R3, NULL } * CUR_TGT->SERVER == krbtgt/R2@R1 * NXT_TGT->SERVER == krbtgt/R4@R2 * * i.e., we got a ticket issued by R2 with remote realm R4, we want to * find krbtgt/R4@R3, not krbtgt/R3@R2, even though we have no TGT * with R3 as its local realm. * * Set up for next iteration of do_traversal() loop by pointing * NXT_KDC to one entry forward of the match. */ static krb5_error_code find_nxt_kdc(struct tr_state *ts) { krb5_data *r1, *r2; krb5_principal *kdcptr; TR_DBG(ts, "find_nxt_kdc"); /* * Solaris Kerberos: * The following assertion is not be true for the case when * ts->nxt points to a cached ticket and not to a freshly * fetched TGT in ts->kdc_tgts. See changes in try_kdc() */ /* assert(ts->nxt_tgt == ts->kdc_tgts[ts->ntgts-1]); */ if (krb5_princ_size(ts->ctx, ts->nxt_tgt->server) != 2) { /* Solaris Kerberos */ char *s_name = NULL; int err = krb5_unparse_name(ts->ctx, ts->nxt_tgt->server, &s_name); if (!err) { krb5_set_error_message(ts->ctx, KRB5_KDCREP_MODIFIED, dgettext(TEXT_DOMAIN, "KDC reply did not match expectations: server '%s' principal size should be 2"), s_name); krb5_free_unparsed_name(ts->ctx, s_name); } else krb5_set_error_message(ts->ctx, KRB5_KDCREP_MODIFIED, dgettext(TEXT_DOMAIN, "KDC reply did not match expectations: server principal size should be 2")); return KRB5_KDCREP_MODIFIED; } r1 = krb5_princ_component(ts->ctx, ts->nxt_tgt->server, 1); for (kdcptr = ts->cur_kdc + 1; *kdcptr != NULL; kdcptr++) { r2 = krb5_princ_component(ts->ctx, *kdcptr, 1); if (r1 != NULL && r2 != NULL && r1->length == r2->length && !memcmp(r1->data, r2->data, r1->length)) { break; } } if (*kdcptr == NULL) { /* * Not found; we probably got an unexpected realm referral. * Don't touch NXT_KDC, thus allowing next_closest_tgt() to * continue looping backwards. */ /* * Solaris Kerberos: * Only free the allocated creds if they are in kdc_tgts. If they * are in cc_tgts no freeing is necessary. */ if (ts->ntgts > 0 && ts->nxt_tgt == ts->kdc_tgts[ts->ntgts-1]) { /* Punt NXT_TGT from KDC_TGTS if bogus. */ krb5_free_creds(ts->ctx, ts->kdc_tgts[--ts->ntgts]); ts->kdc_tgts[ts->ntgts] = NULL; } TR_DBG_RET(ts, "find_nxt_kdc", KRB5_KDCREP_MODIFIED); krb5_set_error_message(ts->ctx, KRB5_KDCREP_MODIFIED, dgettext(TEXT_DOMAIN, "KDC reply did not match expectation: KDC not found. Probably got an unexpected realm referral")); return KRB5_KDCREP_MODIFIED; } ts->nxt_kdc = kdcptr; TR_DBG_RET(ts, "find_nxt_kdc", 0); return 0; }
/*ARGSUSED*/ krb5_error_code process_tgs_req(krb5_data *pkt, const krb5_fulladdr *from, krb5_data **response) { krb5_keyblock * subkey = 0; krb5_kdc_req *request = 0; krb5_db_entry server; 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; krb5_transited enc_tkt_transited; int newtransited = 0; krb5_error_code retval = 0; int nprincs = 0; krb5_boolean more; krb5_timestamp kdc_time, authtime=0; krb5_keyblock session_key; krb5_timestamp until, rtime; krb5_keyblock encrypting_key; krb5_key_data *server_key; char *cname = 0, *sname = 0, *tmp = 0; const char *fromstring = 0; krb5_last_req_entry *nolrarray[2], nolrentry; /* krb5_address *noaddrarray[1]; */ krb5_enctype useenctype; int errcode, errcode2; register int i; int firstpass = 1; const char *status = 0; char ktypestr[128]; char rep_etypestr[128]; char fromstringbuf[70]; session_key.contents = 0; retval = decode_krb5_tgs_req(pkt, &request); if (retval) return retval; if (request->msg_type != KRB5_TGS_REQ) return KRB5_BADMSGTYPE; ktypes2str(ktypestr, sizeof(ktypestr), request->nktypes, request->ktype); /* * setup_server_realm() sets up the global realm-specific data pointer. */ if ((retval = setup_server_realm(request->server))) { krb5_free_kdc_req(kdc_context, request); return retval; } fromstring = inet_ntop(ADDRTYPE2FAMILY(from->address->addrtype), from->address->contents, fromstringbuf, sizeof(fromstringbuf)); if (!fromstring) fromstring = "<unknown>"; if ((errcode = krb5_unparse_name(kdc_context, request->server, &sname))) { status = "UNPARSING SERVER"; goto cleanup; } limit_string(sname); /* errcode = kdc_process_tgs_req(request, from, pkt, &req_authdat); */ errcode = kdc_process_tgs_req(request, from, pkt, &header_ticket, &subkey); if (header_ticket && header_ticket->enc_part2 && (errcode2 = krb5_unparse_name(kdc_context, header_ticket->enc_part2->client, &cname))) { status = "UNPARSING CLIENT"; errcode = errcode2; goto cleanup; } limit_string(cname); if (errcode) { status = "PROCESS_TGS"; goto cleanup; } if (!header_ticket) { errcode = KRB5_NO_TKT_SUPPLIED; /* XXX? */ status="UNEXPECTED NULL in header_ticket"; goto cleanup; } /* * 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. */ authtime = header_ticket->enc_part2->times.authtime; /* XXX make sure server here has the proper realm...taken from AP_REQ header? */ nprincs = 1; if ((errcode = get_principal(kdc_context, request->server, &server, &nprincs, &more))) { status = "LOOKING_UP_SERVER"; nprincs = 0; goto cleanup; } tgt_again: if (more) { status = "NON_UNIQUE_PRINCIPAL"; errcode = KRB5KDC_ERR_PRINCIPAL_NOT_UNIQUE; goto cleanup; } else if (nprincs != 1) { /* * might be a request for a TGT for some other realm; we * should do our best to find such a TGS in this db */ if (firstpass && krb5_is_tgs_principal(request->server) == TRUE) { if (krb5_princ_size(kdc_context, request->server) == 2) { krb5_data *server_1 = krb5_princ_component(kdc_context, request->server, 1); krb5_data *tgs_1 = krb5_princ_component(kdc_context, tgs_server, 1); if (!tgs_1 || !data_eq(*server_1, *tgs_1)) { krb5_db_free_principal(kdc_context, &server, nprincs); find_alternate_tgs(request, &server, &more, &nprincs); firstpass = 0; goto tgt_again; } } } krb5_db_free_principal(kdc_context, &server, nprincs); status = "UNKNOWN_SERVER"; errcode = KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN; goto cleanup; } if ((errcode = krb5_timeofday(kdc_context, &kdc_time))) { status = "TIME_OF_DAY"; goto cleanup; } if ((retval = validate_tgs_request(request, server, header_ticket, kdc_time, &status))) { if (!status) status = "UNKNOWN_REASON"; errcode = retval + ERROR_TABLE_BASE_krb5; goto cleanup; } /* * We pick the session keytype here.... * * Some special care needs to be taken in the user-to-user * case, since we don't know what keytypes the application server * which is doing user-to-user authentication can support. We * know that it at least must be able to support the encryption * type of the session key in the TGT, since otherwise it won't be * able to decrypt the U2U ticket! So we use that in preference * to anything else. */ useenctype = 0; if (isflagset(request->kdc_options, KDC_OPT_ENC_TKT_IN_SKEY)) { krb5_keyblock * st_sealing_key; krb5_kvno st_srv_kvno; krb5_enctype etype; /* * Get the key for the second ticket, and decrypt it. */ if ((errcode = kdc_get_server_key(request->second_ticket[st_idx], &st_sealing_key, &st_srv_kvno))) { status = "2ND_TKT_SERVER"; goto cleanup; } errcode = krb5_decrypt_tkt_part(kdc_context, st_sealing_key, request->second_ticket[st_idx]); krb5_free_keyblock(kdc_context, st_sealing_key); if (errcode) { status = "2ND_TKT_DECRYPT"; goto cleanup; } etype = request->second_ticket[st_idx]->enc_part2->session->enctype; if (!krb5_c_valid_enctype(etype)) { status = "BAD_ETYPE_IN_2ND_TKT"; errcode = KRB5KDC_ERR_ETYPE_NOSUPP; goto cleanup; } for (i = 0; i < request->nktypes; i++) { if (request->ktype[i] == etype) { useenctype = etype; break; } } } /* * Select the keytype for the ticket session key. */ if ((useenctype == 0) && (useenctype = select_session_keytype(kdc_context, &server, request->nktypes, request->ktype)) == 0) { /* unsupported ktype */ status = "BAD_ENCRYPTION_TYPE"; errcode = KRB5KDC_ERR_ETYPE_NOSUPP; goto cleanup; } errcode = krb5_c_make_random_key(kdc_context, useenctype, &session_key); if (errcode) { /* random key failed */ status = "RANDOM_KEY_FAILED"; goto cleanup; } ticket_reply.server = request->server; /* XXX careful for realm... */ enc_tkt_reply.flags = 0; enc_tkt_reply.times.starttime = 0; /* * Fix header_ticket's starttime; if it's zero, fill in the * authtime's value. */ if (!(header_ticket->enc_part2->times.starttime)) header_ticket->enc_part2->times.starttime = header_ticket->enc_part2->times.authtime; /* don't use new addresses unless forwarded, see below */ enc_tkt_reply.caddrs = header_ticket->enc_part2->caddrs; /* noaddrarray[0] = 0; */ reply_encpart.caddrs = 0; /* optional...don't put it in */ /* 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)) setflag(enc_tkt_reply.flags, TKT_FLG_FORWARDABLE); if (isflagset(request->kdc_options, KDC_OPT_FORWARDED)) { setflag(enc_tkt_reply.flags, TKT_FLG_FORWARDED); /* include new addresses in ticket & reply */ enc_tkt_reply.caddrs = request->addresses; reply_encpart.caddrs = request->addresses; } if (isflagset(header_ticket->enc_part2->flags, TKT_FLG_FORWARDED)) setflag(enc_tkt_reply.flags, TKT_FLG_FORWARDED); if (isflagset(request->kdc_options, KDC_OPT_PROXIABLE)) setflag(enc_tkt_reply.flags, TKT_FLG_PROXIABLE); if (isflagset(request->kdc_options, KDC_OPT_PROXY)) { setflag(enc_tkt_reply.flags, TKT_FLG_PROXY); /* include new addresses in ticket & reply */ enc_tkt_reply.caddrs = request->addresses; reply_encpart.caddrs = request->addresses; } if (isflagset(request->kdc_options, KDC_OPT_ALLOW_POSTDATE)) setflag(enc_tkt_reply.flags, TKT_FLG_MAY_POSTDATE); if (isflagset(request->kdc_options, KDC_OPT_POSTDATED)) { setflag(enc_tkt_reply.flags, TKT_FLG_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)) { /* 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_deltat old_life; /* 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_life = enc_tkt_reply.times.endtime - enc_tkt_reply.times.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; until = (request->till == 0) ? kdc_infinity : request->till; enc_tkt_reply.times.endtime = min(until, min(enc_tkt_reply.times.starttime + server.max_life, min(enc_tkt_reply.times.starttime + max_life_for_realm, header_ticket->enc_part2->times.endtime))); if (isflagset(request->kdc_options, KDC_OPT_RENEWABLE_OK) && (enc_tkt_reply.times.endtime < request->till) && isflagset(header_ticket->enc_part2->flags, TKT_FLG_RENEWABLE)) { setflag(request->kdc_options, KDC_OPT_RENEWABLE); request->rtime = min(request->till, header_ticket->enc_part2->times.renew_till); } } rtime = (request->rtime == 0) ? kdc_infinity : request->rtime; if (isflagset(request->kdc_options, KDC_OPT_RENEWABLE)) { /* already checked above in policy check to reject request for a renewable ticket using a non-renewable ticket */ setflag(enc_tkt_reply.flags, TKT_FLG_RENEWABLE); enc_tkt_reply.times.renew_till = min(rtime, min(header_ticket->enc_part2->times.renew_till, enc_tkt_reply.times.starttime + min(server.max_renewable_life, max_renewable_life_for_realm))); } else { enc_tkt_reply.times.renew_till = 0; } /* * Set authtime to be the same as header_ticket's */ enc_tkt_reply.times.authtime = header_ticket->enc_part2->times.authtime; /* * Propagate the preauthentication flags through to the returned ticket. */ if (isflagset(header_ticket->enc_part2->flags, TKT_FLG_PRE_AUTH)) setflag(enc_tkt_reply.flags, TKT_FLG_PRE_AUTH); if (isflagset(header_ticket->enc_part2->flags, TKT_FLG_HW_AUTH)) setflag(enc_tkt_reply.flags, TKT_FLG_HW_AUTH); /* 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; /* assemble any authorization data */ if (request->authorization_data.ciphertext.data) { krb5_data scratch; scratch.length = request->authorization_data.ciphertext.length; if (!(scratch.data = malloc(request->authorization_data.ciphertext.length))) { status = "AUTH_NOMEM"; errcode = ENOMEM; goto cleanup; } if ((errcode = krb5_c_decrypt(kdc_context, header_ticket->enc_part2->session, KRB5_KEYUSAGE_TGS_REQ_AD_SESSKEY, 0, &request->authorization_data, &scratch))) { status = "AUTH_ENCRYPT_FAIL"; free(scratch.data); goto cleanup; } /* scratch now has the authorization data, so we decode it */ errcode = decode_krb5_authdata(&scratch, &(request->unenc_authdata)); free(scratch.data); if (errcode) { status = "AUTH_DECODE"; goto cleanup; } if ((errcode = concat_authorization_data(request->unenc_authdata, header_ticket->enc_part2->authorization_data, &enc_tkt_reply.authorization_data))) { status = "CONCAT_AUTH"; goto cleanup; } } else enc_tkt_reply.authorization_data = header_ticket->enc_part2->authorization_data; enc_tkt_reply.session = &session_key; enc_tkt_reply.client = header_ticket->enc_part2->client; 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 (realm_compare(header_ticket->server, tgs_server) || realm_compare(header_ticket->server, enc_tkt_reply.client)) { /* tgt issued by local realm or issued by realm of client */ enc_tkt_reply.transited = header_ticket->enc_part2->transited; } else { /* tgt issued by some other realm and not the realm of the client */ /* assemble new transited field into allocated storage */ if (header_ticket->enc_part2->transited.tr_type != KRB5_DOMAIN_X500_COMPRESS) { status = "BAD_TRTYPE"; errcode = KRB5KDC_ERR_TRTYPE_NOSUPP; goto cleanup; } enc_tkt_transited.tr_type = KRB5_DOMAIN_X500_COMPRESS; enc_tkt_transited.magic = 0; enc_tkt_transited.tr_contents.magic = 0; enc_tkt_transited.tr_contents.data = 0; enc_tkt_transited.tr_contents.length = 0; enc_tkt_reply.transited = enc_tkt_transited; if ((errcode = add_to_transited(&header_ticket->enc_part2->transited.tr_contents, &enc_tkt_reply.transited.tr_contents, header_ticket->server, enc_tkt_reply.client, request->server))) { status = "ADD_TR_FAIL"; goto cleanup; } newtransited = 1; } if (!isflagset (request->kdc_options, KDC_OPT_DISABLE_TRANSITED_CHECK)) { unsigned int tlen; char *tdots; errcode = krb5_check_transited_list (kdc_context, &enc_tkt_reply.transited.tr_contents, krb5_princ_realm (kdc_context, header_ticket->enc_part2->client), krb5_princ_realm (kdc_context, request->server)); tlen = enc_tkt_reply.transited.tr_contents.length; tdots = tlen > 125 ? "..." : ""; tlen = tlen > 125 ? 125 : tlen; if (errcode == 0) { setflag (enc_tkt_reply.flags, TKT_FLG_TRANSIT_POLICY_CHECKED); } else if (errcode == KRB5KRB_AP_ERR_ILL_CR_TKT) krb5_klog_syslog (LOG_INFO, "bad realm transit path from '%s' to '%s' " "via '%.*s%s'", cname ? cname : "<unknown client>", sname ? sname : "<unknown server>", tlen, enc_tkt_reply.transited.tr_contents.data, tdots); else { const char *emsg = krb5_get_error_message(kdc_context, errcode); krb5_klog_syslog (LOG_ERR, "unexpected error checking transit from " "'%s' to '%s' via '%.*s%s': %s", cname ? cname : "<unknown client>", sname ? sname : "<unknown server>", tlen, enc_tkt_reply.transited.tr_contents.data, tdots, emsg); krb5_free_error_message(kdc_context, emsg); } } else krb5_klog_syslog (LOG_INFO, "not checking transit path"); if (reject_bad_transit && !isflagset (enc_tkt_reply.flags, TKT_FLG_TRANSIT_POLICY_CHECKED)) { errcode = KRB5KDC_ERR_POLICY; status = "BAD_TRANSIT"; 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)) { if ((errcode = krb5_unparse_name(kdc_context, client2, &tmp))) tmp = 0; if (tmp != NULL) limit_string(tmp); krb5_klog_syslog(LOG_INFO, "TGS_REQ %s: 2ND_TKT_MISMATCH: " "authtime %d, %s for %s, 2nd tkt client %s", fromstring, authtime, cname ? cname : "<unknown client>", sname ? sname : "<unknown server>", tmp ? tmp : "<unknown>"); errcode = KRB5KDC_ERR_SERVER_NOMATCH; goto cleanup; } ticket_reply.enc_part.kvno = 0; ticket_reply.enc_part.enctype = t2enc->session->enctype; if ((errcode = krb5_encrypt_tkt_part(kdc_context, t2enc->session, &ticket_reply))) { status = "2ND_TKT_ENCRYPT"; goto cleanup; } st_idx++; } 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_dbekd_decrypt_key_data(kdc_context, &master_keyblock, server_key, &encrypting_key, NULL))) { status = "DECRYPT_SERVER_KEY"; goto cleanup; } errcode = krb5_encrypt_tkt_part(kdc_context, &encrypting_key, &ticket_reply); krb5_free_keyblock_contents(kdc_context, &encrypting_key); if (errcode) { status = "TKT_ENCRYPT"; goto cleanup; } ticket_reply.enc_part.kvno = server_key->key_data_kvno; } /* Start assembling the response */ reply.msg_type = KRB5_TGS_REP; reply.padata = 0; /* always */ reply.client = header_ticket->enc_part2->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 EXCEPT for authtime; its location is used for ktime */ reply_encpart.times = enc_tkt_reply.times; reply_encpart.times.authtime = header_ticket->enc_part2->times.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; nolrentry.lr_type = KRB5_LRQ_NONE; nolrentry.value = 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 = krb5_encode_kdc_rep(kdc_context, KRB5_TGS_REP, &reply_encpart, subkey ? 1 : 0, subkey ? subkey : header_ticket->enc_part2->session, &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: if (status) { const char * emsg = NULL; if (!errcode) rep_etypes2str(rep_etypestr, sizeof(rep_etypestr), &reply); if (errcode) emsg = krb5_get_error_message (kdc_context, errcode); krb5_klog_syslog(LOG_INFO, "TGS_REQ (%s) %s: %s: authtime %d, " "%s%s %s for %s%s%s", ktypestr, fromstring, status, authtime, !errcode ? rep_etypestr : "", !errcode ? "," : "", cname ? cname : "<unknown client>", sname ? sname : "<unknown server>", errcode ? ", " : "", errcode ? emsg : ""); if (errcode) krb5_free_error_message (kdc_context, emsg); } 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 > 128) errcode = KRB_ERR_GENERIC; retval = prepare_error_tgs(request, header_ticket, errcode, fromstring, response, status); if (got_err) { krb5_free_error_message (kdc_context, status); status = 0; } } if (header_ticket) krb5_free_ticket(kdc_context, header_ticket); if (request) krb5_free_kdc_req(kdc_context, request); if (cname) free(cname); if (sname) free(sname); if (nprincs) krb5_db_free_principal(kdc_context, &server, 1); if (session_key.contents) krb5_free_keyblock_contents(kdc_context, &session_key); if (newtransited) free(enc_tkt_reply.transited.tr_contents.data); if (subkey) krb5_free_keyblock(kdc_context, subkey); return retval; }
static krb5_error_code kpasswd_set_password(struct kdc_server *kdc, TALLOC_CTX *mem_ctx, struct auth_session_info *session_info, DATA_BLOB *decoded_data, DATA_BLOB *kpasswd_reply, const char **error_string) { krb5_context context = kdc->smb_krb5_context->krb5_context; krb5_data k_dec_data; krb5_data *k_clear_data; krb5_principal target_principal; krb5_error_code code; DATA_BLOB password; char *target_realm = NULL; char *target_name = NULL; char *target_principal_string = NULL; bool is_service_principal = false; bool ok; size_t num_components; enum samPwdChangeReason reject_reason = SAM_PWD_CHANGE_NO_ERROR; struct samr_DomInfo1 *dominfo = NULL; NTSTATUS status; k_dec_data.length = decoded_data->length; k_dec_data.data = (char *)decoded_data->data; code = decode_krb5_setpw_req(&k_dec_data, &k_clear_data, &target_principal); if (code != 0) { DBG_WARNING("decode_krb5_setpw_req failed: %s\n", error_message(code)); ok = kpasswd_make_error_reply(mem_ctx, KRB5_KPASSWD_MALFORMED, "Failed to decode packet", kpasswd_reply); if (!ok) { *error_string = "Failed to create reply"; return KRB5_KPASSWD_HARDERROR; } return 0; } ok = convert_string_talloc_handle(mem_ctx, lpcfg_iconv_handle(kdc->task->lp_ctx), CH_UTF8, CH_UTF16, (const char *)k_clear_data->data, k_clear_data->length, (void **)&password.data, &password.length); krb5_free_data(context, k_clear_data); if (!ok) { DBG_WARNING("String conversion failed\n"); *error_string = "String conversion failed"; return KRB5_KPASSWD_HARDERROR; } target_realm = smb_krb5_principal_get_realm(context, target_principal); code = krb5_unparse_name_flags(context, target_principal, KRB5_PRINCIPAL_UNPARSE_NO_REALM, &target_name); if (code != 0) { DBG_WARNING("Failed to parse principal\n"); *error_string = "String conversion failed"; return KRB5_KPASSWD_HARDERROR; } if ((target_name != NULL && target_realm == NULL) || (target_name == NULL && target_realm != NULL)) { krb5_free_principal(context, target_principal); SAFE_FREE(target_realm); SAFE_FREE(target_name); ok = kpasswd_make_error_reply(mem_ctx, KRB5_KPASSWD_MALFORMED, "Realm and principal must be " "both present, or neither " "present", kpasswd_reply); if (!ok) { *error_string = "Failed to create reply"; return KRB5_KPASSWD_HARDERROR; } return 0; } if (target_name != NULL && target_realm != NULL) { SAFE_FREE(target_realm); SAFE_FREE(target_name); } else { krb5_free_principal(context, target_principal); SAFE_FREE(target_realm); SAFE_FREE(target_name); return kpasswd_change_password(kdc, mem_ctx, session_info, &password, kpasswd_reply, error_string); } num_components = krb5_princ_size(context, target_principal); if (num_components >= 2) { is_service_principal = true; code = krb5_unparse_name_flags(context, target_principal, KRB5_PRINCIPAL_UNPARSE_SHORT, &target_principal_string); } else { code = krb5_unparse_name(context, target_principal, &target_principal_string); } krb5_free_principal(context, target_principal); if (code != 0) { ok = kpasswd_make_error_reply(mem_ctx, KRB5_KPASSWD_MALFORMED, "Failed to parse principal", kpasswd_reply); if (!ok) { *error_string = "Failed to create reply"; return KRB5_KPASSWD_HARDERROR; } } status = kpasswd_samdb_set_password(mem_ctx, kdc->task->event_ctx, kdc->task->lp_ctx, session_info, is_service_principal, target_principal_string, &password, &reject_reason, &dominfo); if (!NT_STATUS_IS_OK(status)) { DBG_ERR("kpasswd_samdb_set_password failed - %s\n", nt_errstr(status)); } ok = kpasswd_make_pwchange_reply(mem_ctx, status, reject_reason, dominfo, kpasswd_reply); if (!ok) { *error_string = "Failed to create reply"; return KRB5_KPASSWD_HARDERROR; } return 0; }
/* check that the SPN update should be allowed as an override via sam_ctx_system This is only called if the client is not a domain controller or administrator */ static bool writespn_check_spn(struct drsuapi_bind_state *b_state, struct dcesrv_call_state *dce_call, struct ldb_dn *dn, const char *spn) { /* * we only allow SPN updates if: * * 1) they are on the clients own account object * 2) they are of the form SERVICE/dnshostname */ struct dom_sid *user_sid, *sid; TALLOC_CTX *tmp_ctx = talloc_new(dce_call); struct ldb_result *res; const char *attrs[] = { "objectSID", "dNSHostName", NULL }; int ret; krb5_context krb_ctx; krb5_error_code kerr; krb5_principal principal; const krb5_data *component; const char *dns_name, *dnsHostName; /* The service principal name shouldn't be NULL */ if (spn == NULL) { talloc_free(tmp_ctx); return false; } /* get the objectSid of the DN that is being modified, and check it matches the user_sid in their token */ ret = dsdb_search_dn(b_state->sam_ctx, tmp_ctx, &res, dn, attrs, DSDB_SEARCH_ONE_ONLY); if (ret != LDB_SUCCESS) { talloc_free(tmp_ctx); return false; } user_sid = &dce_call->conn->auth_state.session_info->security_token->sids[PRIMARY_USER_SID_INDEX]; sid = samdb_result_dom_sid(tmp_ctx, res->msgs[0], "objectSid"); if (sid == NULL) { talloc_free(tmp_ctx); return false; } dnsHostName = ldb_msg_find_attr_as_string(res->msgs[0], "dNSHostName", NULL); if (dnsHostName == NULL) { talloc_free(tmp_ctx); return false; } if (!dom_sid_equal(sid, user_sid)) { talloc_free(tmp_ctx); return false; } kerr = smb_krb5_init_context_basic(tmp_ctx, dce_call->conn->dce_ctx->lp_ctx, &krb_ctx); if (kerr != 0) { talloc_free(tmp_ctx); return false; } ret = krb5_parse_name_flags(krb_ctx, spn, KRB5_PRINCIPAL_PARSE_NO_REALM, &principal); if (kerr != 0) { krb5_free_context(krb_ctx); talloc_free(tmp_ctx); return false; } if (krb5_princ_size(krb_ctx, principal) != 2) { krb5_free_principal(krb_ctx, principal); krb5_free_context(krb_ctx); talloc_free(tmp_ctx); return false; } component = krb5_princ_component(krb_ctx, principal, 1); dns_name = (const char *)component->data; if (strcasecmp(dns_name, dnsHostName) != 0) { krb5_free_principal(krb_ctx, principal); krb5_free_context(krb_ctx); talloc_free(tmp_ctx); return false; } /* its a simple update on their own account - allow it with * permissions override */ krb5_free_principal(krb_ctx, principal); krb5_free_context(krb_ctx); talloc_free(tmp_ctx); return true; }
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 */ }
static int check_iprop_rpcsec_auth(struct svc_req *rqstp) { /* XXX Since the client can authenticate against any principal in the database, we need to do a sanity check. Only checking for "kiprop" now, but that means theoretically the client could be authenticating to kiprop on some other machine. */ /* Code taken from kadm_rpc_svc.c, tweaked. */ gss_ctx_id_t ctx; krb5_context kctx; OM_uint32 maj_stat, min_stat; gss_name_t name; krb5_principal princ; int ret, success; krb5_data *c1, *c2, *realm; gss_buffer_desc gss_str; kadm5_server_handle_t handle; size_t slen; char *sdots; success = 0; handle = (kadm5_server_handle_t)global_server_handle; if (rqstp->rq_cred.oa_flavor != RPCSEC_GSS) return 0; ctx = rqstp->rq_svccred; maj_stat = gss_inquire_context(&min_stat, ctx, NULL, &name, NULL, NULL, NULL, NULL, NULL); if (maj_stat != GSS_S_COMPLETE) { krb5_klog_syslog(LOG_ERR, "check_rpcsec_auth: " "failed inquire_context, stat=%u", maj_stat); log_badauth(maj_stat, min_stat, &rqstp->rq_xprt->xp_raddr, NULL); goto fail_name; } kctx = handle->context; ret = gss_to_krb5_name_1(rqstp, kctx, name, &princ, &gss_str); if (ret == 0) goto fail_name; slen = gss_str.length; trunc_name(&slen, &sdots); /* * Since we accept with GSS_C_NO_NAME, the client can authenticate * against the entire kdb. Therefore, ensure that the service * name is something reasonable. */ if (krb5_princ_size(kctx, princ) != 2) goto fail_princ; c1 = krb5_princ_component(kctx, princ, 0); c2 = krb5_princ_component(kctx, princ, 1); realm = krb5_princ_realm(kctx, princ); if (strncmp(handle->params.realm, realm->data, realm->length) == 0 && strncmp("kiprop", c1->data, c1->length) == 0) { success = 1; } fail_princ: if (!success) { krb5_klog_syslog(LOG_ERR, "bad service principal %.*s%s", (int) slen, (char *) gss_str.value, sdots); } gss_release_buffer(&min_stat, &gss_str); krb5_free_principal(kctx, princ); fail_name: gss_release_name(&min_stat, &name); return success; }
/* * May the fleas of a thousand camels infest the ISO, they who think * that arbitrarily large multi-component names are a Good Thing..... */ static krb5_error_code k5_parse_name(krb5_context context, const char *name, int flags, krb5_principal *nprincipal) { register const char *cp; register char *q; register int i,c,size; int components = 0; const char *parsed_realm = NULL; int fcompsize[FCOMPNUM]; unsigned int realmsize = 0; char *default_realm = NULL; int default_realm_size = 0; char *tmpdata; krb5_principal principal; krb5_error_code retval; unsigned int enterprise = (flags & KRB5_PRINCIPAL_PARSE_ENTERPRISE); int first_at; /* * Pass 1. Find out how many components there are to the name, * and get string sizes for the first FCOMPNUM components. For * enterprise principal names (UPNs), there is only a single * component. */ size = 0; for (i=0,cp = name, first_at = 1; (c = *cp); cp++) { if (c == QUOTECHAR) { cp++; if (!(c = *cp)) /* * QUOTECHAR can't be at the last * character of the name! */ return(KRB5_PARSE_MALFORMED); size++; continue; } else if (c == COMPONENT_SEP && !enterprise) { if (parsed_realm) /* * Shouldn't see a component separator * after we've parsed out the realm name! */ return(KRB5_PARSE_MALFORMED); if (i < FCOMPNUM) { fcompsize[i] = size; } size = 0; i++; } else if (c == REALM_SEP && (!enterprise || !first_at)) { if (parsed_realm) /* * Multiple realm separaters * not allowed; zero-length realms are. */ return(KRB5_PARSE_MALFORMED); parsed_realm = cp + 1; if (i < FCOMPNUM) { fcompsize[i] = size; } size = 0; } else { if (c == REALM_SEP && enterprise && first_at) first_at = 0; size++; } } if (parsed_realm != NULL) realmsize = size; else if (i < FCOMPNUM) fcompsize[i] = size; components = i + 1; /* * Now, we allocate the principal structure and all of its * component pieces */ principal = (krb5_principal)malloc(sizeof(krb5_principal_data)); if (principal == NULL) { return(ENOMEM); } principal->data = (krb5_data *) malloc(sizeof(krb5_data) * components); if (principal->data == NULL) { krb5_xfree((char *)principal); return ENOMEM; } principal->length = components; /* * If a realm was not found, then use the default realm, unless * KRB5_PRINCIPAL_PARSE_NO_REALM was specified in which case the * realm will be empty. */ if (!parsed_realm) { if (flags & KRB5_PRINCIPAL_PARSE_REQUIRE_REALM) { krb5_set_error_message(context, KRB5_PARSE_MALFORMED, "Principal %s is missing required realm", name); krb5_xfree(principal->data); krb5_xfree(principal); return KRB5_PARSE_MALFORMED; } if (!default_realm && (flags & KRB5_PRINCIPAL_PARSE_NO_REALM) == 0) { retval = krb5_get_default_realm(context, &default_realm); if (retval) { krb5_xfree(principal->data); krb5_xfree((char *)principal); return(retval); } default_realm_size = strlen(default_realm); } realmsize = default_realm_size; } else if (flags & KRB5_PRINCIPAL_PARSE_NO_REALM) { krb5_set_error_message(context, KRB5_PARSE_MALFORMED, "Principal %s has realm present", name); krb5_xfree(principal->data); krb5_xfree(principal); return KRB5_PARSE_MALFORMED; } /* * Pass 2. Happens only if there were more than FCOMPNUM * component; if this happens, someone should be shot * immediately. Nevertheless, we will attempt to handle said * case..... <martyred sigh> */ if (components >= FCOMPNUM) { size = 0; parsed_realm = NULL; for (i=0,cp = name; (c = *cp); cp++) { if (c == QUOTECHAR) { cp++; size++; } else if (c == COMPONENT_SEP) { if (krb5_princ_size(context, principal) > i) krb5_princ_component(context, principal, i)->length = size; size = 0; i++; } else if (c == REALM_SEP) { if (krb5_princ_size(context, principal) > i) krb5_princ_component(context, principal, i)->length = size; size = 0; parsed_realm = cp+1; } else size++; } if (parsed_realm) krb5_princ_realm(context, principal)->length = size; else if (krb5_princ_size(context, principal) > i) krb5_princ_component(context, principal, i)->length = size; if (i + 1 != components) { #if !defined(_WIN32) fprintf(stderr, "Programming error in krb5_parse_name!"); #endif assert(i + 1 == components); abort(); } } else { /* * If there were fewer than FCOMPSIZE components (the * usual case), then just copy the sizes to the * principal structure */ for (i=0; i < components; i++) krb5_princ_component(context, principal, i)->length = fcompsize[i]; } /* * Now, we need to allocate the space for the strings themselves..... */ tmpdata = malloc(realmsize + 1); if (tmpdata == 0) { krb5_xfree(principal->data); krb5_xfree(principal); krb5_xfree(default_realm); return ENOMEM; } krb5_princ_set_realm_length(context, principal, realmsize); krb5_princ_set_realm_data(context, principal, tmpdata); for (i=0; i < components; i++) { char *tmpdata2 = malloc(krb5_princ_component(context, principal, i)->length + 1); if (tmpdata2 == NULL) { for (i--; i >= 0; i--) krb5_xfree(krb5_princ_component(context, principal, i)->data); krb5_xfree(krb5_princ_realm(context, principal)->data); krb5_xfree(principal->data); krb5_xfree(principal); krb5_xfree(default_realm); return(ENOMEM); } krb5_princ_component(context, principal, i)->data = tmpdata2; krb5_princ_component(context, principal, i)->magic = KV5M_DATA; } /* * Pass 3. Now we go through the string a *third* time, this * time filling in the krb5_principal structure which we just * allocated. */ q = krb5_princ_component(context, principal, 0)->data; for (i=0,cp = name, first_at = 1; (c = *cp); cp++) { if (c == QUOTECHAR) { cp++; switch (c = *cp) { case 'n': *q++ = '\n'; break; case 't': *q++ = '\t'; break; case 'b': *q++ = '\b'; break; case '0': *q++ = '\0'; break; default: *q++ = c; break; } } else if (c == COMPONENT_SEP && !enterprise) { i++; *q++ = '\0'; q = krb5_princ_component(context, principal, i)->data; } else if (c == REALM_SEP && (!enterprise || !first_at)) { i++; *q++ = '\0'; q = krb5_princ_realm(context, principal)->data; } else { if (c == REALM_SEP && enterprise && first_at) first_at = 0; *q++ = c; } } *q++ = '\0'; if (!parsed_realm) { if (flags & KRB5_PRINCIPAL_PARSE_NO_REALM) (krb5_princ_realm(context, principal)->data)[0] = '\0'; else strlcpy(krb5_princ_realm(context, principal)->data, default_realm, realmsize+1); } /* * Alright, we're done. Now stuff a pointer to this monstrosity * into the return variable, and let's get out of here. */ if (enterprise) krb5_princ_type(context, principal) = KRB5_NT_ENTERPRISE_PRINCIPAL; else krb5_princ_type(context, principal) = KRB5_NT_PRINCIPAL; principal->magic = KV5M_PRINCIPAL; principal->realm.magic = KV5M_DATA; *nprincipal = principal; if (default_realm != NULL) krb5_xfree(default_realm); return(0); }
static krb5_int32 prep_reprocess_req(krb5_kdc_req *request, krb5_principal *krbtgt_princ) { krb5_error_code retval = KRB5KRB_AP_ERR_BADMATCH; size_t len = 0; char **realms, **cpp, *temp_buf=NULL; krb5_data *comp1 = NULL, *comp2 = NULL; char *comp1_str = NULL; /* By now we know that server principal name is unknown. * If CANONICALIZE flag is set in the request * If req is not U2U authn. req * the requested server princ. has exactly two components * either * the name type is NT-SRV-HST * or name type is NT-UNKNOWN and * the 1st component is listed in conf file under host_based_services * the 1st component is not in a list in conf under "no_host_referral" * the 2d component looks like fully-qualified domain name (FQDN) * If all of these conditions are satisfied - try mapping the FQDN and * re-process the request as if client had asked for cross-realm TGT. */ if (isflagset(request->kdc_options, KDC_OPT_CANONICALIZE) && !isflagset(request->kdc_options, KDC_OPT_ENC_TKT_IN_SKEY) && krb5_princ_size(kdc_context, request->server) == 2) { comp1 = krb5_princ_component(kdc_context, request->server, 0); comp2 = krb5_princ_component(kdc_context, request->server, 1); comp1_str = calloc(1,comp1->length+1); if (!comp1_str) { retval = ENOMEM; goto cleanup; } strlcpy(comp1_str,comp1->data,comp1->length+1); if ((krb5_princ_type(kdc_context, request->server) == KRB5_NT_SRV_HST || (krb5_princ_type(kdc_context, request->server) == KRB5_NT_UNKNOWN && kdc_active_realm->realm_host_based_services != NULL && (krb5_match_config_pattern(kdc_active_realm->realm_host_based_services, comp1_str) == TRUE || krb5_match_config_pattern(kdc_active_realm->realm_host_based_services, KRB5_CONF_ASTERISK) == TRUE))) && (kdc_active_realm->realm_no_host_referral == NULL || (krb5_match_config_pattern(kdc_active_realm->realm_no_host_referral, KRB5_CONF_ASTERISK) == FALSE && krb5_match_config_pattern(kdc_active_realm->realm_no_host_referral, comp1_str) == FALSE))) { for (len=0; len < comp2->length; len++) { if (comp2->data[len] == '.') break; } if (len == comp2->length) goto cleanup; temp_buf = calloc(1, comp2->length+1); if (!temp_buf){ retval = ENOMEM; goto cleanup; } strlcpy(temp_buf, comp2->data,comp2->length+1); retval = krb5int_get_domain_realm_mapping(kdc_context, temp_buf, &realms); free(temp_buf); if (retval) { /* no match found */ kdc_err(kdc_context, retval, 0); goto cleanup; } if (realms == 0) { retval = KRB5KRB_AP_ERR_BADMATCH; goto cleanup; } if (realms[0] == 0) { free(realms); retval = KRB5KRB_AP_ERR_BADMATCH; goto cleanup; } /* Modify request. * Construct cross-realm tgt : krbtgt/REMOTE_REALM@LOCAL_REALM * and use it as a principal in this req. */ retval = krb5_build_principal(kdc_context, krbtgt_princ, (*request->server).realm.length, (*request->server).realm.data, "krbtgt", realms[0], (char *)0); for (cpp = realms; *cpp; cpp++) free(*cpp); } } cleanup: free(comp1_str); return retval; }
/** * @brief * Get a TGT for use at the remote host. * * @param[in] context - The Kerberos context. * @param[in] auth_context - Authentication context * @param[in] client - Principal to be copied * @param[in] server - Server of type krb5_principal * @param[in] cc - Credential cache handle * @param[out] outbuf - Replay cache data (NULL if not needed) * * @return krb5_error_code * @retval 0 - success * @retval !=0 - failure */ static krb5_error_code fwd_tgt_creds(krb5_context context, krb5_auth_context auth_context, krb5_principal client, krb5_principal server, krb5_ccache cc, krb5_data outbuf) { krb5_replay_data replaydata; krb5_data *scratch = 0; krb5_address **addrs = 0; krb5_flags kdcoptions; krb5_error_code retval; krb5_creds creds, tgt; krb5_creds *pcreds; int free_rhost = 0; char *rhost; memset((char *)&creds, 0, sizeof(creds)); memset((char *)&tgt, 0, sizeof(creds)); if (krb5_princ_type(context, server) != KRB5_NT_SRV_HST) return KRB5_FWD_BAD_PRINCIPAL; if (krb5_princ_size(context, server) < 2) return KRB5_CC_BADNAME; rhost = malloc(server->data[1].length+1); if (!rhost) return ENOMEM; free_rhost = 1; 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; 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 */ retval = krb5_cc_retrieve_cred(context, cc, KRB5_TC_SUPPORTED_KTYPES, &creds, &tgt); if (retval) goto errout; /* tgt->client must be equal to creds.client */ if (!krb5_principal_compare(context, tgt.client, creds.client)) { retval = KRB5_PRINC_NOMATCH; goto errout; } if (!tgt.ticket.length) { retval = KRB5_NO_TKT_SUPPLIED; goto errout; } kdcoptions = flags2options(tgt.ticket_flags)| KDC_OPT_FORWARDED; if ((retval = krb5_get_cred_via_tkt(context, &tgt, kdcoptions, addrs, &creds, &pcreds))) goto errout; retval = krb5_mk_1cred(context, auth_context, pcreds, &scratch, &replaydata); krb5_free_creds(context, pcreds); if (retval) { if (scratch) krb5_free_data(context, scratch); } else { *outbuf = *scratch; free(scratch); } errout: if (addrs) krb5_free_addresses(context, addrs); if (free_rhost) free(rhost); krb5_free_cred_contents(context, &creds); krb5_free_cred_contents(context, &tgt); return retval; }
/* 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; }
WERROR DsCrackNameOneName(struct ldb_context *sam_ctx, TALLOC_CTX *mem_ctx, uint32_t format_flags, enum drsuapi_DsNameFormat format_offered, enum drsuapi_DsNameFormat format_desired, const char *name, struct drsuapi_DsNameInfo1 *info1) { krb5_error_code ret; const char *domain_filter = NULL; const char *result_filter = NULL; struct ldb_dn *name_dn = NULL; struct ldb_dn *search_dn = NULL; struct smb_krb5_context *smb_krb5_context = NULL; int scope = LDB_SCOPE_SUBTREE; info1->status = DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR; info1->dns_domain_name = NULL; info1->result_name = NULL; if (!name) { return WERR_INVALID_PARAM; } /* TODO: - fill the correct names in all cases! * - handle format_flags */ if (format_desired == DRSUAPI_DS_NAME_FORMAT_UNKNOWN) { return WERR_OK; } /* here we need to set the domain_filter and/or the result_filter */ switch (format_offered) { case DRSUAPI_DS_NAME_FORMAT_UNKNOWN: { unsigned int i; enum drsuapi_DsNameFormat formats[] = { DRSUAPI_DS_NAME_FORMAT_FQDN_1779, DRSUAPI_DS_NAME_FORMAT_USER_PRINCIPAL, DRSUAPI_DS_NAME_FORMAT_NT4_ACCOUNT, DRSUAPI_DS_NAME_FORMAT_CANONICAL, DRSUAPI_DS_NAME_FORMAT_GUID, DRSUAPI_DS_NAME_FORMAT_DISPLAY, DRSUAPI_DS_NAME_FORMAT_SERVICE_PRINCIPAL, DRSUAPI_DS_NAME_FORMAT_SID_OR_SID_HISTORY, DRSUAPI_DS_NAME_FORMAT_CANONICAL_EX }; WERROR werr; for (i=0; i < ARRAY_SIZE(formats); i++) { werr = DsCrackNameOneName(sam_ctx, mem_ctx, format_flags, formats[i], format_desired, name, info1); if (!W_ERROR_IS_OK(werr)) { return werr; } if (info1->status != DRSUAPI_DS_NAME_STATUS_NOT_FOUND && (formats[i] != DRSUAPI_DS_NAME_FORMAT_CANONICAL || info1->status != DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR)) { return werr; } } return werr; } case DRSUAPI_DS_NAME_FORMAT_CANONICAL: case DRSUAPI_DS_NAME_FORMAT_CANONICAL_EX: { char *str, *s, *account; scope = LDB_SCOPE_ONELEVEL; if (strlen(name) == 0) { info1->status = DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR; return WERR_OK; } str = talloc_strdup(mem_ctx, name); W_ERROR_HAVE_NO_MEMORY(str); if (format_offered == DRSUAPI_DS_NAME_FORMAT_CANONICAL_EX) { /* Look backwards for the \n, and replace it with / */ s = strrchr(str, '\n'); if (!s) { info1->status = DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR; return WERR_OK; } s[0] = '/'; } s = strchr(str, '/'); if (!s) { /* there must be at least one / */ info1->status = DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR; return WERR_OK; } s[0] = '\0'; s++; domain_filter = talloc_asprintf(mem_ctx, "(&(objectClass=crossRef)(dnsRoot=%s)(systemFlags:%s:=%u))", ldb_binary_encode_string(mem_ctx, str), LDB_OID_COMPARATOR_AND, SYSTEM_FLAG_CR_NTDS_DOMAIN); W_ERROR_HAVE_NO_MEMORY(domain_filter); /* There may not be anything after the domain component (search for the domain itself) */ account = s; if (account && *account) { WERROR werr = get_format_functional_filtering_param(sam_ctx, mem_ctx, account, info1, &search_dn, domain_filter, &result_filter); if (!W_ERROR_IS_OK(werr)) { return werr; } if (info1->status != DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR) return WERR_OK; } break; } case DRSUAPI_DS_NAME_FORMAT_NT4_ACCOUNT: { char *p; char *domain; const char *account = NULL; domain = talloc_strdup(mem_ctx, name); W_ERROR_HAVE_NO_MEMORY(domain); p = strchr(domain, '\\'); if (!p) { /* invalid input format */ info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND; return WERR_OK; } p[0] = '\0'; if (p[1]) { account = &p[1]; } domain_filter = talloc_asprintf(mem_ctx, "(&(objectClass=crossRef)(netbiosName=%s)(systemFlags:%s:=%u))", ldb_binary_encode_string(mem_ctx, domain), LDB_OID_COMPARATOR_AND, SYSTEM_FLAG_CR_NTDS_DOMAIN); W_ERROR_HAVE_NO_MEMORY(domain_filter); if (account) { result_filter = talloc_asprintf(mem_ctx, "(sAMAccountName=%s)", ldb_binary_encode_string(mem_ctx, account)); W_ERROR_HAVE_NO_MEMORY(result_filter); } talloc_free(domain); break; } /* A LDAP DN as a string */ case DRSUAPI_DS_NAME_FORMAT_FQDN_1779: { domain_filter = NULL; name_dn = ldb_dn_new(mem_ctx, sam_ctx, name); if (! ldb_dn_validate(name_dn)) { info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND; return WERR_OK; } break; } /* A GUID as a string */ case DRSUAPI_DS_NAME_FORMAT_GUID: { struct GUID guid; char *ldap_guid; NTSTATUS nt_status; domain_filter = NULL; nt_status = GUID_from_string(name, &guid); if (!NT_STATUS_IS_OK(nt_status)) { info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND; return WERR_OK; } ldap_guid = ldap_encode_ndr_GUID(mem_ctx, &guid); if (!ldap_guid) { return WERR_NOMEM; } result_filter = talloc_asprintf(mem_ctx, "(objectGUID=%s)", ldap_guid); W_ERROR_HAVE_NO_MEMORY(result_filter); break; } case DRSUAPI_DS_NAME_FORMAT_DISPLAY: { domain_filter = NULL; result_filter = talloc_asprintf(mem_ctx, "(|(displayName=%s)(samAccountName=%s))", ldb_binary_encode_string(mem_ctx, name), ldb_binary_encode_string(mem_ctx, name)); W_ERROR_HAVE_NO_MEMORY(result_filter); break; } /* A S-1234-5678 style string */ case DRSUAPI_DS_NAME_FORMAT_SID_OR_SID_HISTORY: { struct dom_sid *sid = dom_sid_parse_talloc(mem_ctx, name); char *ldap_sid; domain_filter = NULL; if (!sid) { info1->dns_domain_name = NULL; info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND; return WERR_OK; } ldap_sid = ldap_encode_ndr_dom_sid(mem_ctx, sid); if (!ldap_sid) { return WERR_NOMEM; } result_filter = talloc_asprintf(mem_ctx, "(objectSid=%s)", ldap_sid); W_ERROR_HAVE_NO_MEMORY(result_filter); break; } case DRSUAPI_DS_NAME_FORMAT_USER_PRINCIPAL: { krb5_principal principal; char *unparsed_name; ret = smb_krb5_init_context(mem_ctx, (struct loadparm_context *)ldb_get_opaque(sam_ctx, "loadparm"), &smb_krb5_context); if (ret) { return WERR_NOMEM; } /* Ensure we reject compleate junk first */ ret = krb5_parse_name(smb_krb5_context->krb5_context, name, &principal); if (ret) { info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND; return WERR_OK; } domain_filter = NULL; /* By getting the unparsed name here, we ensure the escaping is correct (and trust the client less) */ ret = krb5_unparse_name(smb_krb5_context->krb5_context, principal, &unparsed_name); if (ret) { krb5_free_principal(smb_krb5_context->krb5_context, principal); return WERR_NOMEM; } krb5_free_principal(smb_krb5_context->krb5_context, principal); /* The ldb_binary_encode_string() here avoid LDAP filter injection attacks */ result_filter = talloc_asprintf(mem_ctx, "(&(objectClass=user)(userPrincipalName=%s))", ldb_binary_encode_string(mem_ctx, unparsed_name)); free(unparsed_name); W_ERROR_HAVE_NO_MEMORY(result_filter); break; } case DRSUAPI_DS_NAME_FORMAT_SERVICE_PRINCIPAL: { krb5_principal principal; char *unparsed_name_short; const krb5_data *component; char *service; ret = smb_krb5_init_context(mem_ctx, (struct loadparm_context *)ldb_get_opaque(sam_ctx, "loadparm"), &smb_krb5_context); if (ret) { return WERR_NOMEM; } ret = krb5_parse_name(smb_krb5_context->krb5_context, name, &principal); if (ret == 0 && krb5_princ_size(smb_krb5_context->krb5_context, principal) < 2) { info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND; krb5_free_principal(smb_krb5_context->krb5_context, principal); return WERR_OK; } else if (ret == 0) { krb5_free_principal(smb_krb5_context->krb5_context, principal); } ret = krb5_parse_name_flags(smb_krb5_context->krb5_context, name, KRB5_PRINCIPAL_PARSE_NO_REALM, &principal); if (ret) { return dns_domain_from_principal(mem_ctx, smb_krb5_context, name, info1); } domain_filter = NULL; ret = krb5_unparse_name_flags(smb_krb5_context->krb5_context, principal, KRB5_PRINCIPAL_UNPARSE_NO_REALM, &unparsed_name_short); if (ret) { krb5_free_principal(smb_krb5_context->krb5_context, principal); return WERR_NOMEM; } component = krb5_princ_component(smb_krb5_context->krb5_context, principal, 0); service = (char *)component->data; if ((krb5_princ_size(smb_krb5_context->krb5_context, principal) == 2) && (strcasecmp(service, "host") == 0)) { /* the 'cn' attribute is just the leading part of the name */ char *computer_name; component = krb5_princ_component( smb_krb5_context->krb5_context, principal, 1); computer_name = talloc_strndup(mem_ctx, (char *)component->data, strcspn((char *)component->data, ".")); if (computer_name == NULL) { krb5_free_principal(smb_krb5_context->krb5_context, principal); free(unparsed_name_short); return WERR_NOMEM; } result_filter = talloc_asprintf(mem_ctx, "(|(&(servicePrincipalName=%s)(objectClass=user))(&(cn=%s)(objectClass=computer)))", ldb_binary_encode_string(mem_ctx, unparsed_name_short), ldb_binary_encode_string(mem_ctx, computer_name)); } else { result_filter = talloc_asprintf(mem_ctx, "(&(servicePrincipalName=%s)(objectClass=user))", ldb_binary_encode_string(mem_ctx, unparsed_name_short)); } krb5_free_principal(smb_krb5_context->krb5_context, principal); free(unparsed_name_short); W_ERROR_HAVE_NO_MEMORY(result_filter); break; } default: { info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND; return WERR_OK; } } if (format_flags & DRSUAPI_DS_NAME_FLAG_SYNTACTICAL_ONLY) { return DsCrackNameOneSyntactical(mem_ctx, format_offered, format_desired, name_dn, name, info1); } return DsCrackNameOneFilter(sam_ctx, mem_ctx, smb_krb5_context, format_flags, format_offered, format_desired, name_dn, name, domain_filter, result_filter, info1, scope, search_dn); }