示例#1
0
static krb5_error_code
tgs_build_reply(krb5_context context, 
		krb5_kdc_configuration *config,
		KDC_REQ *req, 
		KDC_REQ_BODY *b,
		hdb_entry_ex *krbtgt,
		krb5_enctype krbtgt_etype,
		krb5_ticket *ticket,
		krb5_data *reply,
		const char *from,
		const char **e_text,
		AuthorizationData *auth_data,
		const struct sockaddr *from_addr,
		int datagram_reply)
{
    krb5_error_code ret;
    krb5_principal cp = NULL, sp = NULL;
    krb5_principal client_principal = NULL;
    char *spn = NULL, *cpn = NULL;
    hdb_entry_ex *server = NULL, *client = NULL;
    EncTicketPart *tgt = &ticket->ticket;
    KRB5SignedPathPrincipals *spp = NULL;
    const EncryptionKey *ekey;
    krb5_keyblock sessionkey;
    krb5_kvno kvno;
    krb5_data rspac;
    int cross_realm = 0;

    PrincipalName *s;
    Realm r;
    int nloop = 0;
    EncTicketPart adtkt;
    char opt_str[128];
    int require_signedpath = 0;

    memset(&sessionkey, 0, sizeof(sessionkey));
    memset(&adtkt, 0, sizeof(adtkt));
    krb5_data_zero(&rspac);

    s = b->sname;
    r = b->realm;

    if(b->kdc_options.enc_tkt_in_skey){
	Ticket *t;
	hdb_entry_ex *uu;
	krb5_principal p;
	Key *uukey;
	    
	if(b->additional_tickets == NULL || 
	   b->additional_tickets->len == 0){
	    ret = KRB5KDC_ERR_BADOPTION; /* ? */
	    kdc_log(context, config, 0,
		    "No second ticket present in request");
	    goto out;
	}
	t = &b->additional_tickets->val[0];
	if(!get_krbtgt_realm(&t->sname)){
	    kdc_log(context, config, 0,
		    "Additional ticket is not a ticket-granting ticket");
	    ret = KRB5KDC_ERR_POLICY;
	    goto out;
	}
	_krb5_principalname2krb5_principal(context, &p, t->sname, t->realm);
	ret = _kdc_db_fetch(context, config, p, 
			    HDB_F_GET_CLIENT|HDB_F_GET_SERVER, 
			    NULL, &uu);
	krb5_free_principal(context, p);
	if(ret){
	    if (ret == HDB_ERR_NOENTRY)
		ret = KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN;
	    goto out;
	}
	ret = hdb_enctype2key(context, &uu->entry, 
			      t->enc_part.etype, &uukey);
	if(ret){
	    _kdc_free_ent(context, uu);
	    ret = KRB5KDC_ERR_ETYPE_NOSUPP; /* XXX */
	    goto out;
	}
	ret = krb5_decrypt_ticket(context, t, &uukey->key, &adtkt, 0);
	_kdc_free_ent(context, uu);
	if(ret)
	    goto out;

	ret = verify_flags(context, config, &adtkt, spn);
	if (ret)
	    goto out;

	s = &adtkt.cname;
	r = adtkt.crealm;
    }

    _krb5_principalname2krb5_principal(context, &sp, *s, r);
    ret = krb5_unparse_name(context, sp, &spn);	
    if (ret)
	goto out;
    _krb5_principalname2krb5_principal(context, &cp, tgt->cname, tgt->crealm);
    ret = krb5_unparse_name(context, cp, &cpn);
    if (ret)
	goto out;
    unparse_flags (KDCOptions2int(b->kdc_options),
		   asn1_KDCOptions_units(),
		   opt_str, sizeof(opt_str));
    if(*opt_str)
	kdc_log(context, config, 0,
		"TGS-REQ %s from %s for %s [%s]", 
		cpn, from, spn, opt_str);
    else
	kdc_log(context, config, 0,
		"TGS-REQ %s from %s for %s", cpn, from, spn);

    /*
     * Fetch server
     */

server_lookup:
    ret = _kdc_db_fetch(context, config, sp, HDB_F_GET_SERVER, NULL, &server);

    if(ret){
	const char *new_rlm;
	Realm req_rlm;
	krb5_realm *realms;

	if ((req_rlm = get_krbtgt_realm(&sp->name)) != NULL) {
	    if(nloop++ < 2) {
		new_rlm = find_rpath(context, tgt->crealm, req_rlm);
		if(new_rlm) {
		    kdc_log(context, config, 5, "krbtgt for realm %s "
			    "not found, trying %s", 
			    req_rlm, new_rlm);
		    krb5_free_principal(context, sp);
		    free(spn);
		    krb5_make_principal(context, &sp, r, 
					KRB5_TGS_NAME, new_rlm, NULL);
		    ret = krb5_unparse_name(context, sp, &spn);	
		    if (ret)
			goto out;
		    goto server_lookup;
		}
	    }
	} else if(need_referral(context, sp, &realms)) {
	    if (strcmp(realms[0], sp->realm) != 0) {
		kdc_log(context, config, 5,
			"Returning a referral to realm %s for "
			"server %s that was not found",
			realms[0], spn);
		krb5_free_principal(context, sp);
		free(spn);
		krb5_make_principal(context, &sp, r, KRB5_TGS_NAME,
				    realms[0], NULL);
		ret = krb5_unparse_name(context, sp, &spn);
		if (ret)
		    goto out;
		krb5_free_host_realm(context, realms);
		goto server_lookup;
	    }
	    krb5_free_host_realm(context, realms);
	}
	kdc_log(context, config, 0,
		"Server not found in database: %s: %s", spn,
		krb5_get_err_text(context, ret));
	if (ret == HDB_ERR_NOENTRY)
	    ret = KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN;
	goto out;
    }

    ret = _kdc_db_fetch(context, config, cp, HDB_F_GET_CLIENT, NULL, &client);
    if(ret) {
	const char *krbtgt_realm; 

	/*
	 * If the client belongs to the same realm as our krbtgt, it
	 * should exist in the local database.
	 *
	 */

	krbtgt_realm = 
	    krb5_principal_get_comp_string(context, 
					   krbtgt->entry.principal, 1);

	if(strcmp(krb5_principal_get_realm(context, cp), krbtgt_realm) == 0) {
	    if (ret == HDB_ERR_NOENTRY)
		ret = KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN;
	    kdc_log(context, config, 1, "Client no longer in database: %s",
		    cpn);
	    goto out;
	}
	
	kdc_log(context, config, 1, "Client not found in database: %s: %s",
		cpn, krb5_get_err_text(context, ret));

	cross_realm = 1;
    }
    
    /*
     * Check that service is in the same realm as the krbtgt. If its
     * not the same, its someone that is using a uni-directional trust
     * backward.
     */
    
    if (strcmp(krb5_principal_get_realm(context, sp),
	       krb5_principal_get_comp_string(context, 
					      krbtgt->entry.principal, 
					      1)) != 0) {
	char *tpn;
	ret = krb5_unparse_name(context, krbtgt->entry.principal, &tpn);
	kdc_log(context, config, 0,
		"Request with wrong krbtgt: %s",
		(ret == 0) ? tpn : "<unknown>");
	if(ret == 0)
	    free(tpn);
	ret = KRB5KRB_AP_ERR_NOT_US;
	goto out;
    }

    /*
     *
     */

    client_principal = cp;

    if (client) {
	const PA_DATA *sdata;
	int i = 0;

	sdata = _kdc_find_padata(req, &i, KRB5_PADATA_S4U2SELF);
	if (sdata) {
	    krb5_crypto crypto;
	    krb5_data datack;
	    PA_S4U2Self self;
	    char *selfcpn = NULL;
	    const char *str;

	    ret = decode_PA_S4U2Self(sdata->padata_value.data, 
				     sdata->padata_value.length,
				     &self, NULL);
	    if (ret) {
		kdc_log(context, config, 0, "Failed to decode PA-S4U2Self");
		goto out;
	    }

	    ret = _krb5_s4u2self_to_checksumdata(context, &self, &datack);
	    if (ret)
		goto out;

	    ret = krb5_crypto_init(context, &tgt->key, 0, &crypto);
	    if (ret) {
		free_PA_S4U2Self(&self);
		krb5_data_free(&datack);
		kdc_log(context, config, 0, "krb5_crypto_init failed: %s",
			krb5_get_err_text(context, ret));
		goto out;
	    }

	    ret = krb5_verify_checksum(context,
				       crypto,
				       KRB5_KU_OTHER_CKSUM,
				       datack.data, 
				       datack.length, 
				       &self.cksum);
	    krb5_data_free(&datack);
	    krb5_crypto_destroy(context, crypto);
	    if (ret) {
		free_PA_S4U2Self(&self);
		kdc_log(context, config, 0, 
			"krb5_verify_checksum failed for S4U2Self: %s",
			krb5_get_err_text(context, ret));
		goto out;
	    }

	    ret = _krb5_principalname2krb5_principal(context,
						     &client_principal,
						     self.name,
						     self.realm);
	    free_PA_S4U2Self(&self);
	    if (ret)
		goto out;

	    ret = krb5_unparse_name(context, client_principal, &selfcpn);	
	    if (ret)
		goto out;

	    /*
	     * Check that service doing the impersonating is
	     * requesting a ticket to it-self.
	     */
	    if (krb5_principal_compare(context, cp, sp) != TRUE) {
		kdc_log(context, config, 0, "S4U2Self: %s is not allowed "
			"to impersonate some other user "
			"(tried for user %s to service %s)",
			cpn, selfcpn, spn);
		free(selfcpn);
		ret = KRB5KDC_ERR_BADOPTION; /* ? */
		goto out;
	    }

	    /*
	     * If the service isn't trusted for authentication to
	     * delegation, remove the forward flag.
	     */

	    if (client->entry.flags.trusted_for_delegation) {
		str = "[forwardable]";
	    } else {
		b->kdc_options.forwardable = 0;
		str = "";
	    }
	    kdc_log(context, config, 0, "s4u2self %s impersonating %s to "
		    "service %s %s", cpn, selfcpn, spn, str);
	    free(selfcpn);
	}
    }

    /*
     * Constrained delegation
     */

    if (client != NULL
	&& b->additional_tickets != NULL
	&& b->additional_tickets->len != 0
	&& b->kdc_options.enc_tkt_in_skey == 0)
    {
	Key *clientkey;
	Ticket *t;
	char *str;

	t = &b->additional_tickets->val[0];

	ret = hdb_enctype2key(context, &client->entry, 
			      t->enc_part.etype, &clientkey);
	if(ret){
	    ret = KRB5KDC_ERR_ETYPE_NOSUPP; /* XXX */
	    goto out;
	}

	ret = krb5_decrypt_ticket(context, t, &clientkey->key, &adtkt, 0);
	if (ret) {
	    kdc_log(context, config, 0,
		    "failed to decrypt ticket for "
		    "constrained delegation from %s to %s ", spn, cpn);
	    goto out;
	}

	/* check that ticket is valid */

	if (adtkt.flags.forwardable == 0) {
	    kdc_log(context, config, 0,
		    "Missing forwardable flag on ticket for "
		    "constrained delegation from %s to %s ", spn, cpn);
	    ret = KRB5KDC_ERR_ETYPE_NOSUPP; /* XXX */
	    goto out;
	}

	ret = check_constrained_delegation(context, config, client, sp);
	if (ret) {
	    kdc_log(context, config, 0,
		    "constrained delegation from %s to %s not allowed", 
		    spn, cpn);
	    goto out;
	}

	ret = _krb5_principalname2krb5_principal(context,
						 &client_principal,
						 adtkt.cname,
						 adtkt.crealm);
	if (ret)
	    goto out;

	ret = krb5_unparse_name(context, client_principal, &str);
	if (ret)
	    goto out;

	ret = verify_flags(context, config, &adtkt, str);
	if (ret) {
	    free(str);
	    goto out;
	}

	/*
	 * Check KRB5SignedPath in authorization data and add new entry to
	 * make sure servers can't fake a ticket to us.
	 */

	ret = check_KRB5SignedPath(context,
				   config,
				   krbtgt,
				   &adtkt,
				   &spp,
				   1);
	if (ret) {
	    kdc_log(context, config, 0,
		    "KRB5SignedPath check from service %s failed "
		    "for delegation to %s for client %s "
		    "from %s failed with %s",
		    spn, str, cpn, from, krb5_get_err_text(context, ret));
	    free(str);
	    goto out;
	}

	kdc_log(context, config, 0, "constrained delegation for %s "
		"from %s to %s", str, cpn, spn);
	free(str);

	/* 
	 * Also require that the KDC have issue the service's krbtgt
	 * used to do the request. 
	 */
	require_signedpath = 1;
    }

    /*
     * Check flags
     */

    ret = _kdc_check_flags(context, config, 
			   client, cpn,
			   server, spn,
			   FALSE);
    if(ret)
	goto out;

    if((b->kdc_options.validate || b->kdc_options.renew) && 
       !krb5_principal_compare(context, 
			       krbtgt->entry.principal,
			       server->entry.principal)){
	kdc_log(context, config, 0, "Inconsistent request.");
	ret = KRB5KDC_ERR_SERVER_NOMATCH;
	goto out;
    }

    /* check for valid set of addresses */
    if(!_kdc_check_addresses(context, config, tgt->caddr, from_addr)) {
	ret = KRB5KRB_AP_ERR_BADADDR;
	kdc_log(context, config, 0, "Request from wrong address");
	goto out;
    }
	
    /*
     * Select enctype, return key and kvno.
     */

    {
	krb5_enctype etype;

	if(b->kdc_options.enc_tkt_in_skey) {
	    int i;
	    ekey = &adtkt.key;
	    for(i = 0; i < b->etype.len; i++)
		if (b->etype.val[i] == adtkt.key.keytype)
		    break;
	    if(i == b->etype.len) {
		krb5_clear_error_string(context);
		return KRB5KDC_ERR_ETYPE_NOSUPP;
	    }
	    etype = b->etype.val[i];
	    kvno = 0;
	} else {
	    Key *skey;
	    
	    ret = _kdc_find_etype(context, server, b->etype.val, b->etype.len,
				  &skey, &etype);
	    if(ret) {
		kdc_log(context, config, 0, 
			"Server (%s) has no support for etypes", spp);
		return ret;
	    }
	    ekey = &skey->key;
	    kvno = server->entry.kvno;
	}
	
	ret = krb5_generate_random_keyblock(context, etype, &sessionkey);
	if (ret)
	    goto out;
    }

    /* check PAC if not cross realm and if there is one */
    if (!cross_realm) {
	Key *tkey;

	ret = hdb_enctype2key(context, &krbtgt->entry, 
			      krbtgt_etype, &tkey);
	if(ret) {
	    kdc_log(context, config, 0,
		    "Failed to find key for krbtgt PAC check");
	    goto out;
	}

	ret = check_PAC(context, config, client_principal, 
			client, server, ekey, &tkey->key,
			tgt, &rspac, &require_signedpath);
	if (ret) {
	    kdc_log(context, config, 0,
		    "Verify PAC failed for %s (%s) from %s with %s",
		    spn, cpn, from, krb5_get_err_text(context, ret));
	    goto out;
	}
    }

    /* also check the krbtgt for signature */
    ret = check_KRB5SignedPath(context,
			       config,
			       krbtgt,
			       tgt,
			       &spp,
			       require_signedpath);
    if (ret) {
	kdc_log(context, config, 0,
		"KRB5SignedPath check failed for %s (%s) from %s with %s",
		spn, cpn, from, krb5_get_err_text(context, ret));
	goto out;
    }

    /*
     *
     */

    ret = tgs_make_reply(context,
			 config, 
			 b, 
			 client_principal,
			 tgt, 
			 ekey,
			 &sessionkey,
			 kvno,
			 auth_data,
			 server, 
			 spn,
			 client, 
			 cp, 
			 krbtgt, 
			 krbtgt_etype,
			 spp,
			 &rspac,
			 e_text,
			 reply);
	
out:
    free(spn);
    free(cpn);
	    
    krb5_data_free(&rspac);
    krb5_free_keyblock_contents(context, &sessionkey);
    if(server)
	_kdc_free_ent(context, server);
    if(client)
	_kdc_free_ent(context, client);

    if (client_principal && client_principal != cp)
	krb5_free_principal(context, client_principal);
    if (cp)
	krb5_free_principal(context, cp);
    if (sp)
	krb5_free_principal(context, sp);

    free_EncTicketPart(&adtkt);

    return ret;
}
示例#2
0
文件: rd_req.c 项目: aosm/Heimdal
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_verify_ap_req2(krb5_context context,
		    krb5_auth_context *auth_context,
		    krb5_ap_req *ap_req,
		    krb5_const_principal server,
		    krb5_keyblock *keyblock,
		    krb5_flags flags,
		    krb5_flags *ap_req_options,
		    krb5_ticket **ticket,
		    krb5_key_usage usage)
{
    krb5_ticket *t;
    krb5_auth_context ac;
    krb5_error_code ret;
    EtypeList etypes;

    memset(&etypes, 0, sizeof(etypes));

    if(ticket)
	*ticket = NULL;

    if (auth_context && *auth_context) {
	ac = *auth_context;
    } else {
	ret = krb5_auth_con_init(context, &ac);
	if (ret)
	    return ret;
    }

    t = calloc(1, sizeof(*t));
    if (t == NULL) {
	ret = ENOMEM;
	krb5_clear_error_message(context);
	goto out;
    }

    if (ap_req->ap_options.use_session_key && ac->keyblock){
	ret = krb5_decrypt_ticket(context, &ap_req->ticket,
				  ac->keyblock,
				  &t->ticket,
				  flags);
	krb5_free_keyblock(context, ac->keyblock);
	ac->keyblock = NULL;
    }else
	ret = krb5_decrypt_ticket(context, &ap_req->ticket,
				  keyblock,
				  &t->ticket,
				  flags);

    if(ret)
	goto out;

    ret = _krb5_principalname2krb5_principal(context,
					     &t->server,
					     ap_req->ticket.sname,
					     ap_req->ticket.realm);
    if (ret) goto out;
    ret = _krb5_principalname2krb5_principal(context,
					     &t->client,
					     t->ticket.cname,
					     t->ticket.crealm);
    if (ret) goto out;

    ret = decrypt_authenticator(context,
				&t->ticket.key,
				&ap_req->authenticator,
				ac->authenticator,
				usage);
    if (ret)
	goto out;

    {
	krb5_principal p1, p2;
	krb5_boolean res;

	_krb5_principalname2krb5_principal(context,
					   &p1,
					   ac->authenticator->cname,
					   ac->authenticator->crealm);
	_krb5_principalname2krb5_principal(context,
					   &p2,
					   t->ticket.cname,
					   t->ticket.crealm);
	res = krb5_principal_compare(context, p1, p2);
	krb5_free_principal(context, p1);
	krb5_free_principal(context, p2);
	if (!res) {
	    ret = KRB5KRB_AP_ERR_BADMATCH;
	    krb5_clear_error_message(context);
	    goto out;
	}
    }

    /* check addresses */

    if (t->ticket.caddr
	&& ac->remote_address
	&& !krb5_address_search(context,
				ac->remote_address,
				t->ticket.caddr)) {
	ret = KRB5KRB_AP_ERR_BADADDR;
	krb5_clear_error_message(context);
	goto out;
    }

    /* check timestamp in authenticator */
    {
	krb5_timestamp now;

	krb5_timeofday(context, &now);

	if (krb5_time_abs(ac->authenticator->ctime, now) > context->max_skew) {
	    ret = KRB5KRB_AP_ERR_SKEW;
	    krb5_clear_error_message(context);
	    goto out;
	}
    }

    if (ac->authenticator->seq_number)
	krb5_auth_con_setremoteseqnumber(context, ac,
					 *ac->authenticator->seq_number);

    /* XXX - Xor sequence numbers */

    if (ac->authenticator->subkey) {
	ret = krb5_auth_con_setremotesubkey(context, ac,
					    ac->authenticator->subkey);
	if (ret)
	    goto out;
    }

    ret = find_etypelist(context, ac, &etypes);
    if (ret)
	goto out;

    ac->keytype = (krb5_keytype)ETYPE_NULL;

    if (etypes.val) {
	size_t i;

	for (i = 0; i < etypes.len; i++) {
	    if (krb5_enctype_valid(context, etypes.val[i]) == 0) {
		ac->keytype = etypes.val[i];
		break;
	    }
	}
    }

    /* save key */
    ret = krb5_copy_keyblock(context, &t->ticket.key, &ac->keyblock);
    if (ret) goto out;

    if (ap_req_options) {
	*ap_req_options = 0;
	if (ac->keytype != ETYPE_NULL)
	    *ap_req_options |= AP_OPTS_USE_SUBKEY;
	if (ap_req->ap_options.use_session_key)
	    *ap_req_options |= AP_OPTS_USE_SESSION_KEY;
	if (ap_req->ap_options.mutual_required)
	    *ap_req_options |= AP_OPTS_MUTUAL_REQUIRED;
    }

    if(ticket)
	*ticket = t;
    else
	krb5_free_ticket(context, t);
    if (auth_context) {
	if (*auth_context == NULL)
	    *auth_context = ac;
    } else
	krb5_auth_con_free(context, ac);
    free_EtypeList(&etypes);
    return 0;
 out:
    free_EtypeList(&etypes);
    if (t)
	krb5_free_ticket(context, t);
    if (auth_context == NULL || *auth_context == NULL)
	krb5_auth_con_free(context, ac);
    return ret;
}