1*c2c66affSColin Finck /*
2*c2c66affSColin Finck * COPYRIGHT: See COPYING in the top level directory
3*c2c66affSColin Finck * PROJECT: ReactOS system libraries
4*c2c66affSColin Finck * FILE: lib/lsalib/lsa.c
5*c2c66affSColin Finck * PURPOSE: Client-side LSA functions
6*c2c66affSColin Finck * UPDATE HISTORY:
7*c2c66affSColin Finck * Created 05/08/00
8*c2c66affSColin Finck */
9*c2c66affSColin Finck
10*c2c66affSColin Finck /* INCLUDES ******************************************************************/
11*c2c66affSColin Finck
12*c2c66affSColin Finck #include <ndk/exfuncs.h>
13*c2c66affSColin Finck #include <ndk/lpctypes.h>
14*c2c66affSColin Finck #include <ndk/lpcfuncs.h>
15*c2c66affSColin Finck #include <ndk/mmfuncs.h>
16*c2c66affSColin Finck #include <ndk/rtlfuncs.h>
17*c2c66affSColin Finck #include <ndk/obfuncs.h>
18*c2c66affSColin Finck #include <lsass/lsass.h>
19*c2c66affSColin Finck
20*c2c66affSColin Finck #define NDEBUG
21*c2c66affSColin Finck #include <debug.h>
22*c2c66affSColin Finck
23*c2c66affSColin Finck /* FUNCTIONS *****************************************************************/
24*c2c66affSColin Finck
25*c2c66affSColin Finck /*
26*c2c66affSColin Finck * @implemented
27*c2c66affSColin Finck */
28*c2c66affSColin Finck NTSTATUS
29*c2c66affSColin Finck NTAPI
LsaCallAuthenticationPackage(IN HANDLE LsaHandle,IN ULONG AuthenticationPackage,IN PVOID ProtocolSubmitBuffer,IN ULONG SubmitBufferLength,OUT PVOID * ProtocolReturnBuffer,OUT PULONG ReturnBufferLength,OUT PNTSTATUS ProtocolStatus)30*c2c66affSColin Finck LsaCallAuthenticationPackage(IN HANDLE LsaHandle,
31*c2c66affSColin Finck IN ULONG AuthenticationPackage,
32*c2c66affSColin Finck IN PVOID ProtocolSubmitBuffer,
33*c2c66affSColin Finck IN ULONG SubmitBufferLength,
34*c2c66affSColin Finck OUT PVOID *ProtocolReturnBuffer,
35*c2c66affSColin Finck OUT PULONG ReturnBufferLength,
36*c2c66affSColin Finck OUT PNTSTATUS ProtocolStatus)
37*c2c66affSColin Finck {
38*c2c66affSColin Finck LSA_API_MSG ApiMessage;
39*c2c66affSColin Finck NTSTATUS Status;
40*c2c66affSColin Finck
41*c2c66affSColin Finck DPRINT1("LsaCallAuthenticationPackage()\n");
42*c2c66affSColin Finck
43*c2c66affSColin Finck ApiMessage.ApiNumber = LSASS_REQUEST_CALL_AUTHENTICATION_PACKAGE;
44*c2c66affSColin Finck ApiMessage.h.u1.s1.DataLength = LSA_PORT_DATA_SIZE(ApiMessage.CallAuthenticationPackage);
45*c2c66affSColin Finck ApiMessage.h.u1.s1.TotalLength = LSA_PORT_MESSAGE_SIZE;
46*c2c66affSColin Finck ApiMessage.h.u2.ZeroInit = 0;
47*c2c66affSColin Finck
48*c2c66affSColin Finck ApiMessage.CallAuthenticationPackage.Request.AuthenticationPackage = AuthenticationPackage;
49*c2c66affSColin Finck ApiMessage.CallAuthenticationPackage.Request.ProtocolSubmitBuffer = ProtocolSubmitBuffer;
50*c2c66affSColin Finck ApiMessage.CallAuthenticationPackage.Request.SubmitBufferLength = SubmitBufferLength;
51*c2c66affSColin Finck
52*c2c66affSColin Finck Status = ZwRequestWaitReplyPort(LsaHandle,
53*c2c66affSColin Finck (PPORT_MESSAGE)&ApiMessage,
54*c2c66affSColin Finck (PPORT_MESSAGE)&ApiMessage);
55*c2c66affSColin Finck if (!NT_SUCCESS(Status))
56*c2c66affSColin Finck {
57*c2c66affSColin Finck DPRINT1("ZwRequestWaitReplyPort() failed (Status 0x%08lx)\n", Status);
58*c2c66affSColin Finck return Status;
59*c2c66affSColin Finck }
60*c2c66affSColin Finck
61*c2c66affSColin Finck if (!NT_SUCCESS(ApiMessage.Status))
62*c2c66affSColin Finck {
63*c2c66affSColin Finck DPRINT1("ZwRequestWaitReplyPort() failed (ApiMessage.Status 0x%08lx)\n", ApiMessage.Status);
64*c2c66affSColin Finck return ApiMessage.Status;
65*c2c66affSColin Finck }
66*c2c66affSColin Finck
67*c2c66affSColin Finck *ProtocolReturnBuffer = ApiMessage.CallAuthenticationPackage.Reply.ProtocolReturnBuffer;
68*c2c66affSColin Finck *ReturnBufferLength = ApiMessage.CallAuthenticationPackage.Reply.ReturnBufferLength;
69*c2c66affSColin Finck *ProtocolStatus = ApiMessage.CallAuthenticationPackage.Reply.ProtocolStatus;
70*c2c66affSColin Finck
71*c2c66affSColin Finck return Status;
72*c2c66affSColin Finck }
73*c2c66affSColin Finck
74*c2c66affSColin Finck
75*c2c66affSColin Finck /*
76*c2c66affSColin Finck * @implemented
77*c2c66affSColin Finck */
78*c2c66affSColin Finck NTSTATUS
79*c2c66affSColin Finck NTAPI
LsaDeregisterLogonProcess(IN HANDLE LsaHandle)80*c2c66affSColin Finck LsaDeregisterLogonProcess(IN HANDLE LsaHandle)
81*c2c66affSColin Finck {
82*c2c66affSColin Finck LSA_API_MSG ApiMessage;
83*c2c66affSColin Finck NTSTATUS Status;
84*c2c66affSColin Finck
85*c2c66affSColin Finck DPRINT("LsaDeregisterLogonProcess()\n");
86*c2c66affSColin Finck
87*c2c66affSColin Finck ApiMessage.ApiNumber = LSASS_REQUEST_DEREGISTER_LOGON_PROCESS;
88*c2c66affSColin Finck ApiMessage.h.u1.s1.DataLength = LSA_PORT_DATA_SIZE(ApiMessage.DeregisterLogonProcess);
89*c2c66affSColin Finck ApiMessage.h.u1.s1.TotalLength = LSA_PORT_MESSAGE_SIZE;
90*c2c66affSColin Finck ApiMessage.h.u2.ZeroInit = 0;
91*c2c66affSColin Finck
92*c2c66affSColin Finck Status = ZwRequestWaitReplyPort(LsaHandle,
93*c2c66affSColin Finck (PPORT_MESSAGE)&ApiMessage,
94*c2c66affSColin Finck (PPORT_MESSAGE)&ApiMessage);
95*c2c66affSColin Finck if (!NT_SUCCESS(Status))
96*c2c66affSColin Finck {
97*c2c66affSColin Finck DPRINT1("ZwRequestWaitReplyPort() failed (Status 0x%08lx)\n", Status);
98*c2c66affSColin Finck return Status;
99*c2c66affSColin Finck }
100*c2c66affSColin Finck
101*c2c66affSColin Finck if (!NT_SUCCESS(ApiMessage.Status))
102*c2c66affSColin Finck {
103*c2c66affSColin Finck DPRINT1("ZwRequestWaitReplyPort() failed (ApiMessage.Status 0x%08lx)\n", ApiMessage.Status);
104*c2c66affSColin Finck return ApiMessage.Status;
105*c2c66affSColin Finck }
106*c2c66affSColin Finck
107*c2c66affSColin Finck ZwClose(LsaHandle);
108*c2c66affSColin Finck
109*c2c66affSColin Finck DPRINT("LsaDeregisterLogonProcess() done (Status 0x%08lx)\n", Status);
110*c2c66affSColin Finck
111*c2c66affSColin Finck return Status;
112*c2c66affSColin Finck }
113*c2c66affSColin Finck
114*c2c66affSColin Finck
115*c2c66affSColin Finck /*
116*c2c66affSColin Finck * @implemented
117*c2c66affSColin Finck */
118*c2c66affSColin Finck NTSTATUS
119*c2c66affSColin Finck NTAPI
LsaFreeReturnBuffer(IN PVOID Buffer)120*c2c66affSColin Finck LsaFreeReturnBuffer(IN PVOID Buffer)
121*c2c66affSColin Finck {
122*c2c66affSColin Finck SIZE_T Size = 0;
123*c2c66affSColin Finck
124*c2c66affSColin Finck return ZwFreeVirtualMemory(NtCurrentProcess(),
125*c2c66affSColin Finck &Buffer,
126*c2c66affSColin Finck &Size,
127*c2c66affSColin Finck MEM_RELEASE);
128*c2c66affSColin Finck }
129*c2c66affSColin Finck
130*c2c66affSColin Finck
131*c2c66affSColin Finck /*
132*c2c66affSColin Finck * @implemented
133*c2c66affSColin Finck */
134*c2c66affSColin Finck NTSTATUS
135*c2c66affSColin Finck NTAPI
LsaLookupAuthenticationPackage(IN HANDLE LsaHandle,IN PLSA_STRING PackageName,OUT PULONG AuthenticationPackage)136*c2c66affSColin Finck LsaLookupAuthenticationPackage(IN HANDLE LsaHandle,
137*c2c66affSColin Finck IN PLSA_STRING PackageName,
138*c2c66affSColin Finck OUT PULONG AuthenticationPackage)
139*c2c66affSColin Finck {
140*c2c66affSColin Finck LSA_API_MSG ApiMessage;
141*c2c66affSColin Finck NTSTATUS Status;
142*c2c66affSColin Finck
143*c2c66affSColin Finck /* Check the package name length */
144*c2c66affSColin Finck if (PackageName->Length > LSASS_MAX_PACKAGE_NAME_LENGTH)
145*c2c66affSColin Finck {
146*c2c66affSColin Finck return STATUS_NAME_TOO_LONG;
147*c2c66affSColin Finck }
148*c2c66affSColin Finck
149*c2c66affSColin Finck ApiMessage.ApiNumber = LSASS_REQUEST_LOOKUP_AUTHENTICATION_PACKAGE;
150*c2c66affSColin Finck ApiMessage.h.u1.s1.DataLength = LSA_PORT_DATA_SIZE(ApiMessage.LookupAuthenticationPackage);
151*c2c66affSColin Finck ApiMessage.h.u1.s1.TotalLength = LSA_PORT_MESSAGE_SIZE;
152*c2c66affSColin Finck ApiMessage.h.u2.ZeroInit = 0;
153*c2c66affSColin Finck
154*c2c66affSColin Finck ApiMessage.LookupAuthenticationPackage.Request.PackageNameLength = PackageName->Length;
155*c2c66affSColin Finck strncpy(ApiMessage.LookupAuthenticationPackage.Request.PackageName,
156*c2c66affSColin Finck PackageName->Buffer,
157*c2c66affSColin Finck ApiMessage.LookupAuthenticationPackage.Request.PackageNameLength);
158*c2c66affSColin Finck ApiMessage.LookupAuthenticationPackage.Request.PackageName[ApiMessage.LookupAuthenticationPackage.Request.PackageNameLength] = ANSI_NULL;
159*c2c66affSColin Finck
160*c2c66affSColin Finck Status = ZwRequestWaitReplyPort(LsaHandle,
161*c2c66affSColin Finck (PPORT_MESSAGE)&ApiMessage,
162*c2c66affSColin Finck (PPORT_MESSAGE)&ApiMessage);
163*c2c66affSColin Finck if (!NT_SUCCESS(Status))
164*c2c66affSColin Finck {
165*c2c66affSColin Finck return Status;
166*c2c66affSColin Finck }
167*c2c66affSColin Finck
168*c2c66affSColin Finck if (!NT_SUCCESS(ApiMessage.Status))
169*c2c66affSColin Finck {
170*c2c66affSColin Finck return ApiMessage.Status;
171*c2c66affSColin Finck }
172*c2c66affSColin Finck
173*c2c66affSColin Finck *AuthenticationPackage = ApiMessage.LookupAuthenticationPackage.Reply.Package;
174*c2c66affSColin Finck
175*c2c66affSColin Finck return Status;
176*c2c66affSColin Finck }
177*c2c66affSColin Finck
178*c2c66affSColin Finck
179*c2c66affSColin Finck /*
180*c2c66affSColin Finck * @implemented
181*c2c66affSColin Finck */
182*c2c66affSColin Finck NTSTATUS
183*c2c66affSColin Finck NTAPI
LsaLogonUser(IN HANDLE LsaHandle,IN PLSA_STRING OriginName,IN SECURITY_LOGON_TYPE LogonType,IN ULONG AuthenticationPackage,IN PVOID AuthenticationInformation,IN ULONG AuthenticationInformationLength,IN PTOKEN_GROUPS LocalGroups OPTIONAL,IN PTOKEN_SOURCE SourceContext,OUT PVOID * ProfileBuffer,OUT PULONG ProfileBufferLength,OUT PLUID LogonId,OUT PHANDLE Token,OUT PQUOTA_LIMITS Quotas,OUT PNTSTATUS SubStatus)184*c2c66affSColin Finck LsaLogonUser(IN HANDLE LsaHandle,
185*c2c66affSColin Finck IN PLSA_STRING OriginName,
186*c2c66affSColin Finck IN SECURITY_LOGON_TYPE LogonType,
187*c2c66affSColin Finck IN ULONG AuthenticationPackage,
188*c2c66affSColin Finck IN PVOID AuthenticationInformation,
189*c2c66affSColin Finck IN ULONG AuthenticationInformationLength,
190*c2c66affSColin Finck IN PTOKEN_GROUPS LocalGroups OPTIONAL,
191*c2c66affSColin Finck IN PTOKEN_SOURCE SourceContext,
192*c2c66affSColin Finck OUT PVOID *ProfileBuffer,
193*c2c66affSColin Finck OUT PULONG ProfileBufferLength,
194*c2c66affSColin Finck OUT PLUID LogonId,
195*c2c66affSColin Finck OUT PHANDLE Token,
196*c2c66affSColin Finck OUT PQUOTA_LIMITS Quotas,
197*c2c66affSColin Finck OUT PNTSTATUS SubStatus)
198*c2c66affSColin Finck {
199*c2c66affSColin Finck LSA_API_MSG ApiMessage;
200*c2c66affSColin Finck NTSTATUS Status;
201*c2c66affSColin Finck
202*c2c66affSColin Finck ApiMessage.ApiNumber = LSASS_REQUEST_LOGON_USER;
203*c2c66affSColin Finck ApiMessage.h.u1.s1.DataLength = LSA_PORT_DATA_SIZE(ApiMessage.LogonUser);
204*c2c66affSColin Finck ApiMessage.h.u1.s1.TotalLength = LSA_PORT_MESSAGE_SIZE;
205*c2c66affSColin Finck ApiMessage.h.u2.ZeroInit = 0;
206*c2c66affSColin Finck
207*c2c66affSColin Finck ApiMessage.LogonUser.Request.OriginName = *OriginName;
208*c2c66affSColin Finck ApiMessage.LogonUser.Request.LogonType = LogonType;
209*c2c66affSColin Finck ApiMessage.LogonUser.Request.AuthenticationPackage = AuthenticationPackage;
210*c2c66affSColin Finck ApiMessage.LogonUser.Request.AuthenticationInformation = AuthenticationInformation;
211*c2c66affSColin Finck ApiMessage.LogonUser.Request.AuthenticationInformationLength = AuthenticationInformationLength;
212*c2c66affSColin Finck ApiMessage.LogonUser.Request.LocalGroups = LocalGroups;
213*c2c66affSColin Finck if (LocalGroups != NULL)
214*c2c66affSColin Finck ApiMessage.LogonUser.Request.LocalGroupsCount = LocalGroups->GroupCount;
215*c2c66affSColin Finck else
216*c2c66affSColin Finck ApiMessage.LogonUser.Request.LocalGroupsCount = 0;
217*c2c66affSColin Finck ApiMessage.LogonUser.Request.SourceContext = *SourceContext;
218*c2c66affSColin Finck
219*c2c66affSColin Finck Status = ZwRequestWaitReplyPort(LsaHandle,
220*c2c66affSColin Finck (PPORT_MESSAGE)&ApiMessage,
221*c2c66affSColin Finck (PPORT_MESSAGE)&ApiMessage);
222*c2c66affSColin Finck if (!NT_SUCCESS(Status))
223*c2c66affSColin Finck {
224*c2c66affSColin Finck return Status;
225*c2c66affSColin Finck }
226*c2c66affSColin Finck
227*c2c66affSColin Finck *SubStatus = ApiMessage.LogonUser.Reply.SubStatus;
228*c2c66affSColin Finck
229*c2c66affSColin Finck if (!NT_SUCCESS(ApiMessage.Status))
230*c2c66affSColin Finck {
231*c2c66affSColin Finck return ApiMessage.Status;
232*c2c66affSColin Finck }
233*c2c66affSColin Finck
234*c2c66affSColin Finck *ProfileBuffer = ApiMessage.LogonUser.Reply.ProfileBuffer;
235*c2c66affSColin Finck *ProfileBufferLength = ApiMessage.LogonUser.Reply.ProfileBufferLength;
236*c2c66affSColin Finck *LogonId = ApiMessage.LogonUser.Reply.LogonId;
237*c2c66affSColin Finck *Token = ApiMessage.LogonUser.Reply.Token;
238*c2c66affSColin Finck *Quotas = ApiMessage.LogonUser.Reply.Quotas;
239*c2c66affSColin Finck
240*c2c66affSColin Finck return Status;
241*c2c66affSColin Finck }
242*c2c66affSColin Finck
243*c2c66affSColin Finck
244*c2c66affSColin Finck /*
245*c2c66affSColin Finck * @implemented
246*c2c66affSColin Finck */
247*c2c66affSColin Finck NTSTATUS
248*c2c66affSColin Finck NTAPI
LsaRegisterLogonProcess(IN PLSA_STRING LogonProcessName,OUT PHANDLE LsaHandle,OUT PLSA_OPERATIONAL_MODE OperationalMode)249*c2c66affSColin Finck LsaRegisterLogonProcess(IN PLSA_STRING LogonProcessName,
250*c2c66affSColin Finck OUT PHANDLE LsaHandle,
251*c2c66affSColin Finck OUT PLSA_OPERATIONAL_MODE OperationalMode)
252*c2c66affSColin Finck {
253*c2c66affSColin Finck SECURITY_QUALITY_OF_SERVICE SecurityQos;
254*c2c66affSColin Finck LSA_CONNECTION_INFO ConnectInfo;
255*c2c66affSColin Finck ULONG ConnectInfoLength = sizeof(ConnectInfo);
256*c2c66affSColin Finck UNICODE_STRING PortName;
257*c2c66affSColin Finck OBJECT_ATTRIBUTES ObjectAttributes;
258*c2c66affSColin Finck UNICODE_STRING EventName;
259*c2c66affSColin Finck HANDLE EventHandle;
260*c2c66affSColin Finck NTSTATUS Status;
261*c2c66affSColin Finck
262*c2c66affSColin Finck DPRINT("LsaRegisterLogonProcess()\n");
263*c2c66affSColin Finck
264*c2c66affSColin Finck /* Check the logon process name length */
265*c2c66affSColin Finck if (LogonProcessName->Length > LSASS_MAX_LOGON_PROCESS_NAME_LENGTH)
266*c2c66affSColin Finck return STATUS_NAME_TOO_LONG;
267*c2c66affSColin Finck
268*c2c66affSColin Finck /* Wait for the LSA authentication thread */
269*c2c66affSColin Finck RtlInitUnicodeString(&EventName,
270*c2c66affSColin Finck L"\\SECURITY\\LSA_AUTHENTICATION_INITIALIZED");
271*c2c66affSColin Finck InitializeObjectAttributes(&ObjectAttributes,
272*c2c66affSColin Finck &EventName,
273*c2c66affSColin Finck OBJ_CASE_INSENSITIVE | OBJ_PERMANENT,
274*c2c66affSColin Finck NULL,
275*c2c66affSColin Finck NULL);
276*c2c66affSColin Finck Status = ZwOpenEvent(&EventHandle,
277*c2c66affSColin Finck SYNCHRONIZE,
278*c2c66affSColin Finck &ObjectAttributes);
279*c2c66affSColin Finck if (!NT_SUCCESS(Status))
280*c2c66affSColin Finck {
281*c2c66affSColin Finck DPRINT("ZwOpenEvent failed (Status 0x%08lx)\n", Status);
282*c2c66affSColin Finck
283*c2c66affSColin Finck Status = ZwCreateEvent(&EventHandle,
284*c2c66affSColin Finck SYNCHRONIZE,
285*c2c66affSColin Finck &ObjectAttributes,
286*c2c66affSColin Finck NotificationEvent,
287*c2c66affSColin Finck FALSE);
288*c2c66affSColin Finck if (!NT_SUCCESS(Status))
289*c2c66affSColin Finck {
290*c2c66affSColin Finck DPRINT1("ZwCreateEvent failed (Status 0x%08lx)\n", Status);
291*c2c66affSColin Finck return Status;
292*c2c66affSColin Finck }
293*c2c66affSColin Finck }
294*c2c66affSColin Finck
295*c2c66affSColin Finck Status = ZwWaitForSingleObject(EventHandle,
296*c2c66affSColin Finck TRUE,
297*c2c66affSColin Finck NULL);
298*c2c66affSColin Finck ZwClose(EventHandle);
299*c2c66affSColin Finck if (!NT_SUCCESS(Status))
300*c2c66affSColin Finck {
301*c2c66affSColin Finck DPRINT1("ZwWaitForSingleObject failed (Status 0x%08lx)\n", Status);
302*c2c66affSColin Finck return Status;
303*c2c66affSColin Finck }
304*c2c66affSColin Finck
305*c2c66affSColin Finck /* Establish the connection */
306*c2c66affSColin Finck RtlInitUnicodeString(&PortName,
307*c2c66affSColin Finck L"\\LsaAuthenticationPort");
308*c2c66affSColin Finck
309*c2c66affSColin Finck SecurityQos.Length = sizeof(SecurityQos);
310*c2c66affSColin Finck SecurityQos.ImpersonationLevel = SecurityIdentification;
311*c2c66affSColin Finck SecurityQos.ContextTrackingMode = SECURITY_DYNAMIC_TRACKING;
312*c2c66affSColin Finck SecurityQos.EffectiveOnly = TRUE;
313*c2c66affSColin Finck
314*c2c66affSColin Finck strncpy(ConnectInfo.LogonProcessNameBuffer,
315*c2c66affSColin Finck LogonProcessName->Buffer,
316*c2c66affSColin Finck LogonProcessName->Length);
317*c2c66affSColin Finck ConnectInfo.Length = LogonProcessName->Length;
318*c2c66affSColin Finck ConnectInfo.LogonProcessNameBuffer[ConnectInfo.Length] = ANSI_NULL;
319*c2c66affSColin Finck ConnectInfo.CreateContext = TRUE;
320*c2c66affSColin Finck
321*c2c66affSColin Finck Status = ZwConnectPort(LsaHandle,
322*c2c66affSColin Finck &PortName,
323*c2c66affSColin Finck &SecurityQos,
324*c2c66affSColin Finck NULL,
325*c2c66affSColin Finck NULL,
326*c2c66affSColin Finck NULL,
327*c2c66affSColin Finck &ConnectInfo,
328*c2c66affSColin Finck &ConnectInfoLength);
329*c2c66affSColin Finck if (!NT_SUCCESS(Status))
330*c2c66affSColin Finck {
331*c2c66affSColin Finck DPRINT1("ZwConnectPort failed (Status 0x%08lx)\n", Status);
332*c2c66affSColin Finck return Status;
333*c2c66affSColin Finck }
334*c2c66affSColin Finck
335*c2c66affSColin Finck DPRINT("ConnectInfo.OperationalMode: 0x%08lx\n", ConnectInfo.OperationalMode);
336*c2c66affSColin Finck *OperationalMode = ConnectInfo.OperationalMode;
337*c2c66affSColin Finck
338*c2c66affSColin Finck if (!NT_SUCCESS(ConnectInfo.Status))
339*c2c66affSColin Finck {
340*c2c66affSColin Finck DPRINT1("ConnectInfo.Status: 0x%08lx\n", ConnectInfo.Status);
341*c2c66affSColin Finck }
342*c2c66affSColin Finck
343*c2c66affSColin Finck return ConnectInfo.Status;
344*c2c66affSColin Finck }
345