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