kdc: move Services for User implementation out of krb5tgs.c
Move the Services for User (SFU/S4U) implementation -- protocol transition and constrained delegation -- into its own compilation unit, with an interface that only takes an astgs_request_t, so it can be easily factored out into a plugin module in the future. This refactoring is also careful to update all client names in the request structure after the SFU/S4U validation has successfully completed.
This commit is contained in:
		| @@ -125,6 +125,7 @@ libkdc_la_SOURCES = 		\ | ||||
| 	krb5tgs.c		\ | ||||
| 	pkinit.c		\ | ||||
| 	pkinit-ec.c		\ | ||||
| 	mssfu.c			\ | ||||
| 	log.c			\ | ||||
| 	misc.c			\ | ||||
| 	kx509.c			\ | ||||
|   | ||||
| @@ -103,6 +103,7 @@ LIBKDC_OBJS=\ | ||||
| 	$(OBJ)\krb5tgs.obj		\ | ||||
| 	$(OBJ)\pkinit.obj		\ | ||||
| 	$(OBJ)\pkinit-ec.obj		\ | ||||
| 	$(OBJ)\mssfu.obj			\ | ||||
| 	$(OBJ)\log.obj			\ | ||||
| 	$(OBJ)\misc.obj			\ | ||||
| 	$(OBJ)\kx509.obj		\ | ||||
| @@ -144,6 +145,7 @@ libkdc_la_SOURCES = 		\ | ||||
| 	krb5tgs.c		\ | ||||
| 	pkinit.c		\ | ||||
| 	pkinit-ec.c		\ | ||||
| 	mssfu.c			\ | ||||
| 	log.c			\ | ||||
| 	misc.c			\ | ||||
| 	kx509.c			\ | ||||
|   | ||||
							
								
								
									
										11
									
								
								kdc/kdc.h
									
									
									
									
									
								
							
							
						
						
									
										11
									
								
								kdc/kdc.h
									
									
									
									
									
								
							| @@ -131,20 +131,29 @@ typedef struct krb5_kdc_configuration { | ||||
| #define ASTGS_REQUEST_DESC_COMMON_ELEMENTS			\ | ||||
|     HEIM_SVC_REQUEST_DESC_COMMON_ELEMENTS;			\ | ||||
| 								\ | ||||
|     /* AS-REQ or TGS-REQ */					\ | ||||
|     KDC_REQ req;						\ | ||||
| 								\ | ||||
|     /* AS-REP or TGS-REP */					\ | ||||
|     KDC_REP rep;						\ | ||||
|     EncTicketPart et;						\ | ||||
|     EncKDCRepPart ek;						\ | ||||
| 								\ | ||||
|     /* princ requested by client (AS) or canon princ (TGT) */	\ | ||||
|     /* client principal (AS) or TGT/S4U principal (TGS) */	\ | ||||
|     krb5_principal client_princ;				\ | ||||
|     hdb_entry_ex *client;					\ | ||||
|     HDB *clientdb;						\ | ||||
|     krb5_principal canon_client_princ;				\ | ||||
| 								\ | ||||
|     /* server principal */					\ | ||||
|     krb5_principal server_princ;				\ | ||||
|     hdb_entry_ex *server;					\ | ||||
| 								\ | ||||
|     /* presented ticket in TGS-REQ (unused by AS) */		\ | ||||
|     krb5_principal *krbtgt_princ;				\ | ||||
|     hdb_entry_ex *krbtgt;					\ | ||||
|     krb5_ticket *ticket;					\ | ||||
| 								\ | ||||
|     krb5_keyblock reply_key;					\ | ||||
| 								\ | ||||
|     krb5_pac pac;						\ | ||||
|   | ||||
| @@ -2343,6 +2343,8 @@ _kdc_as_rep(astgs_request_t r) | ||||
| 	    goto out; | ||||
|     } | ||||
|  | ||||
|     r->canon_client_princ = r->client->entry.principal; | ||||
|  | ||||
|     /* | ||||
|      * Verify flags after the user been required to prove its identity | ||||
|      * with in a preauth mech. | ||||
|   | ||||
							
								
								
									
										561
									
								
								kdc/krb5tgs.c
									
									
									
									
									
								
							
							
						
						
									
										561
									
								
								kdc/krb5tgs.c
									
									
									
									
									
								
							| @@ -342,63 +342,6 @@ check_tgs_flags(astgs_request_t r, KDC_REQ_BODY *b, | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * Determine if constrained delegation is allowed from this client to this server | ||||
|  */ | ||||
|  | ||||
| static krb5_error_code | ||||
| check_constrained_delegation(krb5_context context, | ||||
| 			     krb5_kdc_configuration *config, | ||||
| 			     HDB *clientdb, | ||||
| 			     hdb_entry_ex *client, | ||||
| 			     hdb_entry_ex *server, | ||||
| 			     krb5_const_principal target) | ||||
| { | ||||
|     const HDB_Ext_Constrained_delegation_acl *acl; | ||||
|     krb5_error_code ret; | ||||
|     size_t i; | ||||
|  | ||||
|     /* | ||||
|      * constrained_delegation (S4U2Proxy) only works within | ||||
|      * the same realm. We use the already canonicalized version | ||||
|      * of the principals here, while "target" is the principal | ||||
|      * provided by the client. | ||||
|      */ | ||||
|     if(!krb5_realm_compare(context, client->entry.principal, server->entry.principal)) { | ||||
| 	ret = KRB5KDC_ERR_BADOPTION; | ||||
| 	kdc_log(context, config, 4, | ||||
| 	    "Bad request for constrained delegation"); | ||||
| 	return ret; | ||||
|     } | ||||
|  | ||||
|     if (clientdb->hdb_check_constrained_delegation) { | ||||
| 	ret = clientdb->hdb_check_constrained_delegation(context, clientdb, client, target); | ||||
| 	if (ret == 0) | ||||
| 	    return 0; | ||||
|     } else { | ||||
| 	/* if client delegates to itself, that ok */ | ||||
| 	if (krb5_principal_compare(context, client->entry.principal, server->entry.principal) == TRUE) | ||||
| 	    return 0; | ||||
|  | ||||
| 	ret = hdb_entry_get_ConstrainedDelegACL(&client->entry, &acl); | ||||
| 	if (ret) { | ||||
| 	    krb5_clear_error_message(context); | ||||
| 	    return ret; | ||||
| 	} | ||||
|  | ||||
| 	if (acl) { | ||||
| 	    for (i = 0; i < acl->len; i++) { | ||||
| 		if (krb5_principal_compare(context, target, &acl->val[i]) == TRUE) | ||||
| 		    return 0; | ||||
| 	    } | ||||
| 	} | ||||
| 	ret = KRB5KDC_ERR_BADOPTION; | ||||
|     } | ||||
|     kdc_log(context, config, 4, | ||||
| 	    "Bad request for constrained delegation"); | ||||
|     return ret; | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * Determine if s4u2self is allowed from this client to this server | ||||
|  * | ||||
| @@ -412,13 +355,13 @@ check_constrained_delegation(krb5_context context, | ||||
|  * alias of client, then it's safe. | ||||
|  */ | ||||
|  | ||||
| static krb5_error_code | ||||
| check_client_matches_target_service(krb5_context context, | ||||
| 				    krb5_kdc_configuration *config, | ||||
| 				    HDB *clientdb, | ||||
| 				    hdb_entry_ex *client, | ||||
| 				    hdb_entry_ex *target_server, | ||||
| 				    krb5_const_principal target_server_principal) | ||||
| krb5_error_code | ||||
| _kdc_check_client_matches_target_service(krb5_context context, | ||||
| 					 krb5_kdc_configuration *config, | ||||
| 					 HDB *clientdb, | ||||
| 					 hdb_entry_ex *client, | ||||
| 					 hdb_entry_ex *target_server, | ||||
| 					 krb5_const_principal target_server_principal) | ||||
| { | ||||
|     krb5_error_code ret; | ||||
|  | ||||
| @@ -587,7 +530,6 @@ fix_transited_encoding(krb5_context context, | ||||
|  | ||||
| static krb5_error_code | ||||
| tgs_make_reply(astgs_request_t r, | ||||
| 	       krb5_principal tgt_name, | ||||
| 	       const EncTicketPart *tgt, | ||||
| 	       const EncryptionKey *serverkey, | ||||
| 	       const EncryptionKey *krbtgtkey, | ||||
| @@ -611,6 +553,8 @@ tgs_make_reply(astgs_request_t r, | ||||
|     krb5_error_code ret; | ||||
|     int is_weak = 0; | ||||
|  | ||||
|     heim_assert(r->client_princ != NULL, "invalid client name passed to tgs_make_reply"); | ||||
|  | ||||
|     rep->pvno = 5; | ||||
|     rep->msg_type = krb_tgs_rep; | ||||
|  | ||||
| @@ -620,7 +564,7 @@ tgs_make_reply(astgs_request_t r, | ||||
|     ALLOC(et->starttime); | ||||
|     *et->starttime = kdc_time; | ||||
|  | ||||
|     ret = check_tgs_flags(r, b, tgt_name, tgt, et); | ||||
|     ret = check_tgs_flags(r, b, r->client_princ, tgt, et); | ||||
|     if(ret) | ||||
| 	goto out; | ||||
|  | ||||
| @@ -661,7 +605,7 @@ tgs_make_reply(astgs_request_t r, | ||||
|     if (ret) | ||||
| 	goto out; | ||||
|     _krb5_principal2principalname(&rep->ticket.sname, server_principal); | ||||
|     ret = copy_Realm(&tgt_name->realm, &rep->crealm); | ||||
|     ret = copy_Realm(&r->client_princ->realm, &rep->crealm); | ||||
|     if (ret) | ||||
| 	goto out; | ||||
|  | ||||
| @@ -674,7 +618,7 @@ tgs_make_reply(astgs_request_t r, | ||||
|     if (et->flags.anonymous && !tgt->flags.anonymous) | ||||
| 	_kdc_make_anonymous_principalname(&rep->cname); | ||||
|     else | ||||
| 	ret = copy_PrincipalName(&tgt_name->name, &rep->cname); | ||||
| 	ret = copy_PrincipalName(&r->client_princ->name, &rep->cname); | ||||
|     if (ret) | ||||
| 	goto out; | ||||
|     rep->ticket.tkt_vno = 5; | ||||
| @@ -791,10 +735,10 @@ tgs_make_reply(astgs_request_t r, | ||||
| 	is_weak = 1; | ||||
|     } | ||||
|  | ||||
|     if (r->client_princ) { | ||||
|     if (r->canon_client_princ) { | ||||
| 	char *cpn; | ||||
|  | ||||
| 	krb5_unparse_name(r->context, r->client_princ, &cpn); | ||||
| 	krb5_unparse_name(r->context, r->canon_client_princ, &cpn); | ||||
| 	_kdc_audit_addkv((kdc_request_t)r, 0, "canon_client_name", "%s", | ||||
| 			 cpn ? cpn : "<unknown>"); | ||||
| 	krb5_xfree(cpn); | ||||
| @@ -819,8 +763,8 @@ tgs_make_reply(astgs_request_t r, | ||||
| 	    krb5_boolean is_tgs = | ||||
| 		krb5_principal_is_krbtgt(r->context, server->entry.principal); | ||||
|  | ||||
| 	    ret = _krb5_kdc_pac_sign_ticket(r->context, r->pac, tgt_name, serverkey, | ||||
| 					    krbtgtkey, rodc_id, NULL, r->client_princ, | ||||
| 	    ret = _krb5_kdc_pac_sign_ticket(r->context, r->pac, r->client_princ, serverkey, | ||||
| 					    krbtgtkey, rodc_id, NULL, r->canon_client_princ, | ||||
| 					    add_ticket_sig, et, | ||||
| 					    is_tgs ? &r->pac_attributes : NULL); | ||||
| 	    if (ret) | ||||
| @@ -962,9 +906,7 @@ validate_fast_ad(astgs_request_t r, krb5_authdata *auth_data) | ||||
| static krb5_error_code | ||||
| tgs_parse_request(astgs_request_t r, | ||||
| 		  const PA_DATA *tgs_req, | ||||
| 		  hdb_entry_ex **krbtgt, | ||||
| 		  krb5_enctype *krbtgt_etype, | ||||
| 		  krb5_ticket **ticket, | ||||
| 		  const char *from, | ||||
| 		  const struct sockaddr *from_addr, | ||||
| 		  time_t **csec, | ||||
| @@ -1016,7 +958,7 @@ tgs_parse_request(astgs_request_t r, | ||||
|  | ||||
|     krbtgt_kvno = ap_req.ticket.enc_part.kvno ? *ap_req.ticket.enc_part.kvno : 0; | ||||
|     ret = _kdc_db_fetch(r->context, config, princ, HDB_F_GET_KRBTGT, | ||||
| 			&krbtgt_kvno, NULL, krbtgt); | ||||
| 			&krbtgt_kvno, NULL, &r->krbtgt); | ||||
|  | ||||
|     if (ret == HDB_ERR_NOT_FOUND_HERE) { | ||||
| 	/* XXX Factor out this unparsing of the same princ all over */ | ||||
| @@ -1074,12 +1016,12 @@ tgs_parse_request(astgs_request_t r, | ||||
| 	goto out; | ||||
|     } | ||||
|  | ||||
|     krbtgt_kvno_try = krbtgt_kvno ? krbtgt_kvno : (*krbtgt)->entry.kvno; | ||||
|     krbtgt_kvno_try = krbtgt_kvno ? krbtgt_kvno : r->krbtgt->entry.kvno; | ||||
|     *krbtgt_etype = ap_req.ticket.enc_part.etype; | ||||
|  | ||||
| next_kvno: | ||||
|     krbtgt_keys = hdb_kvno2keys(r->context, &(*krbtgt)->entry, krbtgt_kvno_try); | ||||
|     ret = hdb_enctype2key(r->context, &(*krbtgt)->entry, krbtgt_keys, | ||||
|     krbtgt_keys = hdb_kvno2keys(r->context, &r->krbtgt->entry, krbtgt_kvno_try); | ||||
|     ret = hdb_enctype2key(r->context, &r->krbtgt->entry, krbtgt_keys, | ||||
| 			  ap_req.ticket.enc_part.etype, &tkey); | ||||
|     if (ret && krbtgt_kvno == 0 && kvno_search_tries > 0) { | ||||
| 	kvno_search_tries--; | ||||
| @@ -1113,12 +1055,12 @@ next_kvno: | ||||
| 			      &tkey->key, | ||||
| 			      verify_ap_req_flags, | ||||
| 			      &ap_req_options, | ||||
| 			      ticket, | ||||
| 			      &r->ticket, | ||||
| 			      KRB5_KU_TGS_REQ_AUTH); | ||||
|     if (*ticket && (*ticket)->ticket.caddr) | ||||
|         _kdc_audit_addaddrs((kdc_request_t)r, (*ticket)->ticket.caddr, "tixaddrs"); | ||||
|     if (r->ticket && r->ticket->ticket.caddr) | ||||
|         _kdc_audit_addaddrs((kdc_request_t)r, r->ticket->ticket.caddr, "tixaddrs"); | ||||
|     if (r->config->warn_ticket_addresses && ret == KRB5KRB_AP_ERR_BADADDR && | ||||
|         *ticket != NULL) { | ||||
|         r->ticket != NULL) { | ||||
|         _kdc_audit_setkv_bool((kdc_request_t)r, "wrongaddr", TRUE); | ||||
|         ret = 0; | ||||
|     } | ||||
| @@ -1165,8 +1107,7 @@ next_kvno: | ||||
| 	} | ||||
|     } | ||||
|  | ||||
|     ret = tgs_check_authenticator(r->context, config, ac, b, | ||||
|                                   &(*ticket)->ticket.key); | ||||
|     ret = tgs_check_authenticator(r->context, config, ac, b, &r->ticket->ticket.key); | ||||
|     if (ret) { | ||||
| 	krb5_auth_con_free(r->context, ac); | ||||
| 	goto out; | ||||
| @@ -1251,7 +1192,7 @@ next_kvno: | ||||
| 	} | ||||
|     } | ||||
|  | ||||
|     ret = validate_fast_ad(r, (*ticket)->ticket.authorization_data); | ||||
|     ret = validate_fast_ad(r, r->ticket->ticket.authorization_data); | ||||
|     if (ret) | ||||
| 	goto out; | ||||
|  | ||||
| @@ -1260,7 +1201,7 @@ next_kvno: | ||||
|      * Check for FAST request | ||||
|      */ | ||||
|  | ||||
|     ret = _kdc_fast_unwrap_request(r, *ticket, ac); | ||||
|     ret = _kdc_fast_unwrap_request(r, r->ticket, ac); | ||||
|     if (ret) | ||||
| 	goto out; | ||||
|  | ||||
| @@ -1405,29 +1346,26 @@ _kdc_db_fetch_client(krb5_context context, | ||||
|  | ||||
| static krb5_error_code | ||||
| tgs_build_reply(astgs_request_t priv, | ||||
| 		hdb_entry_ex *krbtgt, | ||||
| 		krb5_enctype krbtgt_etype, | ||||
| 		krb5_ticket *ticket, | ||||
| 		AuthorizationData **auth_data, | ||||
| 		const struct sockaddr *from_addr) | ||||
| { | ||||
|     krb5_context context = priv->context; | ||||
|     krb5_kdc_configuration *config = priv->config; | ||||
|     KDC_REQ *req = &priv->req; | ||||
|     KDC_REQ_BODY *b = &priv->req.req_body; | ||||
|     const char *from = priv->from; | ||||
|     krb5_error_code ret, ret2; | ||||
|     krb5_principal cp = NULL, sp = NULL, rsp = NULL, tp = NULL, dp = NULL; | ||||
|     krb5_principal cp = NULL, sp = NULL, rsp = NULL; | ||||
|     krb5_principal krbtgt_out_principal = NULL; | ||||
|     krb5_principal user2user_princ = NULL; | ||||
|     char *spn = NULL, *cpn = NULL, *tpn = NULL, *dpn = NULL, *krbtgt_out_n = NULL; | ||||
|     char *spn = NULL, *cpn = NULL, *krbtgt_out_n = NULL; | ||||
|     char *user2user_name = NULL; | ||||
|     hdb_entry_ex *server = NULL, *client = NULL, *s4u2self_impersonated_client = NULL; | ||||
|     hdb_entry_ex *server = NULL, *client = NULL; | ||||
|     hdb_entry_ex *user2user_krbtgt = NULL; | ||||
|     HDB *clientdb, *s4u2self_impersonated_clientdb; | ||||
|     HDB *clientdb; | ||||
|     HDB *serverdb = NULL; | ||||
|     krb5_realm ref_realm = NULL; | ||||
|     EncTicketPart *tgt = &ticket->ticket; | ||||
|     EncTicketPart *tgt = &priv->ticket->ticket; | ||||
|     const EncryptionKey *ekey; | ||||
|     krb5_keyblock sessionkey; | ||||
|     krb5_kvno kvno; | ||||
| @@ -1435,9 +1373,9 @@ tgs_build_reply(astgs_request_t priv, | ||||
|     uint16_t rodc_id; | ||||
|     krb5_boolean add_ticket_sig = FALSE; | ||||
|     const char *tgt_realm = /* Realm of TGT issuer */ | ||||
|         krb5_principal_get_realm(context, krbtgt->entry.principal); | ||||
|         krb5_principal_get_realm(context, priv->krbtgt->entry.principal); | ||||
|     const char *our_realm = /* Realm of this KDC */ | ||||
|         krb5_principal_get_comp_string(context, krbtgt->entry.principal, 1); | ||||
|         krb5_principal_get_comp_string(context, priv->krbtgt->entry.principal, 1); | ||||
|     char **capath = NULL; | ||||
|     size_t num_capath = 0; | ||||
|  | ||||
| @@ -1650,6 +1588,8 @@ server_lookup: | ||||
|     else | ||||
| 	rsp = sp; | ||||
|  | ||||
|     priv->server_princ = sp; | ||||
|  | ||||
|     /* | ||||
|      * Now refetch the primary krbtgt, and get the current kvno (the | ||||
|      * sign check may have been on an old kvno, and the server may | ||||
| @@ -1680,7 +1620,7 @@ server_lookup: | ||||
| 			HDB_F_GET_KRBTGT, NULL, NULL, &krbtgt_out); | ||||
|     if (ret) { | ||||
| 	char *ktpn = NULL; | ||||
| 	ret = krb5_unparse_name(context, krbtgt->entry.principal, &ktpn); | ||||
| 	ret = krb5_unparse_name(context, priv->krbtgt->entry.principal, &ktpn); | ||||
| 	kdc_log(context, config, 4, | ||||
| 		"No such principal %s (needed for authz-data signature keys) " | ||||
| 		"while processing TGS-REQ for service %s with krbtg %s", | ||||
| @@ -1706,6 +1646,7 @@ server_lookup: | ||||
| 	    size_t i; | ||||
| 	    hdb_entry_ex *user2user_client = NULL; | ||||
| 	    krb5_boolean user2user_kdc_issued = FALSE; | ||||
| 	    char *tpn; | ||||
|  | ||||
| 	    if(b->additional_tickets == NULL || | ||||
| 	       b->additional_tickets->len == 0){ | ||||
| @@ -1745,6 +1686,7 @@ server_lookup: | ||||
| 		    ret = KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN; | ||||
| 		_kdc_audit_addreason((kdc_request_t)priv, | ||||
| 				     "User-to-user service principal (TGS) unknown"); | ||||
| 		krb5_xfree(tpn); | ||||
| 		goto out; | ||||
| 	    } | ||||
| 	    ret = hdb_enctype2key(context, &user2user_krbtgt->entry, NULL, | ||||
| @@ -1753,12 +1695,14 @@ server_lookup: | ||||
| 		ret = KRB5KDC_ERR_ETYPE_NOSUPP; /* XXX */ | ||||
| 		_kdc_audit_addreason((kdc_request_t)priv, | ||||
| 				     "User-to-user enctype not supported"); | ||||
| 		krb5_xfree(tpn); | ||||
| 		goto out; | ||||
| 	    } | ||||
| 	    ret = krb5_decrypt_ticket(context, t, &uukey->key, &adtkt, 0); | ||||
| 	    if(ret) { | ||||
| 		_kdc_audit_addreason((kdc_request_t)priv, | ||||
| 				     "User-to-user TGT decrypt failure"); | ||||
| 		krb5_xfree(tpn); | ||||
| 		goto out; | ||||
| 	    } | ||||
|  | ||||
| @@ -1766,8 +1710,10 @@ server_lookup: | ||||
| 	    if (ret) { | ||||
| 		_kdc_audit_addreason((kdc_request_t)priv, | ||||
| 				     "User-to-user TGT expired or invalid"); | ||||
| 		krb5_xfree(tpn); | ||||
| 		goto out; | ||||
| 	    } | ||||
| 	    krb5_xfree(tpn); | ||||
|  | ||||
| 	    /* Fetch the name from the TGT. */ | ||||
| 	    ret = _krb5_principalname2krb5_principal(context, &user2user_princ, | ||||
| @@ -1816,12 +1762,12 @@ server_lookup: | ||||
| 	     * Also check that the account is the same one specified in the | ||||
| 	     * request. | ||||
| 	     */ | ||||
| 	    ret = check_client_matches_target_service(context, | ||||
| 						      config, | ||||
| 						      serverdb, | ||||
| 						      server, | ||||
| 						      user2user_client, | ||||
| 						      user2user_princ); | ||||
| 	    ret = _kdc_check_client_matches_target_service(context, | ||||
| 							   config, | ||||
| 							   serverdb, | ||||
| 							   server, | ||||
| 							   user2user_client, | ||||
| 							   user2user_princ); | ||||
| 	    if (ret) { | ||||
| 		_kdc_free_ent(context, user2user_client); | ||||
| 		goto out; | ||||
| @@ -1945,7 +1891,7 @@ server_lookup: | ||||
| 	goto out; | ||||
|     } | ||||
|  | ||||
|     if (_kdc_synthetic_princ_used_p(context, ticket)) | ||||
|     if (_kdc_synthetic_princ_used_p(context, priv->ticket)) | ||||
| 	flags |= HDB_F_SYNTHETIC_OK; | ||||
|  | ||||
|     ret = _kdc_db_fetch_client(context, config, flags, cp, cpn, our_realm, | ||||
| @@ -1954,12 +1900,14 @@ server_lookup: | ||||
| 	goto out; | ||||
|     flags &= ~HDB_F_SYNTHETIC_OK; | ||||
|     priv->client = client; | ||||
|     priv->clientdb = clientdb; | ||||
|  | ||||
|     heim_assert(priv->client_princ == NULL, "client_princ should be NULL for TGS"); | ||||
|  | ||||
|     ret = _kdc_check_pac(context, config, cp, NULL, client, server, krbtgt, krbtgt, | ||||
|     ret = _kdc_check_pac(context, config, cp, NULL, | ||||
| 			 priv->client, priv->server, | ||||
| 			 priv->krbtgt, priv->krbtgt, | ||||
| 			 &priv->ticket_key->key, &priv->ticket_key->key, tgt, | ||||
| 			 &kdc_issued, &priv->pac, &priv->client_princ, &priv->pac_attributes); | ||||
| 			 &kdc_issued, &priv->pac, &priv->canon_client_princ, | ||||
| 			 &priv->pac_attributes); | ||||
|     if (ret) { | ||||
| 	const char *msg = krb5_get_error_message(context, ret); | ||||
|         _kdc_audit_addreason((kdc_request_t)priv, "PAC check failed"); | ||||
| @@ -1975,351 +1923,15 @@ server_lookup: | ||||
|      */ | ||||
|  | ||||
|     /* by default the tgt principal matches the client principal */ | ||||
|     tp = cp; | ||||
|     tpn = cpn; | ||||
|  | ||||
|     if (client) { | ||||
| 	const PA_DATA *sdata; | ||||
| 	int i = 0; | ||||
|  | ||||
| 	sdata = _kdc_find_padata(req, &i, KRB5_PADATA_FOR_USER); | ||||
| 	if (sdata) { | ||||
| 	    krb5_crypto crypto; | ||||
| 	    krb5_data datack; | ||||
| 	    PA_S4U2Self self; | ||||
| 	    const char *str; | ||||
|  | ||||
| 	    ret = decode_PA_S4U2Self(sdata->padata_value.data, | ||||
| 				     sdata->padata_value.length, | ||||
| 				     &self, NULL); | ||||
| 	    if (ret) { | ||||
|                 _kdc_audit_addreason((kdc_request_t)priv, | ||||
|                                      "Failed to decode PA-S4U2Self"); | ||||
| 		kdc_log(context, config, 4, "Failed to decode PA-S4U2Self"); | ||||
| 		goto out; | ||||
| 	    } | ||||
|  | ||||
| 	    if (!krb5_checksum_is_keyed(context, self.cksum.cksumtype)) { | ||||
| 		free_PA_S4U2Self(&self); | ||||
|                 _kdc_audit_addreason((kdc_request_t)priv, | ||||
|                                      "PA-S4U2Self with unkeyed checksum"); | ||||
| 		kdc_log(context, config, 4, "Reject PA-S4U2Self with unkeyed checksum"); | ||||
| 		ret = KRB5KRB_AP_ERR_INAPP_CKSUM; | ||||
| 		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) { | ||||
| 		const char *msg = krb5_get_error_message(context, ret); | ||||
| 		free_PA_S4U2Self(&self); | ||||
| 		krb5_data_free(&datack); | ||||
| 		kdc_log(context, config, 4, "krb5_crypto_init failed: %s", msg); | ||||
| 		krb5_free_error_message(context, msg); | ||||
| 		goto out; | ||||
| 	    } | ||||
|  | ||||
| 	    /* Allow HMAC_MD5 checksum with any key type */ | ||||
| 	    if (self.cksum.cksumtype == CKSUMTYPE_HMAC_MD5) { | ||||
| 		struct krb5_crypto_iov iov; | ||||
| 		unsigned char csdata[16]; | ||||
| 		Checksum cs; | ||||
|  | ||||
| 		cs.checksum.length = sizeof(csdata); | ||||
| 		cs.checksum.data = &csdata; | ||||
|  | ||||
| 		iov.data.data = datack.data; | ||||
| 		iov.data.length = datack.length; | ||||
| 		iov.flags = KRB5_CRYPTO_TYPE_DATA; | ||||
|  | ||||
| 		ret = _krb5_HMAC_MD5_checksum(context, NULL, &crypto->key, | ||||
| 					      KRB5_KU_OTHER_CKSUM, &iov, 1, | ||||
| 					      &cs); | ||||
| 		if (ret == 0 && | ||||
| 		    krb5_data_ct_cmp(&cs.checksum, &self.cksum.checksum) != 0) | ||||
| 		    ret = KRB5KRB_AP_ERR_BAD_INTEGRITY; | ||||
| 	    } | ||||
| 	    else { | ||||
| 		ret = _kdc_verify_checksum(context, | ||||
| 					   crypto, | ||||
| 					   KRB5_KU_OTHER_CKSUM, | ||||
| 					   &datack, | ||||
| 					   &self.cksum); | ||||
| 	    } | ||||
| 	    krb5_data_free(&datack); | ||||
| 	    krb5_crypto_destroy(context, crypto); | ||||
| 	    if (ret) { | ||||
| 		const char *msg = krb5_get_error_message(context, ret); | ||||
| 		free_PA_S4U2Self(&self); | ||||
|                 _kdc_audit_addreason((kdc_request_t)priv, | ||||
|                                      "S4U2Self checksum failed"); | ||||
| 		kdc_log(context, config, 4, | ||||
| 			"krb5_verify_checksum failed for S4U2Self: %s", msg); | ||||
| 		krb5_free_error_message(context, msg); | ||||
| 		goto out; | ||||
| 	    } | ||||
|  | ||||
| 	    ret = _krb5_principalname2krb5_principal(context, | ||||
| 						     &tp, | ||||
| 						     self.name, | ||||
| 						     self.realm); | ||||
| 	    free_PA_S4U2Self(&self); | ||||
| 	    if (ret) | ||||
| 		goto out; | ||||
|  | ||||
| 	    ret = krb5_unparse_name(context, tp, &tpn); | ||||
| 	    if (ret) | ||||
| 		goto out; | ||||
|  | ||||
|             /* | ||||
|              * Note no HDB_F_SYNTHETIC_OK -- impersonating non-existent clients | ||||
|              * is probably not desirable! | ||||
|              */ | ||||
| 	    ret = _kdc_db_fetch(context, config, tp, HDB_F_GET_CLIENT | flags, | ||||
| 				NULL, &s4u2self_impersonated_clientdb, | ||||
| 				&s4u2self_impersonated_client); | ||||
| 	    if (ret) { | ||||
| 		const char *msg; | ||||
|  | ||||
| 		/* | ||||
| 		 * If the client belongs to the same realm as our krbtgt, it | ||||
| 		 * should exist in the local database. | ||||
| 		 * | ||||
| 		 */ | ||||
|  | ||||
| 		if (ret == HDB_ERR_NOENTRY) | ||||
| 		    ret = KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN; | ||||
| 		msg = krb5_get_error_message(context, ret); | ||||
|                 _kdc_audit_addreason((kdc_request_t)priv, | ||||
|                                      "S4U2Self principal to impersonate not found"); | ||||
| 		kdc_log(context, config, 2, | ||||
| 			"S4U2Self principal to impersonate %s not found in database: %s", | ||||
| 			tpn, msg); | ||||
| 		krb5_free_error_message(context, msg); | ||||
| 		goto out; | ||||
| 	    } | ||||
|  | ||||
| 	    /* Ignore require_pwchange and pw_end attributes (as Windows does), | ||||
| 	     * since S4U2Self is not password authentication. */ | ||||
| 	    s4u2self_impersonated_client->entry.flags.require_pwchange = FALSE; | ||||
| 	    free(s4u2self_impersonated_client->entry.pw_end); | ||||
| 	    s4u2self_impersonated_client->entry.pw_end = NULL; | ||||
|  | ||||
| 	    ret = kdc_check_flags(priv, FALSE, s4u2self_impersonated_client, priv->server); | ||||
| 	    if (ret) | ||||
| 		goto out; /* kdc_check_flags() calls _kdc_audit_addreason() */ | ||||
|  | ||||
| 	    /* If we were about to put a PAC into the ticket, we better fix it to be the right PAC */ | ||||
| 	    krb5_pac_free(context, priv->pac); | ||||
| 	    priv->pac = NULL; | ||||
|  | ||||
| 	    ret = _kdc_pac_generate(context, | ||||
| 				    s4u2self_impersonated_client, | ||||
| 				    server, | ||||
| 				    NULL, | ||||
| 				    KRB5_PAC_WAS_GIVEN_IMPLICITLY, | ||||
| 				    &priv->pac); | ||||
| 	    if (ret) { | ||||
| 		kdc_log(context, config, 4, "PAC generation failed for -- %s", tpn); | ||||
| 		goto out; | ||||
| 	    } | ||||
|  | ||||
| 	    /* | ||||
| 	     * Check that service doing the impersonating is | ||||
| 	     * requesting a ticket to it-self. | ||||
| 	     */ | ||||
| 	    ret = check_client_matches_target_service(context, | ||||
| 						      config, | ||||
| 						      clientdb, | ||||
| 						      client, | ||||
| 						      server, | ||||
| 						      sp); | ||||
| 	    if (ret) { | ||||
| 		kdc_log(context, config, 4, "S4U2Self: %s is not allowed " | ||||
| 			"to impersonate to service " | ||||
| 			"(tried for user %s to service %s)", | ||||
| 			cpn, tpn, spn); | ||||
| 		goto out; | ||||
| 	    } | ||||
|  | ||||
| 	    /* | ||||
| 	     * If the service isn't trusted for authentication to | ||||
| 	     * delegation or if the impersonate client is disallowed | ||||
| 	     * forwardable, remove the forwardable flag. | ||||
| 	     */ | ||||
|  | ||||
| 	    if (client->entry.flags.trusted_for_delegation && | ||||
| 		s4u2self_impersonated_client->entry.flags.forwardable) { | ||||
| 		str = "[forwardable]"; | ||||
| 	    } else { | ||||
| 		b->kdc_options.forwardable = 0; | ||||
| 		str = ""; | ||||
| 	    } | ||||
| 	    kdc_log(context, config, 4, "s4u2self %s impersonating %s to " | ||||
| 		    "service %s %s", cpn, tpn, spn, str); | ||||
| 	} | ||||
|     } | ||||
|     priv->client_princ = cp; | ||||
|  | ||||
|     /* | ||||
|      * Constrained delegation | ||||
|      * Services for User: protocol transition and constrained delegation | ||||
|      */ | ||||
|  | ||||
|     if (client != NULL | ||||
| 	&& b->additional_tickets != NULL | ||||
| 	&& b->additional_tickets->len != 0 | ||||
| 	&& b->kdc_options.cname_in_addl_tkt | ||||
| 	&& b->kdc_options.enc_tkt_in_skey == 0) | ||||
|     { | ||||
| 	hdb_entry_ex *adclient = NULL; | ||||
| 	krb5_boolean ad_kdc_issued = FALSE; | ||||
| 	Key *clientkey; | ||||
| 	Ticket *t; | ||||
|  | ||||
| 	/* | ||||
| 	 * We require that the service's krbtgt has a PAC. | ||||
| 	 */ | ||||
| 	if (priv->pac == NULL) { | ||||
| 	    ret = KRB5KDC_ERR_BADOPTION; | ||||
| 	    _kdc_audit_addreason((kdc_request_t)priv, "Missing PAC"); | ||||
| 	    kdc_log(context, config, 4, | ||||
| 		    "Constrained delegation without PAC, %s/%s", | ||||
| 		    cpn, spn); | ||||
| 	    goto out; | ||||
| 	} | ||||
|  | ||||
| 	krb5_pac_free(context, priv->pac); | ||||
| 	priv->pac = NULL; | ||||
|  | ||||
| 	krb5_free_principal(context, priv->client_princ); | ||||
| 	priv->client_princ = NULL; | ||||
|  | ||||
| 	t = &b->additional_tickets->val[0]; | ||||
|  | ||||
| 	ret = hdb_enctype2key(context, &client->entry, | ||||
| 			      hdb_kvno2keys(context, &client->entry, | ||||
| 					    t->enc_part.kvno ? * t->enc_part.kvno : 0), | ||||
| 			      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_audit_addreason((kdc_request_t)priv, | ||||
|                                  "Failed to decrypt constrained delegation ticket"); | ||||
| 	    kdc_log(context, config, 4, | ||||
| 		    "failed to decrypt ticket for " | ||||
| 		    "constrained delegation from %s to %s ", cpn, spn); | ||||
| 	    goto out; | ||||
| 	} | ||||
|  | ||||
| 	ret = _krb5_principalname2krb5_principal(context, | ||||
| 						 &tp, | ||||
| 						 adtkt.cname, | ||||
| 						 adtkt.crealm); | ||||
| 	if (ret) | ||||
| 	    goto out; | ||||
|  | ||||
| 	ret = krb5_unparse_name(context, tp, &tpn); | ||||
| 	if (ret) | ||||
| 	    goto out; | ||||
|  | ||||
|         _kdc_audit_addkv((kdc_request_t)priv, 0, "impersonatee", "%s", tpn); | ||||
|  | ||||
| 	ret = _krb5_principalname2krb5_principal(context, | ||||
| 						 &dp, | ||||
| 						 t->sname, | ||||
| 						 t->realm); | ||||
| 	if (ret) | ||||
| 	    goto out; | ||||
|  | ||||
| 	ret = krb5_unparse_name(context, dp, &dpn); | ||||
| 	if (ret) | ||||
| 	    goto out; | ||||
|  | ||||
| 	/* check that ticket is valid */ | ||||
| 	if (adtkt.flags.forwardable == 0) { | ||||
|             _kdc_audit_addreason((kdc_request_t)priv, | ||||
|                                  "Missing forwardable flag on ticket for constrained delegation"); | ||||
| 	    kdc_log(context, config, 4, | ||||
| 		    "Missing forwardable flag on ticket for " | ||||
| 		    "constrained delegation from %s (%s) as %s to %s ", | ||||
| 		    cpn, dpn, tpn, spn); | ||||
| 	    ret = KRB5KDC_ERR_BADOPTION; | ||||
| 	    goto out; | ||||
| 	} | ||||
|  | ||||
| 	ret = check_constrained_delegation(context, config, clientdb, | ||||
| 					   client, server, sp); | ||||
| 	if (ret) { | ||||
|             _kdc_audit_addreason((kdc_request_t)priv, | ||||
|                                  "Constrained delegation not allowed"); | ||||
| 	    kdc_log(context, config, 4, | ||||
| 		    "constrained delegation from %s (%s) as %s to %s not allowed", | ||||
| 		    cpn, dpn, tpn, spn); | ||||
| 	    goto out; | ||||
| 	} | ||||
|  | ||||
| 	ret = _kdc_verify_flags(context, config, &adtkt, tpn); | ||||
| 	if (ret) { | ||||
|             _kdc_audit_addreason((kdc_request_t)priv, | ||||
|                                  "Constrained delegation ticket expired or invalid"); | ||||
| 	    goto out; | ||||
| 	} | ||||
|  | ||||
| 	/* Try lookup the delegated client in DB */ | ||||
| 	ret = _kdc_db_fetch_client(context, config, flags, tp, tpn, our_realm, | ||||
| 				   NULL, &adclient); | ||||
| 	if (ret) | ||||
| 	    goto out; | ||||
|  | ||||
| 	if (adclient != NULL) { | ||||
| 	    ret = kdc_check_flags(priv, FALSE, adclient, priv->server); | ||||
| 	    if (ret) { | ||||
| 		_kdc_free_ent(context, adclient); | ||||
| 		goto out; | ||||
| 	    } | ||||
| 	} | ||||
|  | ||||
| 	/* | ||||
| 	 * TODO: pass in t->sname and t->realm and build | ||||
| 	 * a S4U_DELEGATION_INFO blob to the PAC. | ||||
| 	 */ | ||||
| 	ret = _kdc_check_pac(context, config, tp, dp, adclient, server, krbtgt, client, | ||||
| 			     &clientkey->key, &priv->ticket_key->key, &adtkt, | ||||
| 			     &ad_kdc_issued, &priv->pac, &priv->client_princ, &priv->pac_attributes); | ||||
| 	if (adclient) | ||||
| 	    _kdc_free_ent(context, adclient); | ||||
| 	if (ret) { | ||||
| 	    const char *msg = krb5_get_error_message(context, ret); | ||||
|             _kdc_audit_addreason((kdc_request_t)priv, | ||||
|                                  "Constrained delegation ticket PAC check failed"); | ||||
| 	    kdc_log(context, config, 4, | ||||
| 		    "Verify delegated PAC failed to %s for client" | ||||
| 		    "%s (%s) as %s from %s with %s", | ||||
| 		    spn, cpn, dpn, tpn, from, msg); | ||||
| 	    krb5_free_error_message(context, msg); | ||||
| 	    goto out; | ||||
| 	} | ||||
|  | ||||
| 	if (priv->pac == NULL || !ad_kdc_issued) { | ||||
| 	    ret = KRB5KDC_ERR_BADOPTION; | ||||
| 	    kdc_log(context, config, 4, | ||||
| 		    "Ticket not signed with PAC; service %s failed for " | ||||
| 		    "for delegation to %s for client %s (%s) from %s; (%s).", | ||||
| 		    spn, tpn, dpn, cpn, from, priv->pac ? "Ticket unsigned" : "No PAC"); | ||||
|             _kdc_audit_addreason((kdc_request_t)priv, | ||||
|                                  "Constrained delegation ticket not signed"); | ||||
| 	    goto out; | ||||
| 	} | ||||
|  | ||||
| 	kdc_log(context, config, 4, "constrained delegation for %s " | ||||
| 		"from %s (%s) to %s", tpn, cpn, dpn, spn); | ||||
|     } | ||||
|     ret = _kdc_validate_services_for_user(priv); | ||||
|     if (ret) | ||||
| 	goto out; | ||||
|  | ||||
|     /* | ||||
|      * Check flags | ||||
| @@ -2331,8 +1943,8 @@ server_lookup: | ||||
|  | ||||
|     if((b->kdc_options.validate || b->kdc_options.renew) && | ||||
|        !krb5_principal_compare(context, | ||||
| 			       krbtgt->entry.principal, | ||||
| 			       server->entry.principal)){ | ||||
| 			       priv->krbtgt->entry.principal, | ||||
| 			       priv->server->entry.principal)){ | ||||
|         _kdc_audit_addreason((kdc_request_t)priv, "Inconsistent request"); | ||||
| 	kdc_log(context, config, 4, "Inconsistent request."); | ||||
| 	ret = KRB5KDC_ERR_SERVER_NOMATCH; | ||||
| @@ -2427,7 +2039,6 @@ server_lookup: | ||||
|      */ | ||||
|  | ||||
|     ret = tgs_make_reply(priv, | ||||
| 			 tp, | ||||
| 			 tgt, | ||||
| 			 ekey, | ||||
| 			 &tkey_sign->key, | ||||
| @@ -2444,29 +2055,18 @@ server_lookup: | ||||
|  | ||||
| out: | ||||
|     free(user2user_name); | ||||
|     if (tpn != cpn) | ||||
| 	    free(tpn); | ||||
|     free(dpn); | ||||
|     free(krbtgt_out_n); | ||||
|     _krb5_free_capath(context, capath); | ||||
|  | ||||
|     krb5_free_keyblock_contents(context, &sessionkey); | ||||
|     if(krbtgt_out) | ||||
| 	_kdc_free_ent(context, krbtgt_out); | ||||
|     if(server) | ||||
| 	_kdc_free_ent(context, server); | ||||
|     if(client) | ||||
| 	_kdc_free_ent(context, client); | ||||
|     if(s4u2self_impersonated_client) | ||||
| 	_kdc_free_ent(context, s4u2self_impersonated_client); | ||||
|     if(user2user_krbtgt) | ||||
| 	_kdc_free_ent(context, user2user_krbtgt); | ||||
|  | ||||
|     krb5_free_principal(context, user2user_princ); | ||||
|     if (tp && tp != cp) | ||||
| 	krb5_free_principal(context, tp); | ||||
|     krb5_free_principal(context, cp); | ||||
|     krb5_free_principal(context, dp); | ||||
|     if (priv->client_princ != cp) | ||||
| 	krb5_free_principal(context, cp); /* else caller frees */ | ||||
|     krb5_free_principal(context, sp); | ||||
|     krb5_free_principal(context, krbtgt_out_principal); | ||||
|     free(ref_realm); | ||||
| @@ -2495,9 +2095,6 @@ _kdc_tgs_rep(astgs_request_t r) | ||||
|     krb5_error_code ret; | ||||
|     int i = 0; | ||||
|     const PA_DATA *tgs_req, *pa; | ||||
|  | ||||
|     hdb_entry_ex *krbtgt = NULL; | ||||
|     krb5_ticket *ticket = NULL; | ||||
|     krb5_enctype krbtgt_etype = ETYPE_NULL; | ||||
|  | ||||
|     time_t *csec = NULL; | ||||
| @@ -2530,9 +2127,7 @@ _kdc_tgs_rep(astgs_request_t r) | ||||
| 	goto out; | ||||
|     } | ||||
|     ret = tgs_parse_request(r, tgs_req, | ||||
| 			    &krbtgt, | ||||
| 			    &krbtgt_etype, | ||||
| 			    &ticket, | ||||
| 			    from, from_addr, | ||||
| 			    &csec, &cusec, | ||||
| 			    &auth_data); | ||||
| @@ -2558,9 +2153,7 @@ _kdc_tgs_rep(astgs_request_t r) | ||||
|     } | ||||
|  | ||||
|     ret = tgs_build_reply(r, | ||||
| 			  krbtgt, | ||||
| 			  krbtgt_etype, | ||||
| 			  ticket, | ||||
| 			  &auth_data, | ||||
| 			  from_addr); | ||||
|     if (ret) { | ||||
| @@ -2589,8 +2182,8 @@ out: | ||||
| 				 r->armor_crypto, | ||||
| 				 &req->req_body, | ||||
| 				 r->ret, | ||||
| 				 ticket != NULL ? ticket->client : NULL, | ||||
| 				 ticket != NULL ? ticket->server : NULL, | ||||
| 				 r->ticket != NULL ? r->ticket->client : NULL, | ||||
| 				 r->ticket != NULL ? r->ticket->server : NULL, | ||||
| 				 csec, cusec, | ||||
| 				 data); | ||||
| 	free_METHOD_DATA(&error_method); | ||||
| @@ -2613,9 +2206,9 @@ out: | ||||
|     } | ||||
|     free_EncryptionKey(&r->et.key); | ||||
|  | ||||
|     if (r->client_princ) { | ||||
| 	krb5_free_principal(r->context, r->client_princ); | ||||
| 	r->client_princ = NULL; | ||||
|     if (r->canon_client_princ) { | ||||
| 	krb5_free_principal(r->context, r->canon_client_princ); | ||||
| 	r->canon_client_princ = NULL; | ||||
|     } | ||||
|     if (r->armor_crypto) { | ||||
| 	krb5_crypto_destroy(r->context, r->armor_crypto); | ||||
| @@ -2628,10 +2221,16 @@ out: | ||||
|     krb5_free_keyblock_contents(r->context, &r->reply_key); | ||||
|     krb5_free_keyblock_contents(r->context, &r->strengthen_key); | ||||
|  | ||||
|     if (ticket) | ||||
| 	krb5_free_ticket(r->context, ticket); | ||||
|     if(krbtgt) | ||||
| 	_kdc_free_ent(r->context, krbtgt); | ||||
|     if (r->ticket) | ||||
| 	krb5_free_ticket(r->context, r->ticket); | ||||
|     if (r->krbtgt) | ||||
| 	_kdc_free_ent(r->context, r->krbtgt); | ||||
|  | ||||
|     if (r->client) | ||||
| 	_kdc_free_ent(r->context, r->client); | ||||
|     krb5_free_principal(r->context, r->client_princ); | ||||
|     if (r->server) | ||||
| 	_kdc_free_ent(r->context, r->server); | ||||
|  | ||||
|     _kdc_free_fast_state(&r->fast); | ||||
|     krb5_pac_free(r->context, r->pac); | ||||
|   | ||||
							
								
								
									
										575
									
								
								kdc/mssfu.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										575
									
								
								kdc/mssfu.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,575 @@ | ||||
| /* | ||||
|  * Copyright (c) 1997-2008 Kungliga Tekniska Högskolan | ||||
|  * (Royal Institute of Technology, Stockholm, Sweden). | ||||
|  * All rights reserved. | ||||
|  * | ||||
|  * Redistribution and use in source and binary forms, with or without | ||||
|  * modification, are permitted provided that the following conditions | ||||
|  * are met: | ||||
|  * | ||||
|  * 1. Redistributions of source code must retain the above copyright | ||||
|  *    notice, this list of conditions and the following disclaimer. | ||||
|  * | ||||
|  * 2. Redistributions in binary form must reproduce the above copyright | ||||
|  *    notice, this list of conditions and the following disclaimer in the | ||||
|  *    documentation and/or other materials provided with the distribution. | ||||
|  * | ||||
|  * 3. Neither the name of the Institute nor the names of its contributors | ||||
|  *    may be used to endorse or promote products derived from this software | ||||
|  *    without specific prior written permission. | ||||
|  * | ||||
|  * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND | ||||
|  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | ||||
|  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | ||||
|  * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE | ||||
|  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | ||||
|  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | ||||
|  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | ||||
|  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | ||||
|  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | ||||
|  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | ||||
|  * SUCH DAMAGE. | ||||
|  */ | ||||
|  | ||||
| #include "kdc_locl.h" | ||||
|  | ||||
| /* | ||||
|  * [MS-SFU] Kerberos Protocol Extensions: | ||||
|  * Service for User (S4U2Self) and Constrained Delegation Protocol (S4U2Proxy) | ||||
|  * https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-sfu/ | ||||
|  */ | ||||
|  | ||||
| /* | ||||
|  * Determine if constrained delegation is allowed from this client to this server | ||||
|  */ | ||||
|  | ||||
| static krb5_error_code | ||||
| check_constrained_delegation(krb5_context context, | ||||
| 			     krb5_kdc_configuration *config, | ||||
| 			     HDB *clientdb, | ||||
| 			     hdb_entry_ex *client, | ||||
| 			     hdb_entry_ex *server, | ||||
| 			     krb5_const_principal target) | ||||
| { | ||||
|     const HDB_Ext_Constrained_delegation_acl *acl; | ||||
|     krb5_error_code ret; | ||||
|     size_t i; | ||||
|  | ||||
|     /* | ||||
|      * constrained delegation (S4U2Proxy) only works within | ||||
|      * the same realm. We use the already canonicalized version | ||||
|      * of the principals here, while "target" is the principal | ||||
|      * provided by the client. | ||||
|      */ | ||||
|     if (!krb5_realm_compare(context, client->entry.principal, server->entry.principal)) { | ||||
| 	ret = KRB5KDC_ERR_BADOPTION; | ||||
| 	kdc_log(context, config, 4, | ||||
| 	    "Bad request for constrained delegation"); | ||||
| 	return ret; | ||||
|     } | ||||
|  | ||||
|     if (clientdb->hdb_check_constrained_delegation) { | ||||
| 	ret = clientdb->hdb_check_constrained_delegation(context, clientdb, client, target); | ||||
| 	if (ret == 0) | ||||
| 	    return 0; | ||||
|     } else { | ||||
| 	/* if client delegates to itself, that ok */ | ||||
| 	if (krb5_principal_compare(context, client->entry.principal, server->entry.principal) == TRUE) | ||||
| 	    return 0; | ||||
|  | ||||
| 	ret = hdb_entry_get_ConstrainedDelegACL(&client->entry, &acl); | ||||
| 	if (ret) { | ||||
| 	    krb5_clear_error_message(context); | ||||
| 	    return ret; | ||||
| 	} | ||||
|  | ||||
| 	if (acl) { | ||||
| 	    for (i = 0; i < acl->len; i++) { | ||||
| 		if (krb5_principal_compare(context, target, &acl->val[i]) == TRUE) | ||||
| 		    return 0; | ||||
| 	    } | ||||
| 	} | ||||
| 	ret = KRB5KDC_ERR_BADOPTION; | ||||
|     } | ||||
|     kdc_log(context, config, 4, | ||||
| 	    "Bad request for constrained delegation"); | ||||
|     return ret; | ||||
| } | ||||
|  | ||||
| static void | ||||
| update_client_names(astgs_request_t r, | ||||
| 		    char **s4ucname, | ||||
| 		    krb5_principal *s4u_client_name, | ||||
| 		    hdb_entry_ex **s4u_client, | ||||
| 		    krb5_principal *s4u_canon_client_name, | ||||
| 		    krb5_pac *s4u_pac) | ||||
| { | ||||
|     krb5_xfree(r->cname); | ||||
|     r->cname = *s4ucname; | ||||
|     *s4ucname = NULL; | ||||
|  | ||||
|     r->client_princ = *s4u_client_name; | ||||
|     *s4u_client_name = NULL; | ||||
|  | ||||
|     _kdc_free_ent(r->context, r->client); | ||||
|     r->client = *s4u_client; | ||||
|     *s4u_client = NULL; | ||||
|  | ||||
|     krb5_free_principal(r->context, r->canon_client_princ); | ||||
|     r->canon_client_princ = *s4u_canon_client_name; | ||||
|     *s4u_canon_client_name = NULL; | ||||
|  | ||||
|     krb5_pac_free(r->context, r->pac); | ||||
|     r->pac = *s4u_pac; | ||||
|     *s4u_pac = NULL; | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * Validate a protocol transition (S4U2Self) request. If present and | ||||
|  * successfully validated then the client in the request structure | ||||
|  * will be replaced with the impersonated client. | ||||
|  */ | ||||
|  | ||||
| static krb5_error_code | ||||
| validate_protocol_transition(astgs_request_t r) | ||||
| { | ||||
|     krb5_error_code ret; | ||||
|     KDC_REQ_BODY *b = &r->req.req_body; | ||||
|     EncTicketPart *ticket = &r->ticket->ticket; | ||||
|     hdb_entry_ex *s4u_client = NULL; | ||||
|     HDB *s4u_clientdb; | ||||
|     int flags = HDB_F_FOR_TGS_REQ; | ||||
|     krb5_principal s4u_client_name = NULL, s4u_canon_client_name = NULL; | ||||
|     krb5_pac s4u_pac = NULL; | ||||
|     const PA_DATA *sdata; | ||||
|     char *s4ucname = NULL; | ||||
|     int i = 0; | ||||
|     krb5_crypto crypto; | ||||
|     krb5_data datack; | ||||
|     PA_S4U2Self self; | ||||
|     const char *str; | ||||
|  | ||||
|     if (r->client == NULL) | ||||
| 	return 0; | ||||
|  | ||||
|     sdata = _kdc_find_padata(&r->req, &i, KRB5_PADATA_FOR_USER); | ||||
|     if (sdata == NULL) | ||||
| 	return 0; | ||||
|  | ||||
|     memset(&self, 0, sizeof(self)); | ||||
|  | ||||
|     if (b->kdc_options.canonicalize) | ||||
| 	flags |= HDB_F_CANON; | ||||
|  | ||||
|     ret = decode_PA_S4U2Self(sdata->padata_value.data, | ||||
| 			     sdata->padata_value.length, | ||||
| 			     &self, NULL); | ||||
|     if (ret) { | ||||
| 	_kdc_audit_addreason((kdc_request_t)r, | ||||
| 			     "Failed to decode PA-S4U2Self"); | ||||
| 	kdc_log(r->context, r->config, 4, "Failed to decode PA-S4U2Self"); | ||||
| 	goto out; | ||||
|     } | ||||
|  | ||||
|     if (!krb5_checksum_is_keyed(r->context, self.cksum.cksumtype)) { | ||||
| 	_kdc_audit_addreason((kdc_request_t)r, | ||||
| 			     "PA-S4U2Self with unkeyed checksum"); | ||||
| 	kdc_log(r->context, r->config, 4, "Reject PA-S4U2Self with unkeyed checksum"); | ||||
| 	ret = KRB5KRB_AP_ERR_INAPP_CKSUM; | ||||
| 	goto out; | ||||
|     } | ||||
|  | ||||
|     ret = _krb5_s4u2self_to_checksumdata(r->context, &self, &datack); | ||||
|     if (ret) | ||||
| 	goto out; | ||||
|  | ||||
|     ret = krb5_crypto_init(r->context, &ticket->key, 0, &crypto); | ||||
|     if (ret) { | ||||
| 	const char *msg = krb5_get_error_message(r->context, ret); | ||||
| 	krb5_data_free(&datack); | ||||
| 	kdc_log(r->context, r->config, 4, "krb5_crypto_init failed: %s", msg); | ||||
| 	krb5_free_error_message(r->context, msg); | ||||
| 	goto out; | ||||
|     } | ||||
|  | ||||
|     /* Allow HMAC_MD5 checksum with any key type */ | ||||
|     if (self.cksum.cksumtype == CKSUMTYPE_HMAC_MD5) { | ||||
| 	struct krb5_crypto_iov iov; | ||||
| 	unsigned char csdata[16]; | ||||
| 	Checksum cs; | ||||
|  | ||||
| 	cs.checksum.length = sizeof(csdata); | ||||
| 	cs.checksum.data = &csdata; | ||||
|  | ||||
| 	iov.data.data = datack.data; | ||||
| 	iov.data.length = datack.length; | ||||
| 	iov.flags = KRB5_CRYPTO_TYPE_DATA; | ||||
|  | ||||
| 	ret = _krb5_HMAC_MD5_checksum(r->context, NULL, &crypto->key, | ||||
| 				      KRB5_KU_OTHER_CKSUM, &iov, 1, | ||||
| 				      &cs); | ||||
| 	if (ret == 0 && | ||||
| 	    krb5_data_ct_cmp(&cs.checksum, &self.cksum.checksum) != 0) | ||||
| 	    ret = KRB5KRB_AP_ERR_BAD_INTEGRITY; | ||||
|     } else { | ||||
| 	ret = _kdc_verify_checksum(r->context, | ||||
| 				   crypto, | ||||
| 				   KRB5_KU_OTHER_CKSUM, | ||||
| 				   &datack, | ||||
| 				   &self.cksum); | ||||
|     } | ||||
|     krb5_data_free(&datack); | ||||
|     krb5_crypto_destroy(r->context, crypto); | ||||
|     if (ret) { | ||||
| 	const char *msg = krb5_get_error_message(r->context, ret); | ||||
| 	_kdc_audit_addreason((kdc_request_t)r, | ||||
| 			     "S4U2Self checksum failed"); | ||||
| 	kdc_log(r->context, r->config, 4, | ||||
| 		"krb5_verify_checksum failed for S4U2Self: %s", msg); | ||||
| 	krb5_free_error_message(r->context, msg); | ||||
| 	goto out; | ||||
|     } | ||||
|  | ||||
|     ret = _krb5_principalname2krb5_principal(r->context, | ||||
| 					     &s4u_client_name, | ||||
| 					     self.name, | ||||
| 					     self.realm); | ||||
|     if (ret) | ||||
| 	goto out; | ||||
|  | ||||
|     ret = krb5_unparse_name(r->context, s4u_client_name, &s4ucname); | ||||
|     if (ret) | ||||
| 	goto out; | ||||
|  | ||||
|     /* | ||||
|      * Note no HDB_F_SYNTHETIC_OK -- impersonating non-existent clients | ||||
|      * is probably not desirable! | ||||
|      */ | ||||
|     ret = _kdc_db_fetch(r->context, r->config, s4u_client_name, | ||||
| 			HDB_F_GET_CLIENT | flags, NULL, | ||||
| 			&s4u_clientdb, &s4u_client); | ||||
|     if (ret) { | ||||
| 	const char *msg; | ||||
|  | ||||
| 	/* | ||||
| 	 * If the client belongs to the same realm as our krbtgt, it | ||||
| 	 * should exist in the local database. | ||||
| 	 * | ||||
| 	 */ | ||||
| 	if (ret == HDB_ERR_NOENTRY) | ||||
| 	    ret = KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN; | ||||
| 	msg = krb5_get_error_message(r->context, ret); | ||||
| 	_kdc_audit_addreason((kdc_request_t)r, | ||||
| 			     "S4U2Self principal to impersonate not found"); | ||||
| 	kdc_log(r->context, r->config, 2, | ||||
| 		"S4U2Self principal to impersonate %s not found in database: %s", | ||||
| 		s4ucname, msg); | ||||
| 	krb5_free_error_message(r->context, msg); | ||||
| 	goto out; | ||||
|     } | ||||
|  | ||||
|     /* | ||||
|      * Ignore require_pwchange and pw_end attributes (as Windows does), | ||||
|      * since S4U2Self is not password authentication. | ||||
|      */ | ||||
|     s4u_client->entry.flags.require_pwchange = FALSE; | ||||
|     free(s4u_client->entry.pw_end); | ||||
|     s4u_client->entry.pw_end = NULL; | ||||
|  | ||||
|     ret = kdc_check_flags(r, FALSE, s4u_client, r->server); | ||||
|     if (ret) | ||||
| 	goto out; /* kdc_check_flags() calls _kdc_audit_addreason() */ | ||||
|  | ||||
|     ret = _kdc_pac_generate(r->context, | ||||
| 			    s4u_client, | ||||
| 			    r->server, | ||||
| 			    NULL, | ||||
| 			    KRB5_PAC_WAS_GIVEN_IMPLICITLY, | ||||
| 			    &s4u_pac); | ||||
|     if (ret) { | ||||
| 	kdc_log(r->context, r->config, 4, "PAC generation failed for -- %s", s4ucname); | ||||
| 	goto out; | ||||
|     } | ||||
|  | ||||
|     /* | ||||
|      * Check that service doing the impersonating is | ||||
|      * requesting a ticket to it-self. | ||||
|      */ | ||||
|     ret = _kdc_check_client_matches_target_service(r->context, | ||||
| 						   r->config, | ||||
| 						   r->clientdb, | ||||
| 						   r->client, | ||||
| 						   r->server, | ||||
| 						   r->server_princ); | ||||
|     if (ret) { | ||||
| 	kdc_log(r->context, r->config, 4, "S4U2Self: %s is not allowed " | ||||
| 		"to impersonate to service " | ||||
| 		 "(tried for user %s to service %s)", | ||||
| 		 r->cname, s4ucname, r->sname); | ||||
| 	goto out; | ||||
|     } | ||||
|  | ||||
|     ret = krb5_copy_principal(r->context, s4u_client->entry.principal, | ||||
| 			      &s4u_canon_client_name); | ||||
|     if (ret) | ||||
| 	goto out; | ||||
|  | ||||
|     /* | ||||
|      * If the service isn't trusted for authentication to | ||||
|      * delegation or if the impersonate client is disallowed | ||||
|      * forwardable, remove the forwardable flag. | ||||
|      */ | ||||
|     if (r->client->entry.flags.trusted_for_delegation && | ||||
| 	s4u_client->entry.flags.forwardable) { | ||||
| 	str = "[forwardable]"; | ||||
|     } else { | ||||
| 	b->kdc_options.forwardable = 0; | ||||
| 	str = ""; | ||||
|     } | ||||
|     kdc_log(r->context, r->config, 4, "s4u2self %s impersonating %s to " | ||||
| 	    "service %s %s", r->cname, s4ucname, r->sname, str); | ||||
|  | ||||
|     /* | ||||
|      * Replace all client information in the request with the | ||||
|      * impersonated client. (The audit entry containing the original | ||||
|      * client name will have been created before this point.) | ||||
|      */ | ||||
|     update_client_names(r, &s4ucname, &s4u_client_name, &s4u_client, | ||||
| 			&s4u_canon_client_name, &s4u_pac); | ||||
|  | ||||
| out: | ||||
|     if (s4u_client) | ||||
| 	_kdc_free_ent(r->context, s4u_client); | ||||
|     krb5_free_principal(r->context, s4u_client_name); | ||||
|     krb5_xfree(s4ucname); | ||||
|     krb5_free_principal(r->context, s4u_canon_client_name); | ||||
|     krb5_pac_free(r->context, s4u_pac); | ||||
|  | ||||
|     free_PA_S4U2Self(&self); | ||||
|  | ||||
|     return ret; | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * Validate a constrained delegation (S4U2Proxy) request. If present | ||||
|  * and successfully validated then the client in the request structure | ||||
|  * will be replaced with the client from the evidence ticket. | ||||
|  */ | ||||
|  | ||||
| static krb5_error_code | ||||
| validate_constrained_delegation(astgs_request_t r) | ||||
| { | ||||
|     krb5_error_code ret; | ||||
|     KDC_REQ_BODY *b = &r->req.req_body; | ||||
|     int flags = HDB_F_FOR_TGS_REQ; | ||||
|     krb5_principal s4u_client_name = NULL, s4u_server_name = NULL; | ||||
|     krb5_principal s4u_canon_client_name = NULL; | ||||
|     krb5_pac s4u_pac = NULL; | ||||
|     uint64_t s4u_pac_attributes; | ||||
|     char *s4ucname = NULL, *s4usname = NULL; | ||||
|     EncTicketPart evidence_tkt; | ||||
|     hdb_entry_ex *s4u_client = NULL; | ||||
|     krb5_boolean ad_kdc_issued = FALSE; | ||||
|     Key *clientkey; | ||||
|     Ticket *t; | ||||
|     krb5_const_realm local_realm; | ||||
|  | ||||
|     if (r->client == NULL | ||||
| 	|| b->additional_tickets == NULL | ||||
| 	|| b->additional_tickets->len == 0 | ||||
| 	|| b->kdc_options.cname_in_addl_tkt == 0 | ||||
| 	|| b->kdc_options.enc_tkt_in_skey) | ||||
| 	return 0; | ||||
|  | ||||
|     memset(&evidence_tkt, 0, sizeof(evidence_tkt)); | ||||
|     local_realm = | ||||
| 	    krb5_principal_get_comp_string(r->context, r->krbtgt->entry.principal, 1); | ||||
|  | ||||
|     /* | ||||
|      * We require that the service's TGT has a PAC; this will have been | ||||
|      * validated prior to this function being called. | ||||
|      */ | ||||
|     if (r->pac == NULL) { | ||||
| 	ret = KRB5KDC_ERR_BADOPTION; | ||||
| 	_kdc_audit_addreason((kdc_request_t)r, "Missing PAC"); | ||||
| 	kdc_log(r->context, r->config, 4, | ||||
| 		"Constrained delegation without PAC, %s/%s", | ||||
| 		r->cname, r->sname); | ||||
| 	goto out; | ||||
|     } | ||||
|  | ||||
|     t = &b->additional_tickets->val[0]; | ||||
|  | ||||
|     ret = hdb_enctype2key(r->context, &r->client->entry, | ||||
| 			  hdb_kvno2keys(r->context, &r->client->entry, | ||||
| 					t->enc_part.kvno ? * t->enc_part.kvno : 0), | ||||
| 			  t->enc_part.etype, &clientkey); | ||||
|     if (ret) { | ||||
| 	ret = KRB5KDC_ERR_ETYPE_NOSUPP; /* XXX */ | ||||
| 	goto out; | ||||
|     } | ||||
|  | ||||
|     ret = krb5_decrypt_ticket(r->context, t, &clientkey->key, &evidence_tkt, 0); | ||||
|     if (ret) { | ||||
| 	_kdc_audit_addreason((kdc_request_t)r, | ||||
| 			     "Failed to decrypt constrained delegation ticket"); | ||||
| 	kdc_log(r->context, r->config, 4, | ||||
| 		"failed to decrypt ticket for " | ||||
| 		"constrained delegation from %s to %s ", r->cname, r->sname); | ||||
| 	goto out; | ||||
|     } | ||||
|  | ||||
|     ret = _krb5_principalname2krb5_principal(r->context, | ||||
| 					     &s4u_client_name, | ||||
| 					     evidence_tkt.cname, | ||||
| 					     evidence_tkt.crealm); | ||||
|     if (ret) | ||||
| 	goto out; | ||||
|  | ||||
|     ret = krb5_unparse_name(r->context, s4u_client_name, &s4ucname); | ||||
|     if (ret) | ||||
| 	goto out; | ||||
|  | ||||
|     _kdc_audit_addkv((kdc_request_t)r, 0, "impersonatee", "%s", s4ucname); | ||||
|  | ||||
|     ret = _krb5_principalname2krb5_principal(r->context, | ||||
| 					     &s4u_server_name, | ||||
| 					     t->sname, | ||||
| 					     t->realm); | ||||
|     if (ret) | ||||
| 	goto out; | ||||
|  | ||||
|     ret = krb5_unparse_name(r->context, s4u_server_name, &s4usname); | ||||
|     if (ret) | ||||
| 	goto out; | ||||
|  | ||||
| 	/* check that ticket is valid */ | ||||
|     if (evidence_tkt.flags.forwardable == 0) { | ||||
| 	_kdc_audit_addreason((kdc_request_t)r, | ||||
| 			     "Missing forwardable flag on ticket for constrained delegation"); | ||||
| 	kdc_log(r->context, r->config, 4, | ||||
| 		"Missing forwardable flag on ticket for " | ||||
| 		"constrained delegation from %s (%s) as %s to %s ", | ||||
| 		r->cname, s4usname, s4ucname, r->sname); | ||||
| 	ret = KRB5KDC_ERR_BADOPTION; | ||||
| 	goto out; | ||||
|     } | ||||
|  | ||||
|     ret = check_constrained_delegation(r->context, r->config, r->clientdb, | ||||
| 				       r->client, r->server, r->server_princ); | ||||
|     if (ret) { | ||||
| 	_kdc_audit_addreason((kdc_request_t)r, | ||||
| 			     "Constrained delegation not allowed"); | ||||
| 	kdc_log(r->context, r->config, 4, | ||||
| 		"constrained delegation from %s (%s) as %s to %s not allowed", | ||||
| 		r->cname, s4usname, s4ucname, r->sname); | ||||
| 	goto out; | ||||
|     } | ||||
|  | ||||
|     ret = _kdc_verify_flags(r->context, r->config, &evidence_tkt, s4ucname); | ||||
|     if (ret) { | ||||
| 	_kdc_audit_addreason((kdc_request_t)r, | ||||
| 			     "Constrained delegation ticket expired or invalid"); | ||||
| 	goto out; | ||||
|     } | ||||
|  | ||||
|     /* Try lookup the delegated client in DB */ | ||||
|     ret = _kdc_db_fetch_client(r->context, r->config, flags, | ||||
| 			       s4u_client_name, s4ucname, local_realm, | ||||
| 			       NULL, &s4u_client); | ||||
|     if (ret) | ||||
| 	goto out; | ||||
|  | ||||
|     if (s4u_client != NULL) { | ||||
| 	ret = kdc_check_flags(r, FALSE, s4u_client, r->server); | ||||
| 	if (ret) | ||||
| 	    goto out; | ||||
|     } | ||||
|  | ||||
|     /* | ||||
|      * TODO: pass in t->sname and t->realm and build | ||||
|      * a S4U_DELEGATION_INFO blob to the PAC. | ||||
|      */ | ||||
|     ret = _kdc_check_pac(r->context, r->config, s4u_client_name, s4u_server_name, | ||||
| 			 s4u_client, r->server, r->krbtgt, r->client, | ||||
| 			 &clientkey->key, &r->ticket_key->key, &evidence_tkt, | ||||
| 			 &ad_kdc_issued, &s4u_pac, | ||||
| 			 &s4u_canon_client_name, &s4u_pac_attributes); | ||||
|     if (ret) { | ||||
| 	const char *msg = krb5_get_error_message(r->context, ret); | ||||
|         _kdc_audit_addreason((kdc_request_t)r, | ||||
|                                  "Constrained delegation ticket PAC check failed"); | ||||
| 	kdc_log(r->context, r->config, 4, | ||||
| 		"Verify delegated PAC failed to %s for client" | ||||
| 		"%s (%s) as %s from %s with %s", | ||||
| 		r->sname, r->cname, s4usname, s4ucname, r->from, msg); | ||||
| 	krb5_free_error_message(r->context, msg); | ||||
| 	goto out; | ||||
|     } | ||||
|  | ||||
|     if (s4u_pac == NULL || !ad_kdc_issued) { | ||||
| 	ret = KRB5KDC_ERR_BADOPTION; | ||||
| 	kdc_log(r->context, r->config, 4, | ||||
| 		"Ticket not signed with PAC; service %s failed for " | ||||
| 		"for delegation to %s for client %s (%s) from %s; (%s).", | ||||
| 		r->sname, s4ucname, s4usname, r->cname, r->from, | ||||
| 		s4u_pac ? "Ticket unsigned" : "No PAC"); | ||||
| 	_kdc_audit_addreason((kdc_request_t)r, | ||||
| 			     "Constrained delegation ticket not signed"); | ||||
| 	goto out; | ||||
|     } | ||||
|  | ||||
|     /* | ||||
|      * If the evidence ticket PAC didn't include PAC_UPN_DNS_INFO with | ||||
|      * the canonical client name, but the user is local to our KDC, we | ||||
|      * can insert the canonical client name ourselves. | ||||
|      */ | ||||
|     if (s4u_canon_client_name == NULL && s4u_client != NULL) { | ||||
| 	ret = krb5_copy_principal(r->context, s4u_client->entry.principal, | ||||
| 				  &s4u_canon_client_name); | ||||
| 	if (ret) | ||||
| 	    goto out; | ||||
|     } | ||||
|  | ||||
|     kdc_log(r->context, r->config, 4, "constrained delegation for %s " | ||||
| 	    "from %s (%s) to %s", s4ucname, r->cname, s4usname, r->sname); | ||||
|  | ||||
|     /* | ||||
|      * Replace all client information in the request with the | ||||
|      * impersonated client. (The audit entry containing the original | ||||
|      * client name will have been created before this point.) | ||||
|      */ | ||||
|     update_client_names(r, &s4ucname, &s4u_client_name, &s4u_client, | ||||
| 			&s4u_canon_client_name, &s4u_pac); | ||||
|     r->pac_attributes = s4u_pac_attributes; | ||||
|  | ||||
| out: | ||||
|     if (s4u_client) | ||||
| 	_kdc_free_ent(r->context, s4u_client); | ||||
|     krb5_free_principal(r->context, s4u_client_name); | ||||
|     krb5_xfree(s4ucname); | ||||
|     krb5_free_principal(r->context, s4u_server_name); | ||||
|     krb5_xfree(s4usname); | ||||
|     krb5_free_principal(r->context, s4u_canon_client_name); | ||||
|     krb5_pac_free(r->context, s4u_pac); | ||||
|  | ||||
|     free_EncTicketPart(&evidence_tkt); | ||||
|  | ||||
|     return ret; | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| krb5_error_code | ||||
| _kdc_validate_services_for_user(astgs_request_t r) | ||||
| { | ||||
|     krb5_error_code ret; | ||||
|  | ||||
|     ret = validate_protocol_transition(r); | ||||
|     if (ret == 0) | ||||
| 	ret = validate_constrained_delegation(r); | ||||
|  | ||||
|     return ret; | ||||
| } | ||||
		Reference in New Issue
	
	Block a user
	 Luke Howard
					Luke Howard