kdc: support for PAC_ATTRIBUTES_INFO
Add PAC_ATTRIBUTES_INFO to the PAC. This info buffer indicates whether the user explicitly requested a PAC be present or absent. Note: this changes the windc plugin ABI.
This commit is contained in:
		| @@ -848,7 +848,7 @@ _kdc_fast_check_armor_pac(astgs_request_t r) | ||||
| 			 armor_client, r->armor_server, | ||||
| 			 r->armor_server, r->armor_server, | ||||
| 			 &r->armor_key->key, &r->armor_key->key, | ||||
| 			 &r->armor_ticket->ticket, &ad_kdc_issued, &mspac, NULL); | ||||
| 			 &r->armor_ticket->ticket, &ad_kdc_issued, &mspac, NULL, NULL); | ||||
|     if (ret) { | ||||
| 	const char *msg = krb5_get_error_message(r->context, ret); | ||||
|  | ||||
|   | ||||
| @@ -109,6 +109,8 @@ struct astgs_request_desc { | ||||
|     Key *armor_key; | ||||
|  | ||||
|     KDCFastState fast; | ||||
|  | ||||
|     uint64_t pac_attributes; | ||||
| }; | ||||
|  | ||||
| typedef struct kx509_req_context_desc { | ||||
|   | ||||
| @@ -1783,34 +1783,33 @@ _kdc_check_anon_policy(astgs_request_t r) | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * | ||||
|  * Determine whether the client requested a PAC be included | ||||
|  * or excluded explictly, or whether it doesn't care. | ||||
|  */ | ||||
|  | ||||
| static krb5_error_code | ||||
| check_pa_pac_request(krb5_context context, | ||||
| 		     KDC_REQ *req, | ||||
| 		     krb5_boolean *include_pac) | ||||
| static uint64_t | ||||
| get_pac_attributes(krb5_context context, KDC_REQ *req) | ||||
| { | ||||
|     krb5_error_code ret; | ||||
|     PA_PAC_REQUEST pacreq; | ||||
|     const PA_DATA *pa; | ||||
|     int i = 0; | ||||
|  | ||||
|     *include_pac = TRUE; | ||||
|     uint32_t pac_attributes; | ||||
|  | ||||
|     pa = _kdc_find_padata(req, &i, KRB5_PADATA_PA_PAC_REQUEST); | ||||
|     if (pa == NULL) | ||||
| 	return KRB5KDC_ERR_PADATA_TYPE_NOSUPP; | ||||
| 	return KRB5_PAC_WAS_GIVEN_IMPLICITLY; | ||||
|  | ||||
|     ret = decode_PA_PAC_REQUEST(pa->padata_value.data, | ||||
| 				pa->padata_value.length, | ||||
| 				&pacreq, | ||||
| 				NULL); | ||||
|     if (ret) | ||||
| 	return KRB5KDC_ERR_PADATA_TYPE_NOSUPP; | ||||
|     *include_pac = pacreq.include_pac; | ||||
| 	return KRB5_PAC_WAS_GIVEN_IMPLICITLY; | ||||
|  | ||||
|     pac_attributes = pacreq.include_pac ? KRB5_PAC_WAS_REQUESTED : 0; | ||||
|     free_PA_PAC_REQUEST(&pacreq); | ||||
|     return 0; | ||||
|     return pac_attributes; | ||||
| } | ||||
|  | ||||
| /* | ||||
| @@ -1825,11 +1824,9 @@ generate_pac(astgs_request_t r, const Key *skey, const Key *tkey) | ||||
|     krb5_data data; | ||||
|     uint16_t rodc_id; | ||||
|     krb5_principal client; | ||||
|     krb5_boolean client_sent_pac_req, pac_request; | ||||
|     krb5_const_principal canon_princ = NULL; | ||||
|  | ||||
|     client_sent_pac_req = | ||||
| 	(check_pa_pac_request(r->context, &r->req, &pac_request) == 0); | ||||
|     r->pac_attributes = get_pac_attributes(r->context, &r->req); | ||||
|  | ||||
|     /* | ||||
|      * When a PA mech replaces the reply key, the PAC may include the | ||||
| @@ -1841,7 +1838,7 @@ generate_pac(astgs_request_t r, const Key *skey, const Key *tkey) | ||||
| 			    r->client, | ||||
| 			    r->server, | ||||
| 			    r->replaced_reply_key ? &r->reply_key : NULL, | ||||
| 			    client_sent_pac_req ? &pac_request : NULL, | ||||
| 			    r->pac_attributes, | ||||
| 			    &p); | ||||
|     if (ret) { | ||||
| 	_kdc_r_log(r, 4, "PAC generation failed for -- %s", | ||||
| @@ -1885,6 +1882,7 @@ generate_pac(astgs_request_t r, const Key *skey, const Key *tkey) | ||||
| 			 rodc_id, | ||||
| 			 NULL, /* UPN */ | ||||
| 			 canon_princ, | ||||
| 			 &r->pac_attributes, | ||||
| 			 &data); | ||||
|     krb5_free_principal(r->context, client); | ||||
|     krb5_pac_free(r->context, p); | ||||
|   | ||||
| @@ -89,7 +89,8 @@ _kdc_check_pac(krb5_context context, | ||||
| 	       EncTicketPart *tkt, | ||||
| 	       krb5_boolean *kdc_issued, | ||||
| 	       krb5_pac *ppac, | ||||
| 	       krb5_principal *pac_canon_name) | ||||
| 	       krb5_principal *pac_canon_name, | ||||
| 	       uint64_t *pac_attributes) | ||||
| { | ||||
|     krb5_pac pac = NULL; | ||||
|     krb5_error_code ret; | ||||
| @@ -129,6 +130,8 @@ _kdc_check_pac(krb5_context context, | ||||
| 		return ret; | ||||
| 	    } | ||||
| 	} | ||||
| 	if (pac_attributes) | ||||
| 	    _krb5_pac_get_attributes_info(context, pac, pac_attributes); | ||||
|     } else if (ret == KRB5_PLUGIN_NO_HANDLE) { | ||||
| 	/* | ||||
| 	 * We can't verify the KDC signatures if the ticket was issued by | ||||
| @@ -150,6 +153,8 @@ _kdc_check_pac(krb5_context context, | ||||
| 		krb5_pac_free(context, pac); | ||||
| 		return ret; | ||||
| 	    } | ||||
| 	    if (pac_attributes) | ||||
| 		_krb5_pac_get_attributes_info(context, pac, pac_attributes); | ||||
| 	} | ||||
|  | ||||
| 	/* Discard the PAC if the plugin didn't handle it */ | ||||
| @@ -818,8 +823,8 @@ tgs_make_reply(astgs_request_t r, | ||||
|  | ||||
| 	/* The PAC should be the last change to the ticket. */ | ||||
| 	ret = _krb5_kdc_pac_sign_ticket(r->context, mspac, tgt_name, serverkey, | ||||
| 					krbtgtkey, rodc_id, add_ticket_sig, &et, | ||||
| 					NULL, r->client_princ); | ||||
| 					krbtgtkey, rodc_id, NULL, r->client_princ, | ||||
| 					add_ticket_sig, &et, &r->pac_attributes); | ||||
| 	if (ret) | ||||
| 	    goto out; | ||||
|     } | ||||
| @@ -1842,7 +1847,7 @@ server_lookup: | ||||
| 	    ret = _kdc_check_pac(context, config, user2user_princ, NULL, | ||||
| 				 user2user_client, user2user_krbtgt, user2user_krbtgt, user2user_krbtgt, | ||||
| 				 &uukey->key, &priv->ticket_key->key, &adtkt, | ||||
| 				 &user2user_kdc_issued, &user2user_pac, NULL); | ||||
| 				 &user2user_kdc_issued, &user2user_pac, NULL, NULL); | ||||
| 	    _kdc_free_ent(context, user2user_client); | ||||
| 	    if (ret) { | ||||
| 		const char *msg = krb5_get_error_message(context, ret); | ||||
| @@ -1970,7 +1975,7 @@ server_lookup: | ||||
|  | ||||
|     ret = _kdc_check_pac(context, config, cp, NULL, client, server, krbtgt, krbtgt, | ||||
| 			 &priv->ticket_key->key, &priv->ticket_key->key, tgt, | ||||
| 			 &kdc_issued, &mspac, &priv->client_princ); | ||||
| 			 &kdc_issued, &mspac, &priv->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"); | ||||
| @@ -2131,7 +2136,7 @@ server_lookup: | ||||
| 				    s4u2self_impersonated_client, | ||||
| 				    server, | ||||
| 				    NULL, | ||||
| 				    NULL, | ||||
| 				    KRB5_PAC_WAS_GIVEN_IMPLICITLY, | ||||
| 				    &mspac); | ||||
| 	    if (ret) { | ||||
| 		kdc_log(context, config, 4, "PAC generation failed for -- %s", tpn); | ||||
| @@ -2302,7 +2307,7 @@ server_lookup: | ||||
| 	 */ | ||||
| 	ret = _kdc_check_pac(context, config, tp, dp, adclient, server, krbtgt, client, | ||||
| 			     &clientkey->key, &priv->ticket_key->key, &adtkt, | ||||
| 			     &ad_kdc_issued, &mspac, &priv->client_princ); | ||||
| 			     &ad_kdc_issued, &mspac, &priv->client_princ, &priv->pac_attributes); | ||||
| 	if (adclient) | ||||
| 	    _kdc_free_ent(context, adclient); | ||||
| 	if (ret) { | ||||
|   | ||||
| @@ -73,8 +73,8 @@ struct generate_uc { | ||||
|     hdb_entry_ex *client; | ||||
|     hdb_entry_ex *server; | ||||
|     const krb5_keyblock *reply_key; | ||||
|     uint64_t pac_attributes; | ||||
|     krb5_pac *pac; | ||||
|     const krb5_boolean *pac_request; | ||||
| }; | ||||
|  | ||||
| static krb5_error_code KRB5_LIB_CALL | ||||
| @@ -90,7 +90,7 @@ generate(krb5_context context, const void *plug, void *plugctx, void *userctx) | ||||
| 			    uc->client, | ||||
| 			    uc->server, | ||||
| 			    uc->reply_key, | ||||
| 			    uc->pac_request, | ||||
| 			    uc->pac_attributes, | ||||
| 			    uc->pac); | ||||
| } | ||||
|  | ||||
| @@ -100,7 +100,7 @@ _kdc_pac_generate(krb5_context context, | ||||
| 		  hdb_entry_ex *client, | ||||
| 		  hdb_entry_ex *server, | ||||
| 		  const krb5_keyblock *reply_key, | ||||
| 		  const krb5_boolean *pac_request, | ||||
| 		  uint64_t pac_attributes, | ||||
| 		  krb5_pac *pac) | ||||
| { | ||||
|     krb5_error_code ret = 0; | ||||
| @@ -118,7 +118,7 @@ _kdc_pac_generate(krb5_context context, | ||||
| 	uc.server = server; | ||||
| 	uc.reply_key = reply_key; | ||||
| 	uc.pac = pac; | ||||
| 	uc.pac_request = pac_request; | ||||
| 	uc.pac_attributes = pac_attributes; | ||||
|  | ||||
| 	ret = _krb5_plugin_run_f(context, &windc_plugin_data, | ||||
| 				 0, &uc, generate); | ||||
|   | ||||
| @@ -57,7 +57,7 @@ typedef krb5_error_code | ||||
| 					       struct hdb_entry_ex *, /* client */ | ||||
| 					       struct hdb_entry_ex *, /* server */ | ||||
| 					       const krb5_keyblock *, /* pk_replykey */ | ||||
| 					       const krb5_boolean *, /* pac_request */ | ||||
| 					       uint64_t,	      /* pac_attributes */ | ||||
| 					       krb5_pac *); | ||||
|  | ||||
| typedef krb5_error_code | ||||
|   | ||||
| @@ -480,4 +480,8 @@ struct krb5_decrypt_tkt_with_subkey_state { | ||||
| /* Flag in KRB5_AUTHDATA_AP_OPTIONS */ | ||||
| #define KERB_AP_OPTIONS_CBT 0x00004000 | ||||
|  | ||||
| /* Flag in PAC_ATTRIBUTES_INFO */ | ||||
| #define KRB5_PAC_WAS_REQUESTED		0x1 | ||||
| #define KRB5_PAC_WAS_GIVEN_IMPLICITLY	0x2 | ||||
|  | ||||
| #endif /* __KRB5_LOCL_H__ */ | ||||
|   | ||||
| @@ -822,6 +822,7 @@ EXPORTS | ||||
| 	_krb5_get_int | ||||
|         _krb5_get_int64 | ||||
| 	_krb5_pac_sign | ||||
| 	_krb5_pac_get_attributes_info | ||||
| 	_krb5_pac_get_canon_principal | ||||
| 	_krb5_kdc_pac_sign_ticket | ||||
| 	_krb5_kdc_pac_ticket_parse | ||||
| @@ -843,7 +844,6 @@ EXPORTS | ||||
| 	_krb5_expand_path_tokens	;! | ||||
| 	_krb5_make_pa_enc_challenge | ||||
| 	_krb5_validate_pa_enc_challenge | ||||
| 	_krb5_store_utf8_as_ucs2le_at_offset | ||||
|  | ||||
|         ; kinit helper | ||||
| 	krb5_get_init_creds_opt_set_pkinit_user_certs | ||||
|   | ||||
							
								
								
									
										219
									
								
								lib/krb5/pac.c
									
									
									
									
									
								
							
							
						
						
									
										219
									
								
								lib/krb5/pac.c
									
									
									
									
									
								
							| @@ -55,13 +55,17 @@ struct krb5_pac_data { | ||||
|     struct PAC_INFO_BUFFER *logon_name; | ||||
|     struct PAC_INFO_BUFFER *upn_dns_info; | ||||
|     struct PAC_INFO_BUFFER *ticket_checksum; | ||||
|     struct PAC_INFO_BUFFER *attributes_info; | ||||
|     krb5_data ticket_sign_data; | ||||
|  | ||||
|     /* parsed upn_dns_info, krb5_pac_verify only */ | ||||
|     /* PAC_UPN_DNS_INFO */ | ||||
|     krb5_principal upn_princ; | ||||
|     uint32_t upn_flags; | ||||
|     krb5_principal canon_princ; | ||||
|     krb5_data sid; | ||||
|  | ||||
|     /* PAC_ATTRIBUTES_INFO */ | ||||
|     uint64_t pac_attributes; | ||||
| }; | ||||
|  | ||||
| #define PAC_ALIGNMENT			8 | ||||
| @@ -75,8 +79,10 @@ struct krb5_pac_data { | ||||
| #define PAC_CONSTRAINED_DELEGATION	11 | ||||
| #define PAC_UPN_DNS_INFO		12 | ||||
| #define PAC_TICKET_CHECKSUM		16 | ||||
| #define PAC_ATTRIBUTES_INFO		17 | ||||
| #define PAC_REQUESTOR_SID		18 | ||||
|  | ||||
| /* Flag in PAC_UPN_DNS_INFO, _krb5_pac_get_upn_dns_info() */ | ||||
| /* Flag in PAC_UPN_DNS_INFO */ | ||||
| #define PAC_EXTRA_LOGON_INFO_FLAGS_UPN_DEFAULTED	0x1 | ||||
| #define PAC_EXTRA_LOGON_INFO_FLAGS_HAS_SAM_NAME_AND_SID	0x2 | ||||
|  | ||||
| @@ -270,6 +276,14 @@ krb5_pac_parse(krb5_context context, const void *ptr, size_t len, | ||||
| 		goto out; | ||||
| 	    } | ||||
| 	    p->ticket_checksum = &p->pac->buffers[i]; | ||||
| 	} else if (p->pac->buffers[i].type == PAC_ATTRIBUTES_INFO) { | ||||
| 	    if (p->attributes_info) { | ||||
| 		ret = EINVAL; | ||||
| 		krb5_set_error_message(context, ret, | ||||
| 				       N_("PAC has multiple attributes info buffers", "")); | ||||
| 		goto out; | ||||
| 	    } | ||||
| 	    p->attributes_info = &p->pac->buffers[i]; | ||||
| 	} | ||||
|     } | ||||
|  | ||||
| @@ -635,14 +649,14 @@ parse_upn_dns_info(krb5_context context, | ||||
| 		   const krb5_data *data, | ||||
| 		   krb5_principal *upn_princ, | ||||
| 		   uint32_t *flags, | ||||
| 		   krb5_principal *sam_name_princ, | ||||
| 		   krb5_principal *canon_princ, | ||||
| 		   krb5_data *sid) | ||||
| { | ||||
|     krb5_error_code ret; | ||||
|     krb5_storage *sp = NULL; | ||||
|     uint16_t upn_length, upn_offset; | ||||
|     uint16_t dns_domain_name_length, dns_domain_name_offset; | ||||
|     uint16_t sam_name_length, sam_name_offset; | ||||
|     uint16_t canon_princ_length, canon_princ_offset; | ||||
|     uint16_t sid_length, sid_offset; | ||||
|     char *upn = NULL; | ||||
|     char *dns_domain_name = NULL; | ||||
| @@ -650,7 +664,7 @@ parse_upn_dns_info(krb5_context context, | ||||
|  | ||||
|     *upn_princ = NULL; | ||||
|     *flags = 0; | ||||
|     *sam_name_princ = NULL; | ||||
|     *canon_princ = NULL; | ||||
|     krb5_data_zero(sid); | ||||
|  | ||||
|     sp = krb5_storage_from_readonly_mem((const char *)data->data + upndnsinfo->offset_lo, | ||||
| @@ -667,13 +681,13 @@ parse_upn_dns_info(krb5_context context, | ||||
|     CHECK(ret, krb5_ret_uint32(sp, flags), out); | ||||
|  | ||||
|     if (*flags & PAC_EXTRA_LOGON_INFO_FLAGS_HAS_SAM_NAME_AND_SID) { | ||||
| 	CHECK(ret, krb5_ret_uint16(sp, &sam_name_length), out); | ||||
| 	CHECK(ret, krb5_ret_uint16(sp, &sam_name_offset), out); | ||||
| 	CHECK(ret, krb5_ret_uint16(sp, &canon_princ_length), out); | ||||
| 	CHECK(ret, krb5_ret_uint16(sp, &canon_princ_offset), out); | ||||
| 	CHECK(ret, krb5_ret_uint16(sp, &sid_length), out); | ||||
| 	CHECK(ret, krb5_ret_uint16(sp, &sid_offset), out); | ||||
|     } else { | ||||
| 	sam_name_offset = 0; | ||||
| 	sid_offset = 0; | ||||
| 	canon_princ_length = canon_princ_offset = 0; | ||||
| 	sid_length = sid_offset = 0; | ||||
|     } | ||||
|  | ||||
|     if (upn_offset) { | ||||
| @@ -682,9 +696,9 @@ parse_upn_dns_info(krb5_context context, | ||||
|     } | ||||
|     CHECK(ret, _krb5_ret_utf8_from_ucs2le_at_offset(sp, dns_domain_name_offset, | ||||
| 						    dns_domain_name_length, &dns_domain_name), out); | ||||
|     if ((*flags & PAC_EXTRA_LOGON_INFO_FLAGS_HAS_SAM_NAME_AND_SID) && sam_name_offset) { | ||||
| 	CHECK(ret, _krb5_ret_utf8_from_ucs2le_at_offset(sp, sam_name_offset, | ||||
| 							sam_name_length, &sam_name), out); | ||||
|     if ((*flags & PAC_EXTRA_LOGON_INFO_FLAGS_HAS_SAM_NAME_AND_SID) && canon_princ_offset) { | ||||
| 	CHECK(ret, _krb5_ret_utf8_from_ucs2le_at_offset(sp, canon_princ_offset, | ||||
| 							canon_princ_length, &sam_name), out); | ||||
|     } | ||||
|  | ||||
|     if (upn_offset) { | ||||
| @@ -701,16 +715,16 @@ parse_upn_dns_info(krb5_context context, | ||||
| 	    goto out; | ||||
|     } | ||||
|  | ||||
|     if (sam_name_offset) { | ||||
|     if (canon_princ_offset) { | ||||
| 	ret = krb5_parse_name_flags(context, | ||||
| 				    sam_name, | ||||
| 				    KRB5_PRINCIPAL_PARSE_NO_REALM | | ||||
| 				    KRB5_PRINCIPAL_PARSE_NO_DEF_REALM, | ||||
| 				    sam_name_princ); | ||||
| 				    canon_princ); | ||||
| 	if (ret) | ||||
| 	    goto out; | ||||
|  | ||||
| 	ret = krb5_principal_set_realm(context, *sam_name_princ, dns_domain_name); | ||||
| 	ret = krb5_principal_set_realm(context, *canon_princ, dns_domain_name); | ||||
| 	if (ret) | ||||
| 	    goto out; | ||||
|     } | ||||
| @@ -734,6 +748,7 @@ static krb5_error_code | ||||
| build_upn_dns_info(krb5_context context, | ||||
| 		   krb5_const_principal upn_princ, | ||||
| 		   krb5_const_principal canon_princ, | ||||
| 		   const krb5_data *sid, | ||||
| 		   krb5_data *upn_dns_info) | ||||
| { | ||||
|     krb5_error_code ret; | ||||
| @@ -779,8 +794,10 @@ build_upn_dns_info(krb5_context context, | ||||
| 	realm = canon_princ->realm; | ||||
|     else if (upn_princ) | ||||
| 	realm = upn_princ->realm; | ||||
|     else | ||||
| 	realm = NULL; | ||||
|     else { | ||||
| 	ret = EINVAL; | ||||
| 	goto out; | ||||
|     } | ||||
|  | ||||
|     ret = _krb5_store_utf8_as_ucs2le_at_offset(sp, (off_t)-1, realm); | ||||
|     if (ret) | ||||
| @@ -789,17 +806,23 @@ build_upn_dns_info(krb5_context context, | ||||
|     flags = 0; | ||||
|     if (upn_princ) | ||||
| 	flags |= PAC_EXTRA_LOGON_INFO_FLAGS_UPN_DEFAULTED; | ||||
|     if (canon_princ) | ||||
|     if (canon_princ || sid) | ||||
| 	flags |= PAC_EXTRA_LOGON_INFO_FLAGS_HAS_SAM_NAME_AND_SID; | ||||
|  | ||||
|     ret = krb5_store_uint32(sp, flags); | ||||
|     if (ret) | ||||
| 	goto out; | ||||
|  | ||||
|     ret = _krb5_store_utf8_as_ucs2le_at_offset(sp, (off_t)-1, | ||||
| 					       canon_princ_name); | ||||
|     if (ret) | ||||
| 	goto out; | ||||
|     if (flags & PAC_EXTRA_LOGON_INFO_FLAGS_HAS_SAM_NAME_AND_SID) { | ||||
| 	ret = _krb5_store_utf8_as_ucs2le_at_offset(sp, (off_t)-1, | ||||
| 						   canon_princ_name); | ||||
| 	if (ret) | ||||
| 	    goto out; | ||||
|  | ||||
| 	ret = _krb5_store_data_at_offset(sp, (off_t)-1, sid); | ||||
| 	if (ret) | ||||
| 	    goto out; | ||||
|     } | ||||
|  | ||||
|     ret = krb5_storage_to_data(sp, upn_dns_info); | ||||
|     if (ret) | ||||
| @@ -1054,6 +1077,80 @@ out: | ||||
|     return ret; | ||||
| } | ||||
|  | ||||
| static krb5_error_code | ||||
| parse_attributes_info(krb5_context context, | ||||
| 		      const struct PAC_INFO_BUFFER *attributes_info, | ||||
| 		      const krb5_data *data, | ||||
| 		      uint64_t *pac_attributes) | ||||
| { | ||||
|     krb5_error_code ret; | ||||
|     krb5_storage *sp = NULL; | ||||
|     uint32_t flags_length; | ||||
|  | ||||
|     *pac_attributes = 0; | ||||
|  | ||||
|     sp = krb5_storage_from_readonly_mem((const char *)data->data + attributes_info->offset_lo, | ||||
| 					attributes_info->buffersize); | ||||
|     if (sp == NULL) | ||||
| 	return krb5_enomem(context); | ||||
|  | ||||
|     krb5_storage_set_flags(sp, KRB5_STORAGE_BYTEORDER_LE); | ||||
|  | ||||
|     ret = krb5_ret_uint32(sp, &flags_length); | ||||
|     if (ret == 0) { | ||||
| 	if (flags_length > 32) | ||||
| 	    ret = krb5_ret_uint64(sp, pac_attributes); | ||||
| 	else { | ||||
| 	    uint32_t pac_attributes32 = 0; | ||||
| 	    ret = krb5_ret_uint32(sp, &pac_attributes32); | ||||
| 	    *pac_attributes = pac_attributes32; | ||||
| 	} | ||||
|     } | ||||
|  | ||||
|     krb5_storage_free(sp); | ||||
|  | ||||
|     return ret; | ||||
| } | ||||
|  | ||||
| static krb5_error_code | ||||
| build_attributes_info(krb5_context context, | ||||
| 		      uint64_t pac_attributes, | ||||
| 		      krb5_data *attributes_info) | ||||
| { | ||||
|     krb5_error_code ret; | ||||
|     krb5_storage *sp = NULL; | ||||
|     uint32_t flags_length; | ||||
|  | ||||
|     krb5_data_zero(attributes_info); | ||||
|  | ||||
|     sp = krb5_storage_emem(); | ||||
|     if (sp == NULL) | ||||
| 	return krb5_enomem(context); | ||||
|  | ||||
|     krb5_storage_set_flags(sp, KRB5_STORAGE_BYTEORDER_LE); | ||||
|  | ||||
|     if (pac_attributes == 0) | ||||
| 	flags_length = 0; | ||||
|     else | ||||
| 	flags_length = 64 - rk_clzll(pac_attributes); | ||||
|     if (flags_length < KRB5_PAC_WAS_GIVEN_IMPLICITLY) | ||||
| 	flags_length = KRB5_PAC_WAS_GIVEN_IMPLICITLY; | ||||
|  | ||||
|     ret = krb5_store_uint32(sp, flags_length); | ||||
|     if (ret == 0) { | ||||
| 	if (flags_length > 32) | ||||
| 	    ret = krb5_store_uint64(sp, pac_attributes); | ||||
| 	else | ||||
| 	    ret = krb5_store_uint32(sp, (uint32_t)pac_attributes); | ||||
|     } | ||||
|     if (ret == 0) | ||||
| 	ret = krb5_storage_to_data(sp, attributes_info); | ||||
|  | ||||
|     krb5_storage_free(sp); | ||||
|  | ||||
|     return ret; | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * Verify the PAC. | ||||
|  * | ||||
| @@ -1169,12 +1266,19 @@ krb5_pac_verify(krb5_context context, | ||||
| 	if (ret) | ||||
| 	    return ret; | ||||
|  | ||||
| 	if (pac->canon_princ && | ||||
| 	if (principal && pac->canon_princ && | ||||
| 	    !krb5_realm_compare(context, principal, pac->canon_princ)) { | ||||
| 	    return KRB5KRB_AP_ERR_MODIFIED; | ||||
| 	} | ||||
|     } | ||||
|  | ||||
|     if (pac->attributes_info) { | ||||
| 	ret = parse_attributes_info(context, pac->attributes_info, &pac->data, | ||||
| 				    &pac->pac_attributes); | ||||
| 	if (ret) | ||||
| 	    return ret; | ||||
|     } | ||||
|  | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| @@ -1244,6 +1348,7 @@ _krb5_pac_sign(krb5_context context, | ||||
| 	       uint16_t rodc_id, | ||||
| 	       krb5_const_principal upn_princ, | ||||
| 	       krb5_const_principal canon_princ, | ||||
| 	       uint64_t *pac_attributes, /* optional */ | ||||
| 	       krb5_data *data) | ||||
| { | ||||
|     krb5_error_code ret; | ||||
| @@ -1256,10 +1361,12 @@ _krb5_pac_sign(krb5_context context, | ||||
|     size_t i, sz; | ||||
|     krb5_data logon, d; | ||||
|     krb5_data upn_dns_info; | ||||
|     krb5_data attributes_info; | ||||
|  | ||||
|     krb5_data_zero(&d); | ||||
|     krb5_data_zero(&logon); | ||||
|     krb5_data_zero(&upn_dns_info); | ||||
|     krb5_data_zero(&attributes_info); | ||||
|  | ||||
|     for (i = 0; i < p->pac->numbuffers; i++) { | ||||
| 	if (p->pac->buffers[i].type == PAC_SERVER_CHECKSUM) { | ||||
| @@ -1312,6 +1419,16 @@ _krb5_pac_sign(krb5_context context, | ||||
| 				       N_("PAC has multiple ticket checksums", "")); | ||||
| 		goto out; | ||||
| 	    } | ||||
| 	} else if (p->pac->buffers[i].type == PAC_ATTRIBUTES_INFO) { | ||||
| 	    if (p->attributes_info == NULL) { | ||||
| 		p->attributes_info = &p->pac->buffers[i]; | ||||
| 	    } | ||||
| 	    if (p->attributes_info != &p->pac->buffers[i]) { | ||||
| 		ret = KRB5KDC_ERR_BADOPTION; | ||||
| 		krb5_set_error_message(context, ret, | ||||
| 				       N_("PAC has multiple attributes info buffers", "")); | ||||
| 		goto out; | ||||
| 	    } | ||||
| 	} | ||||
|     } | ||||
|  | ||||
| @@ -1325,6 +1442,8 @@ _krb5_pac_sign(krb5_context context, | ||||
| 	num++; | ||||
|     if (p->ticket_sign_data.length != 0 && p->ticket_checksum == NULL) | ||||
| 	num++; | ||||
|     if (pac_attributes && p->attributes_info == NULL) | ||||
| 	num++; | ||||
|  | ||||
|     if (num) { | ||||
| 	void *ptr; | ||||
| @@ -1362,6 +1481,11 @@ _krb5_pac_sign(krb5_context context, | ||||
| 	    memset(p->ticket_checksum, 0, sizeof(*p->ticket_checksum)); | ||||
| 	    p->ticket_checksum->type = PAC_TICKET_CHECKSUM; | ||||
| 	} | ||||
| 	if (pac_attributes && p->attributes_info == NULL) { | ||||
| 	    p->attributes_info = &p->pac->buffers[p->pac->numbuffers++]; | ||||
| 	    memset(p->attributes_info, 0, sizeof(*p->attributes_info)); | ||||
| 	    p->attributes_info->type = PAC_ATTRIBUTES_INFO; | ||||
| 	} | ||||
|     } | ||||
|  | ||||
|     /* Calculate LOGON NAME */ | ||||
| @@ -1374,8 +1498,11 @@ _krb5_pac_sign(krb5_context context, | ||||
|     if (ret == 0) | ||||
|         ret = pac_checksum(context, priv_key, &priv_cksumtype, &priv_size); | ||||
|  | ||||
|     if (upn_princ || canon_princ) | ||||
| 	ret = build_upn_dns_info(context, upn_princ, canon_princ, &upn_dns_info); | ||||
|     if (ret == 0 && (upn_princ || canon_princ)) | ||||
| 	ret = build_upn_dns_info(context, upn_princ, canon_princ, NULL, &upn_dns_info); | ||||
|  | ||||
|     if (ret == 0 && pac_attributes) | ||||
| 	ret = build_attributes_info(context, *pac_attributes, &attributes_info); | ||||
|  | ||||
|     /* Encode PAC */ | ||||
|     if (ret == 0) { | ||||
| @@ -1440,13 +1567,20 @@ _krb5_pac_sign(krb5_context context, | ||||
| 		ret = KRB5KDC_ERR_BADOPTION; | ||||
| 		goto out; | ||||
| 	    } | ||||
| 	} else if ((upn_princ || canon_princ) && | ||||
| 	} else if (upn_dns_info.length != 0 && | ||||
| 		   p->pac->buffers[i].type == PAC_UPN_DNS_INFO) { | ||||
| 	    len = krb5_storage_write(spdata, upn_dns_info.data, upn_dns_info.length); | ||||
| 	    if (upn_dns_info.length != len) { | ||||
| 		ret = KRB5KDC_ERR_BADOPTION; | ||||
| 		goto out; | ||||
| 	    } | ||||
| 	} else if (attributes_info.length != 0 && | ||||
| 		   p->pac->buffers[i].type == PAC_ATTRIBUTES_INFO) { | ||||
| 	    len = krb5_storage_write(spdata, attributes_info.data, attributes_info.length); | ||||
| 	    if (attributes_info.length != len) { | ||||
| 		ret = KRB5KDC_ERR_BADOPTION; | ||||
| 		goto out; | ||||
| 	    } | ||||
| 	} else { | ||||
| 	    len = p->pac->buffers[i].buffersize; | ||||
| 	    ptr = (char *)p->data.data + p->pac->buffers[i].offset_lo; | ||||
| @@ -1537,6 +1671,7 @@ _krb5_pac_sign(krb5_context context, | ||||
|  | ||||
|     krb5_data_free(&logon); | ||||
|     krb5_data_free(&upn_dns_info); | ||||
|     krb5_data_free(&attributes_info); | ||||
|     krb5_storage_free(sp); | ||||
|     krb5_storage_free(spdata); | ||||
|  | ||||
| @@ -1545,6 +1680,7 @@ out: | ||||
|     krb5_data_free(&d); | ||||
|     krb5_data_free(&logon); | ||||
|     krb5_data_free(&upn_dns_info); | ||||
|     krb5_data_free(&attributes_info); | ||||
|     if (sp) | ||||
| 	krb5_storage_free(sp); | ||||
|     if (spdata) | ||||
| @@ -1614,12 +1750,33 @@ _krb5_pac_get_canon_principal(krb5_context context, | ||||
| { | ||||
|     *canon_princ = NULL; | ||||
|  | ||||
|     if (pac->canon_princ == NULL) | ||||
|     if (pac->canon_princ == NULL) { | ||||
| 	krb5_set_error_message(context, ENOENT, | ||||
| 			       "PAC missing UPN DNS info buffer"); | ||||
| 	return ENOENT; | ||||
|     } | ||||
|  | ||||
|     return krb5_copy_principal(context, pac->canon_princ, canon_princ); | ||||
| } | ||||
|  | ||||
| KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL | ||||
| _krb5_pac_get_attributes_info(krb5_context context, | ||||
| 			      krb5_pac pac, | ||||
| 			      uint64_t *pac_attributes) | ||||
| { | ||||
|     *pac_attributes = 0; | ||||
|  | ||||
|     if (pac->attributes_info == NULL) { | ||||
| 	krb5_set_error_message(context, ENOENT, | ||||
| 			       "PAC missing attributes info buffer"); | ||||
| 	return ENOENT; | ||||
|     } | ||||
|  | ||||
|     *pac_attributes = pac->pac_attributes; | ||||
|  | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| static unsigned char single_zero = '\0'; | ||||
| static krb5_data single_zero_pac = { 1, &single_zero }; | ||||
|  | ||||
| @@ -1747,10 +1904,11 @@ _krb5_kdc_pac_sign_ticket(krb5_context context, | ||||
| 			  const krb5_keyblock *server_key, | ||||
| 			  const krb5_keyblock *kdc_key, | ||||
| 			  uint16_t rodc_id, | ||||
| 			  krb5_const_principal upn, | ||||
| 			  krb5_const_principal canon_name, | ||||
| 			  krb5_boolean add_ticket_sig, | ||||
| 			  EncTicketPart *tkt, | ||||
| 			  krb5_const_principal upn, | ||||
| 			  krb5_const_principal canon_name) | ||||
| 			  uint64_t *pac_attributes) /* optional */ | ||||
| { | ||||
|     krb5_error_code ret; | ||||
|     krb5_data tkt_data; | ||||
| @@ -1785,7 +1943,8 @@ _krb5_kdc_pac_sign_ticket(krb5_context context, | ||||
|     } | ||||
|  | ||||
|     ret = _krb5_pac_sign(context, pac, tkt->authtime, client, server_key, | ||||
| 			 kdc_key, rodc_id, upn, canon_name, &rspac); | ||||
| 			 kdc_key, rodc_id, upn, canon_name, | ||||
| 			 pac_attributes, &rspac); | ||||
|     if (ret == 0) | ||||
|         ret = _kdc_tkt_insert_pac(context, tkt, &rspac); | ||||
|     krb5_data_free(&rspac); | ||||
|   | ||||
| @@ -897,7 +897,8 @@ check_ticket_signature(krb5_context context, | ||||
| 	t_err(context, tkt->name, "remove_AuthorizationData", ret); | ||||
|  | ||||
|     ret = _krb5_kdc_pac_sign_ticket(context, pac, client, tkt->key, | ||||
| 				    tkt->kdc_key, tkt->rodc_id, signedticket, &et); | ||||
| 				    tkt->kdc_key, tkt->rodc_id, | ||||
| 				    NULL, NULL, signedticket, &et, NULL); | ||||
|     if (ret) | ||||
| 	t_err(context, tkt->name, "_krb5_kdc_pac_sign_ticket", ret); | ||||
|  | ||||
| @@ -915,7 +916,8 @@ check_ticket_signature(krb5_context context, | ||||
| 	t_err(context, tkt->name, "remove_AuthorizationData 2", ret); | ||||
|  | ||||
|     ret = _krb5_kdc_pac_sign_ticket(context, pac, client, tkt->key, | ||||
| 				    tkt->kdc_key, tkt->rodc_id, signedticket, &et); | ||||
| 				    tkt->kdc_key, tkt->rodc_id, | ||||
| 				    NULL, NULL, signedticket, &et, NULL); | ||||
|     if (ret) | ||||
| 	t_err(context, tkt->name, "_krb5_kdcsignedticketsign_ticket 2", ret); | ||||
|  | ||||
| @@ -978,7 +980,8 @@ main(int argc, char **argv) | ||||
| 	krb5_err(context, 1, ret, "krb5_pac_verify"); | ||||
|  | ||||
|     ret = _krb5_pac_sign(context, pac, authtime, p, | ||||
| 			 &member_keyblock, &kdc_keyblock, 0, &data); | ||||
| 			 &member_keyblock, &kdc_keyblock, 0, NULL, NULL, | ||||
| 			 NULL, &data); | ||||
|     if (ret) | ||||
| 	krb5_err(context, 1, ret, "_krb5_pac_sign"); | ||||
|  | ||||
| @@ -1034,7 +1037,8 @@ main(int argc, char **argv) | ||||
| 	free(list); | ||||
|  | ||||
| 	ret = _krb5_pac_sign(context, pac2, authtime, p, | ||||
| 			     &member_keyblock, &kdc_keyblock, 0, &data); | ||||
| 			     &member_keyblock, &kdc_keyblock, 0, | ||||
| 			     NULL, NULL, NULL, &data); | ||||
| 	if (ret) | ||||
| 	    krb5_err(context, 1, ret, "_krb5_pac_sign 4"); | ||||
|  | ||||
| @@ -1076,34 +1080,6 @@ main(int argc, char **argv) | ||||
|     krb5_pac_free(context, pac); | ||||
|     krb5_free_principal(context, p2); | ||||
|  | ||||
|     /* | ||||
|      * check extra logon info PAC | ||||
|      */ | ||||
|     ret = krb5_pac_parse(context, extra_logon_info, | ||||
| 			 sizeof(extra_logon_info) - 1, &pac); | ||||
|     if (ret) | ||||
| 	krb5_err(context, 1, ret, "krb5_pac_parse"); | ||||
|  | ||||
|     { | ||||
| 	krb5_principal upn = NULL; | ||||
| 	uint32_t flags = 0; | ||||
| 	krb5_principal sam_name = NULL; | ||||
| 	krb5_data sid; | ||||
|  | ||||
| 	krb5_data_zero(&sid); | ||||
|  | ||||
| 	ret = _krb5_pac_get_upn_dns_info(context, pac, &upn, | ||||
| 					 &flags, &sam_name, &sid); | ||||
| 	if (ret) | ||||
| 	    krb5_err(context, 1, ret, "_krb5_pac_get_upn_dns_info"); | ||||
|  | ||||
| 	krb5_free_principal(context, upn); | ||||
| 	krb5_free_principal(context, sam_name); | ||||
| 	krb5_data_free(&sid); | ||||
|     } | ||||
|  | ||||
|     krb5_pac_free(context, pac); | ||||
|  | ||||
|     /* | ||||
|      * Test empty free | ||||
|      */ | ||||
| @@ -1161,7 +1137,8 @@ main(int argc, char **argv) | ||||
|     } | ||||
|  | ||||
|     ret = _krb5_pac_sign(context, pac, authtime, p, | ||||
| 			 &member_keyblock, &kdc_keyblock, 0, &data); | ||||
| 			 &member_keyblock, &kdc_keyblock, 0, | ||||
| 			 NULL, NULL, NULL, &data); | ||||
|     if (ret) | ||||
| 	krb5_err(context, 1, ret, "_krb5_pac_sign"); | ||||
|  | ||||
| @@ -1193,6 +1170,33 @@ main(int argc, char **argv) | ||||
|     krb5_pac_free(context, pac); | ||||
|     krb5_free_principal(context, p); | ||||
|  | ||||
|     /* | ||||
|      * check extra logon info PAC | ||||
|      */ | ||||
|     ret = krb5_pac_parse(context, extra_logon_info, | ||||
| 			 sizeof(extra_logon_info) - 1, &pac); | ||||
|     if (ret) | ||||
| 	krb5_err(context, 1, ret, "krb5_pac_parse"); | ||||
|  | ||||
|     ret = krb5_pac_verify(context, pac, 0, NULL, NULL, NULL); | ||||
|     if (ret) | ||||
| 	krb5_err(context, 1, ret, "krb5_pac_verify"); | ||||
|  | ||||
|     ret = krb5_parse_name(context, "c9d801a8_0@EXAMPLE.COM", &p); | ||||
|     if (ret) | ||||
| 	krb5_err(context, 1, ret, "_krb5_pac_get_canon_principal"); | ||||
|  | ||||
|     ret = _krb5_pac_get_canon_principal(context, pac, &p2); | ||||
|     if (ret) | ||||
| 	krb5_err(context, 1, ret, "_krb5_pac_get_canon_principal"); | ||||
|  | ||||
|     if (!krb5_principal_compare(context, p, p2)) | ||||
| 	krb5_errx(context, 1, "canon principal doesn't match"); | ||||
|  | ||||
|     krb5_pac_free(context, pac); | ||||
|     krb5_free_principal(context, p); | ||||
|     krb5_free_principal(context, p2); | ||||
|  | ||||
|     /* Test PAC ticket-signature */ | ||||
|     { | ||||
| 	const struct test_pac_ticket *tkt; | ||||
|   | ||||
| @@ -814,6 +814,7 @@ HEIMDAL_KRB5_2.0 { | ||||
| 		_krb5_get_int; | ||||
| 		_krb5_get_int64; | ||||
| 		_krb5_pac_sign; | ||||
| 		_krb5_pac_get_attributes_info; | ||||
| 		_krb5_pac_get_canon_principal; | ||||
| 		_krb5_kdc_pac_sign_ticket; | ||||
| 		_krb5_kdc_pac_ticket_parse; | ||||
| @@ -833,7 +834,6 @@ HEIMDAL_KRB5_2.0 { | ||||
| 		_krb5_crypto_set_flags; | ||||
| 		_krb5_make_pa_enc_challenge; | ||||
| 		_krb5_validate_pa_enc_challenge; | ||||
| 		_krb5_store_utf8_as_ucs2le_at_offset; | ||||
|  | ||||
| 		# kinit helper | ||||
| 		krb5_get_init_creds_opt_set_pkinit_user_certs; | ||||
|   | ||||
| @@ -23,13 +23,14 @@ pac_generate(void *ctx, krb5_context context, | ||||
| 	     struct hdb_entry_ex *client, | ||||
| 	     struct hdb_entry_ex *server, | ||||
| 	     const krb5_keyblock *pk_replykey, | ||||
| 	     const krb5_boolean *pac_request, | ||||
| 	     uint64_t pac_attributes, | ||||
| 	     krb5_pac *pac) | ||||
| { | ||||
|     krb5_error_code ret; | ||||
|     krb5_data data; | ||||
|  | ||||
|     if (pac_request != NULL && *pac_request == FALSE) { | ||||
|     if ((pac_attributes & (KRB5_PAC_WAS_REQUESTED | | ||||
| 			   KRB5_PAC_WAS_GIVEN_IMPLICITLY)) == 0) { | ||||
| 	*pac = NULL; | ||||
| 	return 0; | ||||
|     } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 Luke Howard
					Luke Howard