1 // Copyright (c) 1999-2018 David Muse
2 // See the COPYING file for more information
3
4 #include <rudiments/permissions.h>
5 #include <rudiments/charstring.h>
6 #include <rudiments/error.h>
7 #include <rudiments/file.h>
8 #include <rudiments/userentry.h>
9 #include <rudiments/groupentry.h>
10 #include <rudiments/stdio.h>
11 #include <rudiments/process.h>
12 #ifdef RUDIMENTS_HAVE_SETENTRIESINACL
13 #include <rudiments/bytestring.h>
14 #endif
15
16 #ifdef RUDIMENTS_HAVE_STDLIB_H
17 #include <stdlib.h>
18 #endif
19 #ifdef RUDIMENTS_HAVE_WINDOWS_H
20 #include <windows.h>
21 #endif
22 #ifdef RUDIMENTS_HAVE_SYS_STAT_H
23 #include <sys/stat.h>
24 #endif
25 #ifdef RUDIMENTS_HAVE_IO_H
26 #include <io.h>
27 #endif
28 #ifdef RUDIMENTS_HAVE_ACLAPI_H
29 #include <aclapi.h>
30 #endif
31
32
33 // Some platforms don't define some of these because they don't support
34 // them. However, every platform I've tried so far interprets the permissions
35 // bitmap the same way, and ignores unsupported bits. Lets hope that's true
36 // in general.
37 #ifndef S_ISUID
38 #define S_ISUID 04000
39 #endif
40 #ifndef S_ISGID
41 #define S_ISGID 02000
42 #endif
43 #ifndef S_ISVTX
44 #define S_ISVTX 01000
45 #endif
46 #ifndef S_IRUSR
47 #define S_IRUSR 0400
48 #endif
49 #ifndef S_IWUSR
50 #define S_IWUSR 0200
51 #endif
52 #ifndef S_IXUSR
53 #define S_IXUSR 0100
54 #endif
55 #ifndef S_IRGRP
56 #define S_IRGRP (S_IRUSR>>3)
57 #endif
58 #ifndef S_IWGRP
59 #define S_IWGRP (S_IWUSR>>3)
60 #endif
61 #ifndef S_IXGRP
62 #define S_IXGRP (S_IXUSR>>3)
63 #endif
64 #ifndef S_IROTH
65 #define S_IROTH (S_IRGRP>>3)
66 #endif
67 #ifndef S_IWOTH
68 #define S_IWOTH (S_IWGRP>>3)
69 #endif
70 #ifndef S_IXOTH
71 #define S_IXOTH (S_IXGRP>>3)
72 #endif
73
74 #ifdef RUDIMENTS_HAVE_GETACE
75
76 // Object-specific rights
77 // see http://msdn.microsoft.com/en-us/magazine/cc982153.aspx
78 #define _CC 0x00000001 // "read"
79 #define _DC 0x00000002 // "write"
80 #define _LC 0x00000004 // "append"
81 #define _SW 0x00000008 // "read extended attributes"
82 #define _RP 0x00000010 // "write extended attributes"
83 #define _WP 0x00000020 // "execute"
84 #define _DT 0x00000040 // "delete child"
85 #define _LO 0x00000080 // "read standard attributes"
86 #define _CR 0x00000100 // "write standard attributes"
87
88 // normalized rights masks
89 #define _ALL GENERIC_ALL
90 #define _READ (READ_CONTROL|GENERIC_READ|_CC|_SW|_LO)
91 #define _WRITE (DELETE|WRITE_DAC|WRITE_OWNER| \
92 GENERIC_WRITE|_DC|_LC|_RP|_DT|_CR| \
93 SYNCHRONIZE)
94 #define _EXEC (GENERIC_EXECUTE|_WP)
95
96 // We need _WRITEWITHOUTSYNCHRONIZE because sometimes (always?)
97 // the SYNCHRONIZE permission is granted implicitly. So, when testing
98 // to see if a file has write permissions, we want to ignore the
99 // SYNCHRONIZE permission to avoid false positives.
100 #define _WRITEWITHOUTSYNCHRONIZE (_WRITE&~SYNCHRONIZE)
101 #endif
102
103
setFilePermissions(const char * filename,mode_t perms)104 bool permissions::setFilePermissions(const char *filename, mode_t perms) {
105 #if defined(RUDIMENTS_HAVE_CHMOD)
106 return !chmod(filename,perms);
107 #elif defined(RUDIMENTS_HAVE_SETSECURITYINFO)
108
109 // is this a file or directory?
110 DWORD fileattr=GetFileAttributes(filename);
111 if (fileattr==INVALID_FILE_ATTRIBUTES) {
112 return false;
113 }
114
115 // determine the attrs
116 // FILE_FLAG_BACKUP_SEMANTICS must be used when opening a
117 // directory, for some reason
118 bool isdir=false;
119 DWORD attrs=FILE_ATTRIBUTE_NORMAL;
120 if (fileattr&FILE_ATTRIBUTE_DIRECTORY) {
121 isdir=true;
122 attrs=FILE_FLAG_BACKUP_SEMANTICS;
123 }
124
125 // convert the perms to a dacl
126 PACL dacl=(PACL)permOctalToDacl(perms,isdir);
127
128 // open the file/directory
129 HANDLE fh=CreateFile(filename,
130 GENERIC_WRITE|WRITE_DAC,
131 FILE_SHARE_DELETE|
132 FILE_SHARE_READ|
133 FILE_SHARE_WRITE,
134 NULL,OPEN_EXISTING,
135 attrs,NULL);
136 if (fh==INVALID_HANDLE_VALUE) {
137 return false;
138 }
139
140 // set the permissions
141 bool success=(SetSecurityInfo(fh,
142 SE_FILE_OBJECT,
143 DACL_SECURITY_INFORMATION,
144 NULL,NULL,dacl,NULL)==
145 ERROR_SUCCESS);
146
147 // close the file/directory
148 CloseHandle(fh);
149
150 // clean up
151 LocalFree(dacl);
152
153 return success;
154 #else
155 RUDIMENTS_SET_ENOSYS
156 return false;
157 #endif
158 }
159
setFilePermissions(int32_t fd,mode_t perms)160 bool permissions::setFilePermissions(int32_t fd, mode_t perms) {
161 #if defined(RUDIMENTS_HAVE_FCHMOD)
162 int32_t result;
163 error::clearError();
164 do {
165 result=fchmod(fd,perms);
166 } while (result==-1 && error::getErrorNumber()==EINTR);
167 return !result;
168 #elif defined(RUDIMENTS_HAVE_SETSECURITYINFO)
169
170 // get the file handle
171 HANDLE fh=(HANDLE)filedescriptor::
172 getHandleFromFileDescriptor(fd);
173
174 // is this a file or directory?
175 BY_HANDLE_FILE_INFORMATION bhfi;
176 if (!GetFileInformationByHandle(fh,&bhfi)) {
177 return false;
178 }
179
180 // determine the attrs
181 bool isdir=false;
182 if (bhfi.dwFileAttributes&FILE_ATTRIBUTE_DIRECTORY) {
183 isdir=true;
184 }
185
186 // convert the perms to a dacl
187 PACL dacl=(PACL)permOctalToDacl(perms,isdir);
188
189 // set the permissions
190 bool success=(SetSecurityInfo(fh,
191 SE_FILE_OBJECT,
192 DACL_SECURITY_INFORMATION,
193 NULL,NULL,dacl,NULL)==
194 ERROR_SUCCESS);
195
196 // clean up
197 LocalFree(dacl);
198
199 return success;
200 #else
201 RUDIMENTS_SET_ENOSYS
202 return false;
203 #endif
204 }
205
everyoneReadWrite()206 mode_t permissions::everyoneReadWrite() {
207 return S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH;
208 }
209
everyoneReadWriteExecute()210 mode_t permissions::everyoneReadWriteExecute() {
211 return S_IRUSR|S_IWUSR|S_IXUSR
212 |S_IRGRP|S_IWGRP|S_IXGRP
213 |S_IROTH|S_IWOTH|S_IXOTH;
214 }
215
ownerRead()216 mode_t permissions::ownerRead() {
217 return S_IRUSR;
218 }
219
ownerWrite()220 mode_t permissions::ownerWrite() {
221 return S_IWUSR;
222 }
223
ownerExecute()224 mode_t permissions::ownerExecute() {
225 return S_IXUSR;
226 }
227
ownerReadWrite()228 mode_t permissions::ownerReadWrite() {
229 return S_IRUSR|S_IWUSR;
230 }
231
ownerReadExecute()232 mode_t permissions::ownerReadExecute() {
233 return S_IRUSR|S_IXUSR;
234 }
235
ownerReadWriteExecute()236 mode_t permissions::ownerReadWriteExecute() {
237 return S_IRUSR|S_IWUSR|S_IXUSR;
238 }
239
groupRead()240 mode_t permissions::groupRead() {
241 return S_IRGRP;
242 }
243
groupWrite()244 mode_t permissions::groupWrite() {
245 return S_IWGRP;
246 }
247
groupExecute()248 mode_t permissions::groupExecute() {
249 return S_IXGRP;
250 }
251
groupReadWrite()252 mode_t permissions::groupReadWrite() {
253 return S_IRGRP|S_IWGRP;
254 }
255
groupReadExecute()256 mode_t permissions::groupReadExecute() {
257 return S_IRGRP|S_IXGRP;
258 }
259
groupReadWriteExecute()260 mode_t permissions::groupReadWriteExecute() {
261 return S_IRGRP|S_IWGRP|S_IXGRP;
262 }
263
othersRead()264 mode_t permissions::othersRead() {
265 return S_IROTH;
266 }
267
othersWrite()268 mode_t permissions::othersWrite() {
269 return S_IWOTH;
270 }
271
othersExecute()272 mode_t permissions::othersExecute() {
273 return S_IXOTH;
274 }
275
othersReadWrite()276 mode_t permissions::othersReadWrite() {
277 return S_IROTH|S_IWOTH;
278 }
279
othersReadExecute()280 mode_t permissions::othersReadExecute() {
281 return S_IROTH|S_IXOTH;
282 }
283
othersReadWriteExecute()284 mode_t permissions::othersReadWriteExecute() {
285 return S_IROTH|S_IWOTH|S_IXOTH;
286 }
287
saveInSwapSpace()288 mode_t permissions::saveInSwapSpace() {
289 return S_ISVTX;
290 }
291
setUserId()292 mode_t permissions::setUserId() {
293 return S_ISUID;
294 }
295
setGroupId()296 mode_t permissions::setGroupId() {
297 return S_ISGID;
298 }
299
evalPermString(const char * permstring)300 mode_t permissions::evalPermString(const char *permstring) {
301 mode_t retval=0;
302 if (charstring::length(permstring)==9) {
303
304 // handle user permissions
305 if (permstring[0]=='r') {
306 retval=retval|ownerRead();
307 }
308 if (permstring[1]=='w') {
309 retval=retval|ownerWrite();
310 }
311 if (permstring[2]=='x') {
312 retval=retval|ownerExecute();
313 } else if (permstring[2]=='X' || permstring[2]=='S') {
314 retval=retval|ownerExecute();
315 retval=retval|setUserId();
316 }
317
318 // handle group permissions
319 if (permstring[3]=='r') {
320 retval=retval|groupRead();
321 }
322 if (permstring[4]=='w') {
323 retval=retval|groupWrite();
324 }
325 if (permstring[5]=='x') {
326 retval=retval|groupExecute();
327 } else if (permstring[5]=='X' || permstring[5]=='S') {
328 retval=retval|groupExecute();
329 }
330
331 // handle others permissions
332 if (permstring[6]=='r') {
333 retval=retval|othersRead();
334 }
335 if (permstring[7]=='w') {
336 retval=retval|othersWrite();
337 }
338 if (permstring[8]=='x') {
339 retval=retval|othersExecute();
340
341 // handle sticky bit
342 } else if (permstring[5]=='t') {
343 retval=retval|saveInSwapSpace();
344 }
345 }
346 return retval;
347 }
348
evalPermOctal(mode_t permoctal)349 char *permissions::evalPermOctal(mode_t permoctal) {
350 char *permstring=new char[10];
351 permstring[9]='\0';
352
353 mode_t shift=permoctal;
354 for (int16_t i=8; i>=0; i--) {
355 uint8_t pos=i%3;
356 permstring[i]=(shift&1)?((pos==2)?'x':(pos==1)?'w':'r'):'-';
357 shift=shift>>1;
358 }
359 return permstring;
360 }
361
permStringToDacl(const char * permstring,bool directory)362 void *permissions::permStringToDacl(const char *permstring, bool directory) {
363 return permOctalToDacl(evalPermString(permstring),directory);
364 }
365
permOctalToDacl(mode_t permoctal,bool directory)366 void *permissions::permOctalToDacl(mode_t permoctal, bool directory) {
367
368 #ifdef RUDIMENTS_HAVE_SETENTRIESINACL
369
370 // FIXME: how?
371 // P - protected: ignore perms from higher
372 // up the inheritance tree
373 // AI - children inherit permissions (for directories only)
374 /*charstring::copy(sddl,"D:P");
375 if (directory) {
376 charstring::append(sddl,"AI");
377 }*/
378
379 // define inheritance...
380 DWORD inheritance=NO_INHERITANCE;
381 if (directory) {
382 inheritance=OBJECT_INHERIT_ACE|CONTAINER_INHERIT_ACE;
383 }
384
385 // create acl entries for world, group and owner
386 EXPLICIT_ACCESS ea[3];
387 bytestring::zero(&ea,sizeof(ea));
388
389 // world
390 ea[0].grfAccessMode=SET_ACCESS;
391 ea[0].grfInheritance=inheritance;
392 ea[0].Trustee.MultipleTrusteeOperation=NO_MULTIPLE_TRUSTEE;
393 ea[0].Trustee.TrusteeForm=TRUSTEE_IS_SID;
394 ea[0].Trustee.TrusteeType=TRUSTEE_IS_WELL_KNOWN_GROUP;
395 SID_IDENTIFIER_AUTHORITY worldsia=
396 SECURITY_WORLD_SID_AUTHORITY;
397 PSID worldsid;
398 AllocateAndInitializeSid(&worldsia,1,SECURITY_WORLD_RID,
399 0,0,0,0,0,0,0,&worldsid);
400 ea[0].Trustee.ptstrName=(LPSTR)worldsid;
401
402 // group
403 ea[1].grfAccessMode=SET_ACCESS;
404 ea[1].grfInheritance=inheritance;
405 ea[1].Trustee.MultipleTrusteeOperation=NO_MULTIPLE_TRUSTEE;
406 ea[1].Trustee.TrusteeForm=TRUSTEE_IS_SID;
407 ea[1].Trustee.TrusteeType=TRUSTEE_IS_GROUP;
408 groupentry grpent;
409 grpent.initialize(process::getGroupId());
410 PSID groupsid=(PSID)bytestring::duplicate(
411 grpent.getSid(),
412 grpent.getSidSize());
413 ea[1].Trustee.ptstrName=(LPSTR)groupsid;
414
415 // owner
416 ea[2].grfAccessMode=SET_ACCESS;
417 ea[2].grfInheritance=inheritance;
418 ea[2].Trustee.MultipleTrusteeOperation=NO_MULTIPLE_TRUSTEE;
419 ea[2].Trustee.TrusteeForm=TRUSTEE_IS_SID;
420 ea[2].Trustee.TrusteeType=TRUSTEE_IS_USER;
421 userentry usrent;
422 usrent.initialize(process::getUserId());
423 PSID ownersid=(PSID)bytestring::duplicate(
424 usrent.getSid(),
425 usrent.getSidSize());
426 ea[2].Trustee.ptstrName=(LPSTR)ownersid;
427
428 // set access permissions
429 DWORD perms=0;
430 mode_t shift=permoctal;
431 for (int16_t i=0; i<9; i++) {
432 if (shift&1) {
433 uint8_t pos=i%3;
434 if (pos==0) {
435 // execute permissions
436 perms|=_EXEC;
437 } else if (pos==1) {
438 // write permissions
439 perms|=_WRITE;
440 } else {
441 // read permissions
442 perms|=_READ;
443 }
444 }
445 shift=shift>>1;
446 if (i==2) {
447 // set world permissions
448 ea[0].grfAccessPermissions=perms;
449 perms=0;
450 } else if (i==5) {
451 // set group permissions
452 ea[1].grfAccessPermissions=perms;
453 perms=0;
454 } else if (i==8) {
455 // set user permissions
456 ea[2].grfAccessPermissions=perms;
457 perms=0;
458 }
459 }
460
461 // set entries in acl
462 PACL pacl=NULL;
463 if (SetEntriesInAcl(3,ea,NULL,&pacl)!=ERROR_SUCCESS) {
464 LocalFree(pacl);
465 pacl=NULL;
466 }
467
468 // clean up
469 delete[] groupsid;
470 delete[] ownersid;
471
472 // return the acl
473 return (void *)pacl;
474 #else
475 return NULL;
476 #endif
477 }
478
daclToPermString(void * dacl)479 char *permissions::daclToPermString(void *dacl) {
480 return evalPermOctal(daclToPermOctal(dacl));
481 }
482
daclToPermOctal(void * dacl)483 mode_t permissions::daclToPermOctal(void *dacl) {
484
485 // init the return value
486 mode_t perms=0;
487
488 #ifdef RUDIMENTS_HAVE_GETACE
489
490 // get the user and convert to an sid
491 userentry usrent;
492 if (!usrent.initialize(process::getUserId())) {
493 return perms;
494 }
495 PSID usersid=(PSID)bytestring::duplicate(
496 usrent.getSid(),
497 usrent.getSidSize());
498
499 // get the group and convert to an sid
500 groupentry grpent;
501 if (!grpent.initialize(process::getGroupId())) {
502 return perms;
503 }
504 PSID groupsid=(PSID)bytestring::duplicate(
505 grpent.getSid(),
506 grpent.getSidSize());
507
508 // get the sid for others
509 // ("S-1-1-0" is the well known SID for "World")
510 PSID otherssid;
511 SID_IDENTIFIER_AUTHORITY worldsia=
512 SECURITY_WORLD_SID_AUTHORITY;
513 if (AllocateAndInitializeSid(&worldsia,1,
514 SECURITY_WORLD_RID,
515 0,0,0,0,0,0,0,
516 &otherssid)!=TRUE) {
517 return perms;
518 }
519
520 // cast the DACL properly
521 PACL d=(PACL)dacl;
522
523 // run through the ACEs of the DACL
524 for (DWORD i=0; i<d->AceCount; i++) {
525
526 // get the ACE
527 PVOID ace=NULL;
528 if (GetAce(d,i,&ace)==FALSE) {
529 continue;
530 }
531
532 // get various ACE components
533 ACCESS_ALLOWED_ACE *aace=(ACCESS_ALLOWED_ACE *)ace;
534 PSID sid=(PSID)&aace->SidStart;
535 DWORD mask=aace->Mask;
536
537 // which sid does this ACE apply to
538 if (EqualSid(sid,usersid)) {
539
540 if (aace->Header.AceType==
541 ACCESS_ALLOWED_ACE_TYPE) {
542
543 // update perms
544 if (mask&_ALL) {
545 perms|=ownerRead();
546 perms|=ownerWrite();
547 perms|=ownerExecute();
548 }
549 if (mask&_READ) {
550 perms|=ownerRead();
551 }
552 if (mask&(_WRITEWITHOUTSYNCHRONIZE)) {
553 perms|=ownerWrite();
554 }
555 if (mask&_EXEC) {
556 perms|=ownerExecute();
557 }
558
559 } else if (aace->Header.AceType==
560 ACCESS_DENIED_ACE_TYPE) {
561
562 // update perms
563 if (mask&_ALL) {
564 perms&=~ownerRead();
565 perms&=~ownerWrite();
566 perms&=~ownerExecute();
567 }
568 if (mask&_READ) {
569 perms&=~ownerRead();
570 }
571 if (mask&(_WRITEWITHOUTSYNCHRONIZE)) {
572 perms&=~ownerWrite();
573 }
574 if (mask&_EXEC) {
575 perms&=~ownerExecute();
576 }
577 }
578
579 } else if (EqualSid(sid,groupsid)) {
580
581 if (aace->Header.AceType==
582 ACCESS_ALLOWED_ACE_TYPE) {
583
584 // update perms
585 if (mask&_ALL) {
586 perms|=groupRead();
587 perms|=groupWrite();
588 perms|=groupExecute();
589 }
590 if (mask&_READ) {
591 perms|=groupRead();
592 }
593 if (mask&(_WRITEWITHOUTSYNCHRONIZE)) {
594 perms|=groupWrite();
595 }
596 if (mask&_EXEC) {
597 perms|=groupExecute();
598 }
599
600 } else if (aace->Header.AceType==
601 ACCESS_DENIED_ACE_TYPE) {
602
603 // update perms
604 if (mask&_ALL) {
605 perms&=~groupRead();
606 perms&=~groupWrite();
607 perms&=~groupExecute();
608 }
609 if (mask&_READ) {
610 perms&=~groupRead();
611 }
612 if (mask&(_WRITEWITHOUTSYNCHRONIZE)) {
613 perms&=~groupWrite();
614 }
615 if (mask&_EXEC) {
616 perms&=~groupExecute();
617 }
618 }
619
620 } else if (EqualSid(sid,otherssid)) {
621
622 if (aace->Header.AceType==
623 ACCESS_ALLOWED_ACE_TYPE) {
624
625 // update perms
626 if (mask&_ALL) {
627 perms|=othersRead();
628 perms|=othersWrite();
629 perms|=othersExecute();
630 }
631 if (mask&_READ) {
632 perms|=othersRead();
633 }
634 if (mask&(_WRITEWITHOUTSYNCHRONIZE)) {
635 perms|=othersWrite();
636 }
637 if (mask&_EXEC) {
638 perms|=othersExecute();
639 }
640
641 } else if (aace->Header.AceType==
642 ACCESS_DENIED_ACE_TYPE) {
643
644 // update perms
645 if (mask&_ALL) {
646 perms&=~othersRead();
647 perms&=~othersWrite();
648 perms&=~othersExecute();
649 }
650 if (mask&_READ) {
651 perms&=~othersRead();
652 }
653 if (mask&(_WRITEWITHOUTSYNCHRONIZE)) {
654 perms&=~othersWrite();
655 }
656 if (mask&_EXEC) {
657 perms&=~othersExecute();
658 }
659 }
660 }
661 }
662
663 // clean up
664 delete[] usersid;
665 delete[] groupsid;
666 LocalFree(otherssid);
667 #endif
668
669 // return permissions
670 return perms;
671 }
672