2 * PROJECT: Local Security Authority Server DLL
3 * LICENSE: GPL - See COPYING in the top level directory
4 * FILE: dll/win32/lsasrv/policy.c
5 * PURPOSE: Policy object routines
6 * COPYRIGHT: Copyright 2011 Eric Kohl
9 /* INCLUDES ****************************************************************/
13 WINE_DEFAULT_DEBUG_CHANNEL(lsasrv
);
16 /* FUNCTIONS ***************************************************************/
20 LsaIOpenPolicyTrusted(OUT LSAPR_HANDLE
*PolicyHandle
)
22 PLSA_DB_OBJECT PolicyObject
;
25 TRACE("(%p)\n", PolicyHandle
);
27 Status
= LsapOpenDbObject(NULL
,
34 if (NT_SUCCESS(Status
))
35 *PolicyHandle
= (LSAPR_HANDLE
)PolicyObject
;
42 LsarQueryAuditLog(PLSA_DB_OBJECT PolicyObject
,
43 PLSAPR_POLICY_INFORMATION
*PolicyInformation
)
45 PPOLICY_AUDIT_LOG_INFO AuditLogInfo
= NULL
;
49 *PolicyInformation
= NULL
;
51 AttributeSize
= sizeof(POLICY_AUDIT_LOG_INFO
);
52 AuditLogInfo
= MIDL_user_allocate(AttributeSize
);
53 if (AuditLogInfo
== NULL
)
54 return STATUS_INSUFFICIENT_RESOURCES
;
56 Status
= LsapGetObjectAttribute(PolicyObject
,
60 if (!NT_SUCCESS(Status
))
62 MIDL_user_free(AuditLogInfo
);
66 *PolicyInformation
= (PLSAPR_POLICY_INFORMATION
)AuditLogInfo
;
74 LsarQueryAuditEvents(PLSA_DB_OBJECT PolicyObject
,
75 PLSAPR_POLICY_INFORMATION
*PolicyInformation
)
77 PLSAP_POLICY_AUDIT_EVENTS_DATA AuditData
= NULL
;
78 PLSAPR_POLICY_AUDIT_EVENTS_INFO p
= NULL
;
80 NTSTATUS Status
= STATUS_SUCCESS
;
82 *PolicyInformation
= NULL
;
85 Status
= LsapGetObjectAttribute(PolicyObject
,
89 if (!NT_SUCCESS(Status
))
92 if (AttributeSize
> 0)
94 AuditData
= MIDL_user_allocate(AttributeSize
);
95 if (AuditData
== NULL
)
96 return STATUS_INSUFFICIENT_RESOURCES
;
98 Status
= LsapGetObjectAttribute(PolicyObject
,
102 if (!NT_SUCCESS(Status
))
105 p
= MIDL_user_allocate(sizeof(LSAPR_POLICY_AUDIT_EVENTS_INFO
));
108 Status
= STATUS_INSUFFICIENT_RESOURCES
;
112 p
->AuditingMode
= AuditData
->AuditingMode
;
113 p
->MaximumAuditEventCount
= AuditData
->MaximumAuditEventCount
;
115 p
->EventAuditingOptions
= MIDL_user_allocate(AuditData
->MaximumAuditEventCount
* sizeof(DWORD
));
116 if (p
->EventAuditingOptions
== NULL
)
118 Status
= STATUS_INSUFFICIENT_RESOURCES
;
122 memcpy(p
->EventAuditingOptions
,
123 &(AuditData
->AuditEvents
[0]),
124 AuditData
->MaximumAuditEventCount
* sizeof(DWORD
));
127 *PolicyInformation
= (PLSAPR_POLICY_INFORMATION
)p
;
130 if (!NT_SUCCESS(Status
))
132 if (p
->EventAuditingOptions
!= NULL
)
133 MIDL_user_free(p
->EventAuditingOptions
);
139 if (AuditData
!= NULL
)
140 MIDL_user_free(AuditData
);
142 return STATUS_SUCCESS
;
147 LsarQueryPrimaryDomain(PLSA_DB_OBJECT PolicyObject
,
148 PLSAPR_POLICY_INFORMATION
*PolicyInformation
)
150 PLSAPR_POLICY_PRIMARY_DOM_INFO p
= NULL
;
151 PUNICODE_STRING DomainName
;
155 *PolicyInformation
= NULL
;
157 p
= MIDL_user_allocate(sizeof(LSAPR_POLICY_PRIMARY_DOM_INFO
));
159 return STATUS_INSUFFICIENT_RESOURCES
;
163 Status
= LsapGetObjectAttribute(PolicyObject
,
167 if (!NT_SUCCESS(Status
))
172 if (AttributeSize
> 0)
174 DomainName
= MIDL_user_allocate(AttributeSize
);
175 if (DomainName
== NULL
)
177 Status
= STATUS_INSUFFICIENT_RESOURCES
;
181 Status
= LsapGetObjectAttribute(PolicyObject
,
185 if (Status
== STATUS_SUCCESS
)
187 DomainName
->Buffer
= (LPWSTR
)((ULONG_PTR
)DomainName
+ (ULONG_PTR
)DomainName
->Buffer
);
189 TRACE("PrimaryDomainName: %wZ\n", DomainName
);
191 p
->Name
.Buffer
= MIDL_user_allocate(DomainName
->MaximumLength
);
192 if (p
->Name
.Buffer
== NULL
)
194 MIDL_user_free(DomainName
);
195 Status
= STATUS_INSUFFICIENT_RESOURCES
;
199 p
->Name
.Length
= DomainName
->Length
;
200 p
->Name
.MaximumLength
= DomainName
->MaximumLength
;
201 memcpy(p
->Name
.Buffer
,
203 DomainName
->MaximumLength
);
206 MIDL_user_free(DomainName
);
211 Status
= LsapGetObjectAttribute(PolicyObject
,
215 if (!NT_SUCCESS(Status
))
220 if (AttributeSize
> 0)
222 p
->Sid
= MIDL_user_allocate(AttributeSize
);
225 Status
= STATUS_INSUFFICIENT_RESOURCES
;
229 Status
= LsapGetObjectAttribute(PolicyObject
,
235 *PolicyInformation
= (PLSAPR_POLICY_INFORMATION
)p
;
238 if (!NT_SUCCESS(Status
))
243 MIDL_user_free(p
->Name
.Buffer
);
246 MIDL_user_free(p
->Sid
);
257 LsarQueryPdAccount(PLSA_DB_OBJECT PolicyObject
,
258 PLSAPR_POLICY_INFORMATION
*PolicyInformation
)
260 PLSAPR_POLICY_PD_ACCOUNT_INFO PdAccountInfo
= NULL
;
262 *PolicyInformation
= NULL
;
264 PdAccountInfo
= MIDL_user_allocate(sizeof(LSAPR_POLICY_PD_ACCOUNT_INFO
));
265 if (PdAccountInfo
== NULL
)
266 return STATUS_INSUFFICIENT_RESOURCES
;
268 PdAccountInfo
->Name
.Length
= 0;
269 PdAccountInfo
->Name
.MaximumLength
= 0;
270 PdAccountInfo
->Name
.Buffer
= NULL
;
272 *PolicyInformation
= (PLSAPR_POLICY_INFORMATION
)PdAccountInfo
;
274 return STATUS_SUCCESS
;
279 LsarQueryAccountDomain(PLSA_DB_OBJECT PolicyObject
,
280 PLSAPR_POLICY_INFORMATION
*PolicyInformation
)
282 PLSAPR_POLICY_ACCOUNT_DOM_INFO p
= NULL
;
283 PUNICODE_STRING DomainName
;
284 ULONG AttributeSize
= 0;
287 *PolicyInformation
= NULL
;
289 p
= MIDL_user_allocate(sizeof(LSAPR_POLICY_ACCOUNT_DOM_INFO
));
291 return STATUS_INSUFFICIENT_RESOURCES
;
294 Status
= LsapGetObjectAttribute(PolicyObject
,
298 if (!NT_SUCCESS(Status
))
303 if (AttributeSize
> 0)
305 DomainName
= MIDL_user_allocate(AttributeSize
);
306 if (DomainName
== NULL
)
308 Status
= STATUS_INSUFFICIENT_RESOURCES
;
312 Status
= LsapGetObjectAttribute(PolicyObject
,
316 if (Status
== STATUS_SUCCESS
)
318 DomainName
->Buffer
= (LPWSTR
)((ULONG_PTR
)DomainName
+ (ULONG_PTR
)DomainName
->Buffer
);
320 TRACE("AccountDomainName: %wZ\n", DomainName
);
322 p
->DomainName
.Buffer
= MIDL_user_allocate(DomainName
->MaximumLength
);
323 if (p
->DomainName
.Buffer
== NULL
)
325 MIDL_user_free(DomainName
);
326 Status
= STATUS_INSUFFICIENT_RESOURCES
;
330 p
->DomainName
.Length
= DomainName
->Length
;
331 p
->DomainName
.MaximumLength
= DomainName
->MaximumLength
;
332 memcpy(p
->DomainName
.Buffer
,
334 DomainName
->MaximumLength
);
337 MIDL_user_free(DomainName
);
342 Status
= LsapGetObjectAttribute(PolicyObject
,
346 if (!NT_SUCCESS(Status
))
351 if (AttributeSize
> 0)
353 p
->Sid
= MIDL_user_allocate(AttributeSize
);
356 Status
= STATUS_INSUFFICIENT_RESOURCES
;
360 Status
= LsapGetObjectAttribute(PolicyObject
,
366 *PolicyInformation
= (PLSAPR_POLICY_INFORMATION
)p
;
369 if (!NT_SUCCESS(Status
))
373 if (p
->DomainName
.Buffer
)
374 MIDL_user_free(p
->DomainName
.Buffer
);
377 MIDL_user_free(p
->Sid
);
388 LsarQueryServerRole(PLSA_DB_OBJECT PolicyObject
,
389 PLSAPR_POLICY_INFORMATION
*PolicyInformation
)
391 PPOLICY_LSA_SERVER_ROLE_INFO ServerRoleInfo
= NULL
;
395 *PolicyInformation
= NULL
;
397 AttributeSize
= sizeof(POLICY_LSA_SERVER_ROLE_INFO
);
398 ServerRoleInfo
= MIDL_user_allocate(AttributeSize
);
399 if (ServerRoleInfo
== NULL
)
400 return STATUS_INSUFFICIENT_RESOURCES
;
402 Status
= LsapGetObjectAttribute(PolicyObject
,
406 if (Status
== STATUS_OBJECT_NAME_NOT_FOUND
)
408 ServerRoleInfo
->LsaServerRole
= PolicyServerRolePrimary
;
409 Status
= STATUS_SUCCESS
;
412 if (!NT_SUCCESS(Status
))
414 MIDL_user_free(ServerRoleInfo
);
418 *PolicyInformation
= (PLSAPR_POLICY_INFORMATION
)ServerRoleInfo
;
426 LsarQueryReplicaSource(PLSA_DB_OBJECT PolicyObject
,
427 PLSAPR_POLICY_INFORMATION
*PolicyInformation
)
430 *PolicyInformation
= NULL
;
431 return STATUS_NOT_IMPLEMENTED
;
436 LsarQueryDefaultQuota(PLSA_DB_OBJECT PolicyObject
,
437 PLSAPR_POLICY_INFORMATION
*PolicyInformation
)
439 PPOLICY_DEFAULT_QUOTA_INFO QuotaInfo
= NULL
;
443 *PolicyInformation
= NULL
;
445 AttributeSize
= sizeof(POLICY_DEFAULT_QUOTA_INFO
);
446 QuotaInfo
= MIDL_user_allocate(AttributeSize
);
447 if (QuotaInfo
== NULL
)
448 return STATUS_INSUFFICIENT_RESOURCES
;
450 Status
= LsapGetObjectAttribute(PolicyObject
,
454 if (!NT_SUCCESS(Status
))
456 MIDL_user_free(QuotaInfo
);
460 *PolicyInformation
= (PLSAPR_POLICY_INFORMATION
)QuotaInfo
;
468 LsarQueryModification(PLSA_DB_OBJECT PolicyObject
,
469 PLSAPR_POLICY_INFORMATION
*PolicyInformation
)
471 PPOLICY_MODIFICATION_INFO Info
= NULL
;
475 *PolicyInformation
= NULL
;
477 AttributeSize
= sizeof(POLICY_MODIFICATION_INFO
);
478 Info
= MIDL_user_allocate(AttributeSize
);
480 return STATUS_INSUFFICIENT_RESOURCES
;
482 Status
= LsapGetObjectAttribute(PolicyObject
,
486 if (!NT_SUCCESS(Status
))
488 MIDL_user_free(Info
);
492 *PolicyInformation
= (PLSAPR_POLICY_INFORMATION
)Info
;
500 LsarQueryAuditFull(PLSA_DB_OBJECT PolicyObject
,
501 PLSAPR_POLICY_INFORMATION
*PolicyInformation
)
503 PPOLICY_AUDIT_FULL_QUERY_INFO AuditFullInfo
= NULL
;
507 *PolicyInformation
= NULL
;
509 AttributeSize
= sizeof(POLICY_AUDIT_FULL_QUERY_INFO
);
510 AuditFullInfo
= MIDL_user_allocate(AttributeSize
);
511 if (AuditFullInfo
== NULL
)
512 return STATUS_INSUFFICIENT_RESOURCES
;
514 Status
= LsapGetObjectAttribute(PolicyObject
,
518 if (!NT_SUCCESS(Status
))
520 MIDL_user_free(AuditFullInfo
);
524 *PolicyInformation
= (PLSAPR_POLICY_INFORMATION
)AuditFullInfo
;
532 LsarQueryDnsDomain(PLSA_DB_OBJECT PolicyObject
,
533 PLSAPR_POLICY_INFORMATION
*PolicyInformation
)
535 PLSAPR_POLICY_DNS_DOMAIN_INFO p
= NULL
;
536 PUNICODE_STRING DomainName
;
540 *PolicyInformation
= NULL
;
542 p
= MIDL_user_allocate(sizeof(LSAPR_POLICY_DNS_DOMAIN_INFO
));
544 return STATUS_INSUFFICIENT_RESOURCES
;
546 /* Primary Domain Name */
548 Status
= LsapGetObjectAttribute(PolicyObject
,
552 if (!NT_SUCCESS(Status
))
557 if (AttributeSize
> 0)
559 DomainName
= MIDL_user_allocate(AttributeSize
);
560 if (DomainName
== NULL
)
562 Status
= STATUS_INSUFFICIENT_RESOURCES
;
566 Status
= LsapGetObjectAttribute(PolicyObject
,
570 if (Status
== STATUS_SUCCESS
)
572 DomainName
->Buffer
= (LPWSTR
)((ULONG_PTR
)DomainName
+ (ULONG_PTR
)DomainName
->Buffer
);
574 TRACE("PrimaryDomainName: %wZ\n", DomainName
);
576 p
->Name
.Buffer
= MIDL_user_allocate(DomainName
->MaximumLength
);
577 if (p
->Name
.Buffer
== NULL
)
579 MIDL_user_free(DomainName
);
580 Status
= STATUS_INSUFFICIENT_RESOURCES
;
584 p
->Name
.Length
= DomainName
->Length
;
585 p
->Name
.MaximumLength
= DomainName
->MaximumLength
;
586 memcpy(p
->Name
.Buffer
,
588 DomainName
->MaximumLength
);
591 MIDL_user_free(DomainName
);
594 /* Primary Domain SID */
596 Status
= LsapGetObjectAttribute(PolicyObject
,
600 if (!NT_SUCCESS(Status
))
605 if (AttributeSize
> 0)
607 p
->Sid
= MIDL_user_allocate(AttributeSize
);
610 Status
= STATUS_INSUFFICIENT_RESOURCES
;
614 Status
= LsapGetObjectAttribute(PolicyObject
,
620 /* DNS Domain Name */
622 Status
= LsapGetObjectAttribute(PolicyObject
,
626 if (!NT_SUCCESS(Status
))
629 if (AttributeSize
> 0)
631 DomainName
= MIDL_user_allocate(AttributeSize
);
632 if (DomainName
== NULL
)
634 Status
= STATUS_INSUFFICIENT_RESOURCES
;
638 Status
= LsapGetObjectAttribute(PolicyObject
,
642 if (Status
== STATUS_SUCCESS
)
644 DomainName
->Buffer
= (LPWSTR
)((ULONG_PTR
)DomainName
+ (ULONG_PTR
)DomainName
->Buffer
);
646 TRACE("DNS Domain Name: %wZ\n", DomainName
);
648 p
->DnsDomainName
.Buffer
= MIDL_user_allocate(DomainName
->MaximumLength
);
649 if (p
->DnsDomainName
.Buffer
== NULL
)
651 MIDL_user_free(DomainName
);
652 Status
= STATUS_INSUFFICIENT_RESOURCES
;
656 p
->DnsDomainName
.Length
= DomainName
->Length
;
657 p
->DnsDomainName
.MaximumLength
= DomainName
->MaximumLength
;
658 memcpy(p
->DnsDomainName
.Buffer
,
660 DomainName
->MaximumLength
);
663 MIDL_user_free(DomainName
);
666 /* DNS Forest Name */
668 Status
= LsapGetObjectAttribute(PolicyObject
,
672 if (!NT_SUCCESS(Status
))
675 if (AttributeSize
> 0)
677 DomainName
= MIDL_user_allocate(AttributeSize
);
678 if (DomainName
== NULL
)
680 Status
= STATUS_INSUFFICIENT_RESOURCES
;
684 Status
= LsapGetObjectAttribute(PolicyObject
,
688 if (Status
== STATUS_SUCCESS
)
690 DomainName
->Buffer
= (LPWSTR
)((ULONG_PTR
)DomainName
+ (ULONG_PTR
)DomainName
->Buffer
);
692 TRACE("DNS Forest Name: %wZ\n", DomainName
);
694 p
->DnsForestName
.Buffer
= MIDL_user_allocate(DomainName
->MaximumLength
);
695 if (p
->DnsForestName
.Buffer
== NULL
)
697 MIDL_user_free(DomainName
);
698 Status
= STATUS_INSUFFICIENT_RESOURCES
;
702 p
->DnsForestName
.Length
= DomainName
->Length
;
703 p
->DnsForestName
.MaximumLength
= DomainName
->MaximumLength
;
704 memcpy(p
->DnsForestName
.Buffer
,
706 DomainName
->MaximumLength
);
709 MIDL_user_free(DomainName
);
712 /* DNS Domain GUID */
713 AttributeSize
= sizeof(GUID
);
714 Status
= LsapGetObjectAttribute(PolicyObject
,
718 if (!NT_SUCCESS(Status
))
721 *PolicyInformation
= (PLSAPR_POLICY_INFORMATION
)p
;
724 if (!NT_SUCCESS(Status
))
729 MIDL_user_free(p
->Name
.Buffer
);
731 if (p
->DnsDomainName
.Buffer
)
732 MIDL_user_free(p
->DnsDomainName
.Buffer
);
734 if (p
->DnsForestName
.Buffer
)
735 MIDL_user_free(p
->DnsForestName
.Buffer
);
738 MIDL_user_free(p
->Sid
);
749 LsarQueryDnsDomainInt(PLSA_DB_OBJECT PolicyObject
,
750 PLSAPR_POLICY_INFORMATION
*PolicyInformation
)
753 *PolicyInformation
= NULL
;
754 return STATUS_NOT_IMPLEMENTED
;
759 LsarQueryLocalAccountDomain(PLSA_DB_OBJECT PolicyObject
,
760 PLSAPR_POLICY_INFORMATION
*PolicyInformation
)
763 *PolicyInformation
= NULL
;
764 return STATUS_NOT_IMPLEMENTED
;
769 LsarSetAuditLog(PLSA_DB_OBJECT PolicyObject
,
770 PPOLICY_AUDIT_LOG_INFO Info
)
772 TRACE("(%p %p)\n", PolicyObject
, Info
);
774 return LsapSetObjectAttribute(PolicyObject
,
777 sizeof(POLICY_AUDIT_LOG_INFO
));
782 LsarSetAuditEvents(PLSA_DB_OBJECT PolicyObject
,
783 PLSAPR_POLICY_AUDIT_EVENTS_INFO Info
)
786 return STATUS_NOT_IMPLEMENTED
;
791 LsarSetPrimaryDomain(PLSA_DB_OBJECT PolicyObject
,
792 PLSAPR_POLICY_PRIMARY_DOM_INFO Info
)
794 PUNICODE_STRING Buffer
;
799 TRACE("(%p %p)\n", PolicyObject
, Info
);
801 Length
= sizeof(UNICODE_STRING
) + Info
->Name
.MaximumLength
;
802 Buffer
= RtlAllocateHeap(RtlGetProcessHeap(),
806 return STATUS_INSUFFICIENT_RESOURCES
;
808 Buffer
->Length
= Info
->Name
.Length
;
809 Buffer
->MaximumLength
= Info
->Name
.MaximumLength
;
810 Buffer
->Buffer
= (LPWSTR
)sizeof(UNICODE_STRING
);
811 Ptr
= (LPWSTR
)((ULONG_PTR
)Buffer
+ sizeof(UNICODE_STRING
));
812 memcpy(Ptr
, Info
->Name
.Buffer
, Info
->Name
.MaximumLength
);
814 Status
= LsapSetObjectAttribute(PolicyObject
,
819 RtlFreeHeap(RtlGetProcessHeap(), 0, Buffer
);
821 if (!NT_SUCCESS(Status
))
825 if (Info
->Sid
!= NULL
)
826 Length
= RtlLengthSid(Info
->Sid
);
828 Status
= LsapSetObjectAttribute(PolicyObject
,
838 LsarSetAccountDomain(PLSA_DB_OBJECT PolicyObject
,
839 PLSAPR_POLICY_ACCOUNT_DOM_INFO Info
)
841 PUNICODE_STRING Buffer
;
846 TRACE("(%p %p)\n", PolicyObject
, Info
);
848 Length
= sizeof(UNICODE_STRING
) + Info
->DomainName
.MaximumLength
;
849 Buffer
= RtlAllocateHeap(RtlGetProcessHeap(),
853 return STATUS_INSUFFICIENT_RESOURCES
;
855 Buffer
->Length
= Info
->DomainName
.Length
;
856 Buffer
->MaximumLength
= Info
->DomainName
.MaximumLength
;
857 Buffer
->Buffer
= (LPWSTR
)sizeof(UNICODE_STRING
);
858 Ptr
= (LPWSTR
)((ULONG_PTR
)Buffer
+ sizeof(UNICODE_STRING
));
859 memcpy(Ptr
, Info
->DomainName
.Buffer
, Info
->DomainName
.MaximumLength
);
861 Status
= LsapSetObjectAttribute(PolicyObject
,
866 RtlFreeHeap(RtlGetProcessHeap(), 0, Buffer
);
868 if (!NT_SUCCESS(Status
))
872 if (Info
->Sid
!= NULL
)
873 Length
= RtlLengthSid(Info
->Sid
);
875 Status
= LsapSetObjectAttribute(PolicyObject
,
885 LsarSetServerRole(PLSA_DB_OBJECT PolicyObject
,
886 PPOLICY_LSA_SERVER_ROLE_INFO Info
)
889 return STATUS_NOT_IMPLEMENTED
;
894 LsarSetReplicaSource(PLSA_DB_OBJECT PolicyObject
,
895 PPOLICY_LSA_REPLICA_SRCE_INFO Info
)
898 return STATUS_NOT_IMPLEMENTED
;
903 LsarSetDefaultQuota(PLSA_DB_OBJECT PolicyObject
,
904 PPOLICY_DEFAULT_QUOTA_INFO Info
)
906 TRACE("(%p %p)\n", PolicyObject
, Info
);
908 return LsapSetObjectAttribute(PolicyObject
,
911 sizeof(POLICY_DEFAULT_QUOTA_INFO
));
916 LsarSetModification(PLSA_DB_OBJECT PolicyObject
,
917 PPOLICY_MODIFICATION_INFO Info
)
920 return STATUS_NOT_IMPLEMENTED
;
925 LsarSetAuditFull(PLSA_DB_OBJECT PolicyObject
,
926 PPOLICY_AUDIT_FULL_QUERY_INFO Info
)
929 return STATUS_NOT_IMPLEMENTED
;
934 LsarSetDnsDomain(PLSA_DB_OBJECT PolicyObject
,
935 PLSAPR_POLICY_DNS_DOMAIN_INFO Info
)
938 return STATUS_NOT_IMPLEMENTED
;
943 LsarSetDnsDomainInt(PLSA_DB_OBJECT PolicyObject
,
944 PLSAPR_POLICY_DNS_DOMAIN_INFO Info
)
947 return STATUS_NOT_IMPLEMENTED
;
952 LsarSetLocalAccountDomain(PLSA_DB_OBJECT PolicyObject
,
953 PLSAPR_POLICY_ACCOUNT_DOM_INFO Info
)
956 return STATUS_NOT_IMPLEMENTED
;