initial (not yet complete) implementation of the checklist control for the permission...
[reactos.git] / reactos / lib / aclui / aclui.c
1 /*
2 * ReactOS Access Control List Editor
3 * Copyright (C) 2004 ReactOS Team
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2.1 of the License, or (at your option) any later version.
9 *
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 */
19 /* $Id$
20 *
21 * PROJECT: ReactOS Access Control List Editor
22 * FILE: lib/aclui/aclui.c
23 * PURPOSE: Access Control List Editor
24 * PROGRAMMER: Thomas Weidenmueller <w3seek@reactos.com>
25 *
26 * UPDATE HISTORY:
27 * 08/10/2004 Created
28 */
29 #include "acluilib.h"
30
31 HINSTANCE hDllInstance;
32
33 static VOID
34 DestroySecurityPage(IN PSECURITY_PAGE sp)
35 {
36 if(sp->hiUsrs != NULL)
37 {
38 ImageList_Destroy(sp->hiUsrs);
39 }
40
41 HeapFree(GetProcessHeap(),
42 0,
43 sp);
44 }
45
46 static VOID
47 FreeAceList(IN PACE_LISTITEM *AceListHead)
48 {
49 PACE_LISTITEM CurItem, NextItem;
50
51 CurItem = *AceListHead;
52 while (CurItem != NULL)
53 {
54 /* free the SID string if present */
55 if (CurItem->DisplayString != NULL)
56 {
57 LocalFree((HLOCAL)CurItem->DisplayString);
58 }
59
60 /* free the ACE list item */
61 NextItem = CurItem->Next;
62 HeapFree(GetProcessHeap(),
63 0,
64 CurItem);
65 CurItem = NextItem;
66 }
67
68 *AceListHead = NULL;
69 }
70
71 static PACE_LISTITEM
72 FindSidInAceList(IN PACE_LISTITEM AceListHead,
73 IN PSID Sid)
74 {
75 PACE_LISTITEM CurItem;
76
77 for (CurItem = AceListHead;
78 CurItem != NULL;
79 CurItem = CurItem->Next)
80 {
81 if (EqualSid((PSID)(CurItem + 1),
82 Sid))
83 {
84 return CurItem;
85 }
86 }
87
88 return NULL;
89 }
90
91 static VOID
92 ReloadUsersGroupsList(IN PSECURITY_PAGE sp)
93 {
94 PSECURITY_DESCRIPTOR SecurityDescriptor;
95 BOOL DaclPresent, DaclDefaulted;
96 PACL Dacl = NULL;
97 HRESULT hRet;
98
99 /* delete the cached ACL */
100 FreeAceList(&sp->AceListHead);
101
102 /* query the ACL */
103 hRet = sp->psi->lpVtbl->GetSecurity(sp->psi,
104 DACL_SECURITY_INFORMATION,
105 &SecurityDescriptor,
106 FALSE);
107 if (SUCCEEDED(hRet) && SecurityDescriptor != NULL)
108 {
109 if (GetSecurityDescriptorDacl(SecurityDescriptor,
110 &DaclPresent,
111 &Dacl,
112 &DaclDefaulted))
113 {
114 PACE_LISTITEM AceListItem, *NextAcePtr;
115 PSID Sid;
116 PVOID Ace;
117 ULONG AceIndex;
118 DWORD AccountNameSize, DomainNameSize, SidLength;
119 SID_NAME_USE SidNameUse;
120 DWORD LookupResult;
121
122 NextAcePtr = &sp->AceListHead;
123
124 for (AceIndex = 0;
125 AceIndex < Dacl->AceCount;
126 AceIndex++)
127 {
128 GetAce(Dacl,
129 AceIndex,
130 &Ace);
131
132 Sid = (PSID)&((PACCESS_ALLOWED_ACE)Ace)->SidStart;
133
134 if (!FindSidInAceList(sp->AceListHead,
135 Sid))
136 {
137 SidLength = GetLengthSid(Sid);
138
139 AccountNameSize = 0;
140 DomainNameSize = 0;
141
142 /* calculate the size of the buffer we need to calculate */
143 LookupAccountSid(NULL, /* FIXME */
144 Sid,
145 NULL,
146 &AccountNameSize,
147 NULL,
148 &DomainNameSize,
149 &SidNameUse);
150
151 /* allocate the ace */
152 AceListItem = HeapAlloc(GetProcessHeap(),
153 0,
154 sizeof(ACE_LISTITEM) +
155 SidLength +
156 ((AccountNameSize + DomainNameSize) * sizeof(WCHAR)));
157 if (AceListItem != NULL)
158 {
159 AceListItem->AccountName = (LPWSTR)((ULONG_PTR)(AceListItem + 1) + SidLength);
160 AceListItem->DomainName = AceListItem->AccountName + AccountNameSize;
161
162 CopySid(SidLength,
163 (PSID)(AceListItem + 1),
164 Sid);
165
166 LookupResult = ERROR_SUCCESS;
167 if (!LookupAccountSid(NULL, /* FIXME */
168 Sid,
169 AceListItem->AccountName,
170 &AccountNameSize,
171 AceListItem->DomainName,
172 &DomainNameSize,
173 &SidNameUse))
174 {
175 LookupResult = GetLastError();
176 if (LookupResult != ERROR_NONE_MAPPED)
177 {
178 HeapFree(GetProcessHeap(),
179 0,
180 AceListItem);
181 continue;
182 }
183 }
184
185 if (AccountNameSize == 0)
186 {
187 AceListItem->AccountName = NULL;
188 }
189 if (DomainNameSize == 0)
190 {
191 AceListItem->DomainName = NULL;
192 }
193
194 AceListItem->Next = NULL;
195 if (LookupResult == ERROR_NONE_MAPPED)
196 {
197 if (!ConvertSidToStringSid(Sid,
198 &AceListItem->DisplayString))
199 {
200 AceListItem->DisplayString = NULL;
201 }
202 }
203 else
204 {
205 LSA_HANDLE LsaHandle;
206 NTSTATUS Status;
207
208 AceListItem->DisplayString = NULL;
209
210 /* read the domain of the SID */
211 if (OpenLSAPolicyHandle(NULL, /* FIXME */
212 POLICY_LOOKUP_NAMES | POLICY_VIEW_LOCAL_INFORMATION,
213 &LsaHandle))
214 {
215 PLSA_REFERENCED_DOMAIN_LIST ReferencedDomain;
216 PLSA_TRANSLATED_NAME Names;
217 PLSA_TRUST_INFORMATION Domain;
218 PLSA_UNICODE_STRING DomainName;
219 PPOLICY_ACCOUNT_DOMAIN_INFO PolicyAccountDomainInfo = NULL;
220
221 Status = LsaLookupSids(LsaHandle,
222 1,
223 &Sid,
224 &ReferencedDomain,
225 &Names);
226 if (NT_SUCCESS(Status))
227 {
228 if (ReferencedDomain != NULL &&
229 Names->DomainIndex >= 0)
230 {
231 Domain = &ReferencedDomain->Domains[Names->DomainIndex];
232 DomainName = &Domain->Name;
233 }
234 else
235 {
236 Domain = NULL;
237 DomainName = NULL;
238 }
239
240 AceListItem->SidNameUse = Names->Use;
241
242 switch (Names->Use)
243 {
244 case SidTypeAlias:
245 if (Domain != NULL)
246 {
247 /* query the domain name for BUILTIN accounts */
248 Status = LsaQueryInformationPolicy(LsaHandle,
249 PolicyAccountDomainInformation,
250 (PVOID*)&PolicyAccountDomainInfo);
251 if (NT_SUCCESS(Status))
252 {
253 DomainName = &PolicyAccountDomainInfo->DomainName;
254
255 /* make the user believe this is a group */
256 AceListItem->SidNameUse = SidTypeGroup;
257 }
258 }
259 /* fall through */
260
261 case SidTypeUser:
262 {
263 if (Domain != NULL)
264 {
265 AceListItem->DisplayString = (LPWSTR)LocalAlloc(LMEM_FIXED,
266 (AccountNameSize * sizeof(WCHAR)) +
267 (DomainName->Length + sizeof(WCHAR)) +
268 (Names->Name.Length + sizeof(WCHAR)) +
269 (4 * sizeof(WCHAR)));
270 if (AceListItem->DisplayString != NULL)
271 {
272 WCHAR *s;
273
274 /* NOTE: LSA_UNICODE_STRINGs are not always NULL-terminated! */
275
276 wcscpy(AceListItem->DisplayString,
277 AceListItem->AccountName);
278 wcscat(AceListItem->DisplayString,
279 L" (");
280 s = AceListItem->DisplayString + wcslen(AceListItem->DisplayString);
281 CopyMemory(s,
282 DomainName->Buffer,
283 DomainName->Length);
284 s += DomainName->Length / sizeof(WCHAR);
285 *(s++) = L'\\';
286 CopyMemory(s,
287 Names->Name.Buffer,
288 Names->Name.Length);
289 s += Names->Name.Length / sizeof(WCHAR);
290 *(s++) = L')';
291 *s = L'\0';
292 }
293
294 /* mark the ace as a user unless it's a
295 BUILTIN account */
296 if (PolicyAccountDomainInfo == NULL)
297 {
298 AceListItem->SidNameUse = SidTypeUser;
299 }
300 }
301 break;
302 }
303
304 case SidTypeWellKnownGroup:
305 {
306 /* make the user believe this is a group */
307 AceListItem->SidNameUse = SidTypeGroup;
308 break;
309 }
310
311 default:
312 {
313 break;
314 }
315 }
316
317 if (PolicyAccountDomainInfo != NULL)
318 {
319 LsaFreeMemory(PolicyAccountDomainInfo);
320 }
321
322 LsaFreeMemory(ReferencedDomain);
323 LsaFreeMemory(Names);
324 }
325 LsaClose(LsaHandle);
326 }
327 }
328
329 /* append item to the cached ACL */
330 *NextAcePtr = AceListItem;
331 NextAcePtr = &AceListItem->Next;
332 }
333 }
334 }
335 }
336 LocalFree((HLOCAL)SecurityDescriptor);
337 }
338 }
339
340 static INT
341 AddAceListEntry(IN PSECURITY_PAGE sp,
342 IN PACE_LISTITEM AceListItem,
343 IN INT Index,
344 IN BOOL Selected)
345 {
346 LVITEM li;
347
348 li.mask = LVIF_IMAGE | LVIF_PARAM | LVIF_STATE | LVIF_TEXT;
349 li.iItem = Index;
350 li.iSubItem = 0;
351 li.state = (Selected ? LVIS_SELECTED : 0);
352 li.stateMask = LVIS_SELECTED;
353 li.pszText = (AceListItem->DisplayString != NULL ? AceListItem->DisplayString : AceListItem->AccountName);
354 switch (AceListItem->SidNameUse)
355 {
356 case SidTypeUser:
357 li.iImage = 0;
358 break;
359 case SidTypeGroup:
360 li.iImage = 1;
361 break;
362 default:
363 li.iImage = -1;
364 break;
365 }
366 li.lParam = (LPARAM)AceListItem;
367
368 return ListView_InsertItem(sp->hWndAceList,
369 &li);
370 }
371
372 static VOID
373 FillUsersGroupsList(IN PSECURITY_PAGE sp)
374 {
375 LPARAM SelLParam;
376 PACE_LISTITEM CurItem;
377 RECT rcLvClient;
378
379 SelLParam = ListViewGetSelectedItemData(sp->hWndAceList);
380
381 DisableRedrawWindow(sp->hWndAceList);
382
383 ListView_DeleteAllItems(sp->hWndAceList);
384
385 ReloadUsersGroupsList(sp);
386
387 for (CurItem = sp->AceListHead;
388 CurItem != NULL;
389 CurItem = CurItem->Next)
390 {
391 AddAceListEntry(sp,
392 CurItem,
393 -1,
394 (SelLParam == (LPARAM)CurItem));
395 }
396
397 EnableRedrawWindow(sp->hWndAceList);
398
399 GetClientRect(sp->hWndAceList, &rcLvClient);
400
401 ListView_SetColumnWidth(sp->hWndAceList,
402 0,
403 rcLvClient.right);
404 }
405
406 static VOID
407 UpdateControlStates(IN PSECURITY_PAGE sp)
408 {
409 BOOL UserOrGroupSelected;
410
411 UserOrGroupSelected = (ListViewGetSelectedItemData(sp->hWndAceList) != 0);
412
413 EnableWindow(sp->hBtnRemove, UserOrGroupSelected);
414 }
415
416 static UINT CALLBACK
417 SecurityPageCallback(IN HWND hwnd,
418 IN UINT uMsg,
419 IN LPPROPSHEETPAGE ppsp)
420 {
421 PSECURITY_PAGE sp = (PSECURITY_PAGE)ppsp->lParam;
422
423 switch(uMsg)
424 {
425 case PSPCB_CREATE:
426 {
427 return TRUE;
428 }
429 case PSPCB_RELEASE:
430 {
431 DestroySecurityPage(sp);
432 UnregisterCheckListControl();
433 return FALSE;
434 }
435 }
436
437 return FALSE;
438 }
439
440 static VOID
441 SetAceCheckListColumns(IN HWND hAceCheckList,
442 IN UINT Button,
443 IN HWND hLabel)
444 {
445 POINT pt;
446 RECT rcLabel;
447
448 GetWindowRect(hLabel,
449 &rcLabel);
450 pt.y = 0;
451 pt.x = (rcLabel.right - rcLabel.left) / 2;
452 MapWindowPoints(hLabel,
453 hAceCheckList,
454 &pt,
455 1);
456
457 SendMessage(hAceCheckList,
458 CLM_SETCHECKBOXCOLUMN,
459 Button,
460 pt.x);
461 }
462
463
464 static INT_PTR CALLBACK
465 SecurityPageProc(IN HWND hwndDlg,
466 IN UINT uMsg,
467 IN WPARAM wParam,
468 IN LPARAM lParam)
469 {
470 PSECURITY_PAGE sp;
471
472 switch(uMsg)
473 {
474 case WM_NOTIFY:
475 {
476 NMHDR *pnmh = (NMHDR*)lParam;
477 if (pnmh->idFrom == IDC_ACELIST)
478 {
479 sp = (PSECURITY_PAGE)GetWindowLongPtr(hwndDlg,
480 DWL_USER);
481 if (sp != NULL)
482 {
483 switch(pnmh->code)
484 {
485 case LVN_ITEMCHANGED:
486 {
487 UpdateControlStates(sp);
488 break;
489 }
490 }
491 }
492 }
493 break;
494 }
495
496 case WM_INITDIALOG:
497 {
498 sp = (PSECURITY_PAGE)((LPPROPSHEETPAGE)lParam)->lParam;
499 if(sp != NULL)
500 {
501 LV_COLUMN lvc;
502 RECT rcLvClient;
503
504 sp->hWnd = hwndDlg;
505 sp->hWndAceList = GetDlgItem(hwndDlg, IDC_ACELIST);
506 sp->hBtnRemove = GetDlgItem(hwndDlg, IDC_ACELIST_REMOVE);
507 sp->hAceCheckList = GetDlgItem(hwndDlg, IDC_ACE_CHECKLIST);
508
509 /* save the pointer to the structure */
510 SetWindowLongPtr(hwndDlg,
511 DWL_USER,
512 (DWORD_PTR)sp);
513
514 sp->hiUsrs = ImageList_LoadBitmap(hDllInstance,
515 MAKEINTRESOURCE(IDB_USRGRPIMAGES),
516 16,
517 3,
518 0);
519
520 /* setup the listview control */
521 ListView_SetExtendedListViewStyleEx(sp->hWndAceList,
522 LVS_EX_FULLROWSELECT,
523 LVS_EX_FULLROWSELECT);
524 ListView_SetImageList(sp->hWndAceList,
525 sp->hiUsrs,
526 LVSIL_SMALL);
527
528 GetClientRect(sp->hWndAceList, &rcLvClient);
529
530 /* add a column to the list view */
531 lvc.mask = LVCF_FMT | LVCF_WIDTH;
532 lvc.fmt = LVCFMT_LEFT;
533 lvc.cx = rcLvClient.right;
534 ListView_InsertColumn(sp->hWndAceList, 0, &lvc);
535
536 FillUsersGroupsList(sp);
537
538 ListViewSelectItem(sp->hWndAceList,
539 0);
540
541 /* calculate the columns of the allow/deny checkboxes */
542 SetAceCheckListColumns(sp->hAceCheckList,
543 CLB_ALLOW,
544 GetDlgItem(hwndDlg, IDC_LABEL_ALLOW));
545 SetAceCheckListColumns(sp->hAceCheckList,
546 CLB_DENY,
547 GetDlgItem(hwndDlg, IDC_LABEL_DENY));
548
549 /* FIXME - hide controls in case the flags aren't present */
550 }
551 break;
552 }
553 }
554 return 0;
555 }
556
557
558 /*
559 * CreateSecurityPage EXPORTED
560 *
561 * @implemented
562 */
563 HPROPSHEETPAGE
564 WINAPI
565 CreateSecurityPage(IN LPSECURITYINFO psi)
566 {
567 PROPSHEETPAGE psp;
568 PSECURITY_PAGE sPage;
569 SI_OBJECT_INFO ObjectInfo;
570 HRESULT hRet;
571
572 if(psi == NULL)
573 {
574 SetLastError(ERROR_INVALID_PARAMETER);
575
576 DPRINT("No ISecurityInformation class passed!\n");
577 return NULL;
578 }
579
580 /* get the object information from the server interface */
581 hRet = psi->lpVtbl->GetObjectInformation(psi, &ObjectInfo);
582
583 if(FAILED(hRet))
584 {
585 SetLastError(hRet);
586
587 DPRINT("CreateSecurityPage() failed! Failed to query the object information!\n");
588 return NULL;
589 }
590
591 if (!RegisterCheckListControl(hDllInstance))
592 {
593 DPRINT("Registering the CHECKLIST_ACLUI class failed!\n");
594 return NULL;
595 }
596
597 sPage = HeapAlloc(GetProcessHeap(),
598 HEAP_ZERO_MEMORY,
599 sizeof(SECURITY_PAGE));
600 if (sPage == NULL)
601 {
602 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
603
604 DPRINT("Not enough memory to allocate a SECURITY_PAGE!\n");
605 return NULL;
606 }
607 sPage->psi = psi;
608 sPage->ObjectInfo = ObjectInfo;
609
610 psp.dwSize = sizeof(PROPSHEETPAGE);
611 psp.dwFlags = PSP_USECALLBACK;
612 psp.hInstance = hDllInstance;
613 psp.pszTemplate = MAKEINTRESOURCE(IDD_SECPAGE);
614 psp.pfnDlgProc = SecurityPageProc;
615 psp.lParam = (LPARAM)sPage;
616 psp.pfnCallback = SecurityPageCallback;
617
618 if((ObjectInfo.dwFlags & SI_PAGE_TITLE) &&
619 ObjectInfo.pszPageTitle != NULL && ObjectInfo.pszPageTitle[0] != L'\0')
620 {
621 /* Set the page title if the flag is present and the string isn't empty */
622 psp.pszTitle = ObjectInfo.pszPageTitle;
623 psp.dwFlags |= PSP_USETITLE;
624 }
625
626 /* NOTE: the SECURITY_PAGE structure will be freed by the property page
627 callback! */
628
629 return CreatePropertySheetPage(&psp);
630 }
631
632
633 /*
634 * EditSecurity EXPORTED
635 *
636 * @implemented
637 */
638 BOOL
639 WINAPI
640 EditSecurity(IN HWND hwndOwner,
641 IN LPSECURITYINFO psi)
642 {
643 HRESULT hRet;
644 SI_OBJECT_INFO ObjectInfo;
645 PROPSHEETHEADER psh;
646 HPROPSHEETPAGE hPages[1];
647 LPWSTR lpCaption;
648 BOOL Ret;
649
650 if(psi == NULL)
651 {
652 SetLastError(ERROR_INVALID_PARAMETER);
653
654 DPRINT("No ISecurityInformation class passed!\n");
655 return FALSE;
656 }
657
658 /* get the object information from the server interface */
659 hRet = psi->lpVtbl->GetObjectInformation(psi, &ObjectInfo);
660
661 if(FAILED(hRet))
662 {
663 SetLastError(hRet);
664
665 DPRINT("GetObjectInformation() failed!\n");
666 return FALSE;
667 }
668
669 /* create the page */
670 hPages[0] = CreateSecurityPage(psi);
671 if(hPages[0] == NULL)
672 {
673 DPRINT("CreateSecurityPage(), couldn't create property sheet!\n");
674 return FALSE;
675 }
676
677 psh.dwSize = sizeof(PROPSHEETHEADER);
678 psh.dwFlags = PSH_DEFAULT;
679 psh.hwndParent = hwndOwner;
680 psh.hInstance = hDllInstance;
681 if((ObjectInfo.dwFlags & SI_PAGE_TITLE) &&
682 ObjectInfo.pszPageTitle != NULL && ObjectInfo.pszPageTitle[0] != L'\0')
683 {
684 /* Set the page title if the flag is present and the string isn't empty */
685 psh.pszCaption = ObjectInfo.pszPageTitle;
686 lpCaption = NULL;
687 }
688 else
689 {
690 /* Set the page title to the object name, make sure the format string
691 has "%1" NOT "%s" because it uses FormatMessage() to automatically
692 allocate the right amount of memory. */
693 LoadAndFormatString(hDllInstance,
694 IDS_PSP_TITLE,
695 &lpCaption,
696 ObjectInfo.pszObjectName);
697 psh.pszCaption = lpCaption;
698 }
699
700 psh.nPages = sizeof(hPages) / sizeof(HPROPSHEETPAGE);
701 psh.nStartPage = 0;
702 psh.phpage = hPages;
703
704 Ret = (PropertySheet(&psh) != -1);
705
706 if(lpCaption != NULL)
707 {
708 LocalFree((HLOCAL)lpCaption);
709 }
710
711 return Ret;
712 }
713
714 BOOL STDCALL
715 DllMain(IN HINSTANCE hinstDLL,
716 IN DWORD dwReason,
717 IN LPVOID lpvReserved)
718 {
719 switch (dwReason)
720 {
721 case DLL_PROCESS_ATTACH:
722 hDllInstance = hinstDLL;
723 break;
724 case DLL_THREAD_ATTACH:
725 case DLL_THREAD_DETACH:
726 case DLL_PROCESS_DETACH:
727 break;
728 }
729 return TRUE;
730 }
731