Beispiel #1
0
 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
}
Beispiel #2
0
/*
 * 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;
}
Beispiel #3
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;
}
Beispiel #4
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;
}
Beispiel #5
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;
}
Beispiel #6
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;
}
Beispiel #7
0
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);
}
Beispiel #8
0
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;
}
Beispiel #9
0
/*
 * 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;
}
Beispiel #10
0
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);
}
Beispiel #11
0
/*
 * 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;
}
Beispiel #12
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);
}
Beispiel #13
0
/*
 * 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;
}
Beispiel #14
0
/*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;
}
Beispiel #15
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;
}
Beispiel #16
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;
}
Beispiel #17
0
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;
}
Beispiel #19
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;
    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;
}
Beispiel #20
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");
  /*
   * 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;
}
Beispiel #22
0
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;
}
Beispiel #23
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;
}
Beispiel #24
0
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 */
}
Beispiel #25
0
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;
}
Beispiel #26
0
/*
 * 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);
}
Beispiel #27
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;
}
Beispiel #28
0
/**
 * @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;
}
Beispiel #29
0
/* 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);
}