talloc_unlink(mem_ctx, user_info_dc);
return code;
}
+
+krb5_error_code samba_kdc_check_device(TALLOC_CTX *mem_ctx,
+ krb5_context context,
+ struct ldb_context *samdb,
+ struct loadparm_context *lp_ctx,
+ struct samba_kdc_entry *device,
+ const krb5_const_pac device_pac,
+ const bool device_pac_is_trusted,
+ const struct authn_kerberos_client_policy *client_policy,
+ struct authn_audit_info **client_audit_info_out,
+ NTSTATUS *status_out)
+{
+ TALLOC_CTX *frame = NULL;
+ krb5_error_code code = 0;
+ NTSTATUS nt_status;
+ struct auth_user_info_dc *device_info = NULL;
+ struct authn_audit_info *client_audit_info = NULL;
+
+ if (status_out != NULL) {
+ *status_out = NT_STATUS_OK;
+ }
+
+ if (!authn_policy_device_restrictions_present(client_policy)) {
+ return 0;
+ }
+
+ if (device == NULL || device_pac == NULL) {
+ NTSTATUS out_status = NT_STATUS_INVALID_WORKSTATION;
+
+ nt_status = authn_kerberos_client_policy_audit_info(mem_ctx,
+ client_policy,
+ NULL /* client_info */,
+ AUTHN_AUDIT_EVENT_KERBEROS_DEVICE_RESTRICTION,
+ AUTHN_AUDIT_REASON_FAST_REQUIRED,
+ out_status,
+ client_audit_info_out);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ code = KRB5KRB_ERR_GENERIC;
+ } else if (authn_kerberos_client_policy_is_enforced(client_policy)) {
+ code = KRB5KDC_ERR_POLICY;
+
+ if (status_out != NULL) {
+ *status_out = out_status;
+ }
+ } else {
+ /* OK. */
+ code = 0;
+ }
+
+ goto out;
+ }
+
+ frame = talloc_stackframe();
+
+ if (device_pac_is_trusted) {
+ krb5_data device_logon_info;
+
+ enum ndr_err_code ndr_err;
+ DATA_BLOB device_logon_info_blob;
+
+ union PAC_INFO pac_logon_info;
+ union netr_Validation validation;
+
+ code = krb5_pac_get_buffer(context, device_pac,
+ PAC_TYPE_LOGON_INFO,
+ &device_logon_info);
+ if (code != 0) {
+ if (code == ENOENT) {
+ DBG_ERR("Device PAC is missing LOGON_INFO\n");
+ } else {
+ DBG_ERR("Error getting LOGON_INFO from device PAC\n");
+ }
+
+ goto out;
+ }
+
+ device_logon_info_blob = data_blob_const(device_logon_info.data,
+ device_logon_info.length);
+
+ ndr_err = ndr_pull_union_blob(&device_logon_info_blob, frame, &pac_logon_info,
+ PAC_TYPE_LOGON_INFO,
+ (ndr_pull_flags_fn_t)ndr_pull_PAC_INFO);
+ smb_krb5_free_data_contents(context, &device_logon_info);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ nt_status = ndr_map_error2ntstatus(ndr_err);
+ DBG_ERR("can't parse device PAC LOGON_INFO: %s\n",
+ nt_errstr(nt_status));
+
+ code = ndr_map_error2errno(ndr_err);
+ goto out;
+ }
+
+ /*
+ * This does a bit of unnecessary work, setting up fields we
+ * don’t care about — we only want the SIDs.
+ */
+ validation.sam3 = &pac_logon_info.logon_info.info->info3;
+ nt_status = make_user_info_dc_netlogon_validation(frame, "", 3, &validation,
+ true, /* This user was authenticated */
+ &device_info);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ code = EINVAL;
+ goto out;
+ }
+
+ /*
+ * We need to expand group memberships within our local domain,
+ * as the token might be generated by a trusted domain.
+ */
+ nt_status = authsam_update_user_info_dc(frame,
+ samdb,
+ device_info);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ code = EINVAL;
+ goto out;
+ }
+ } else {
+ nt_status = samba_kdc_get_user_info_dc(frame,
+ device,
+ SAMBA_ASSERTED_IDENTITY_AUTHENTICATION_AUTHORITY,
+ SAMBA_CLAIMS_VALID_INCLUDE,
+ SAMBA_COMPOUNDED_AUTH_EXCLUDE,
+ &device_info);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ DBG_ERR("samba_kdc_get_user_info_dc failed: %s\n",
+ nt_errstr(nt_status));
+
+ code = KRB5KDC_ERR_TGT_REVOKED;
+ goto out;
+ }
+ }
+
+ nt_status = authn_policy_authenticate_from_device(frame,
+ samdb,
+ lp_ctx,
+ device_info,
+ client_policy,
+ &client_audit_info);
+ if (client_audit_info != NULL) {
+ *client_audit_info_out = talloc_move(mem_ctx, &client_audit_info);
+ }
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ if (NT_STATUS_EQUAL(nt_status, NT_STATUS_AUTHENTICATION_FIREWALL_FAILED)) {
+ code = KRB5KDC_ERR_POLICY;
+ } else {
+ code = KRB5KRB_ERR_GENERIC;
+ }
+
+ goto out;
+ }
+
+out:
+ talloc_free(frame);
+ return code;
+}