• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  *		 Display and audit security attributes in an NTFS volume
3  *
4  * Copyright (c) 2007-2016 Jean-Pierre Andre
5  *
6  *	Options :
7  *		-a auditing security data
8  *		-b backing up NTFS ACLs
9  *		-e set extra backed-up parameters (in conjunction with -s)
10  *		-h displaying hexadecimal security descriptors within a file
11  *		-r recursing in a directory
12  *		-s setting backed-up NTFS ACLs
13  *		-u getting a user mapping proposal
14  *		-v verbose (very verbose if set twice)
15  *	   also, if compile-time option is set
16  *		-t run internal tests (with no access to storage)
17  *
18  *	On Linux (being root, with volume not mounted) :
19  *		ntfssecaudit -h [file]
20  *			display the security descriptors found in file
21  *		ntfssecaudit -a[rv] volume
22  *			audit the volume
23  *		ntfssecaudit [-v] volume file
24  *			display the security parameters of file
25  *		ntfssecaudit -r[v] volume directory
26  *			display the security parameters of files in directory
27  *		ntfssecaudit -b[v] volume [directory]
28  *			backup the security parameters of files in directory
29  *		ntfssecaudit -s[ve] volume [backupfile]
30  *			set the security parameters as indicated in backup
31  *			with -e set extra parameters (Windows attrib)
32  *		ntfssecaudit volume perms file
33  *			set the security parameters of file to perms (mode or acl)
34  *		ntfssecaudit -r[v] volume perms directory
35  *			set the security parameters of files in directory to perms
36  *          special cases, do not require being root :
37  *		ntfssecaudit [-v] mounted-file
38  *			display the security parameters of mounted file
39  *		ntfssecaudit -u[v] mounted-file
40  *			display a user mapping proposal
41  *
42  *
43  *	On Windows (the volume being part of file name)
44  *		ntfssecaudit -h [file]
45  *			display the security descriptors found in file
46  *		ntfssecaudit -a[rv] volume
47  *			audit the volume
48  *		ntfssecaudit [-v] file
49  *			display the security parameters of file
50  *		ntfssecaudit -r[v] directory
51  *			display the security parameters of files in directory
52  *		ntfssecaudit -b[v] directory
53  *			backup the security parameters of files in directory
54  *		ntfssecaudit -s[v] volume [backupfile]
55  *			set the security parameters as indicated in backup
56  *			with -e set extra parameters (Windows attrib)
57  *		ntfssecaudit perms file
58  *			set the security parameters of file to perms (mode or acl)
59  *		ntfssecaudit -r[v] perms directory
60  *			set the security parameters of files in directory to perms
61  */
62 
63 /*	History
64  *
65  *  Nov 2007
66  *     - first version, by concatenating miscellaneous utilities
67  *
68  *  Jan 2008, version 1.0.1
69  *     - fixed mode displaying
70  *     - added a global severe errors count
71  *
72  *  Feb 2008, version 1.0.2
73  *     - implemented conversions for big-endian machines
74  *
75  *  Mar 2008, version 1.0.3
76  *     - avoided consistency checks on $Secure when there is no such file
77  *
78  *  Mar 2008, version 1.0.4
79  *     - changed ordering of ACE's
80  *     - changed representation for special flags
81  *     - defaulted to stdin for option -h
82  *     - added self tests (compile time option)
83  *     - fixed errors specific to big-endian computers
84  *
85  *  Apr 2008, version 1.1.0
86  *     - developped Posix ACLs to NTFS ACLs conversions
87  *     - developped NTFS ACLs backup and restore
88  *
89  *  Apr 2008, version 1.1.1
90  *     - fixed an error specific to big-endian computers
91  *     - checked hash value and fixed error report in restore
92  *     - improved display in showhex() and restore()
93  *
94  *  Apr 2008, version 1.1.2
95  *     - improved and fixed Posix ACLs to NTFS ACLs conversions
96  *
97  *  Apr 2008, version 1.1.3
98  *     - reenabled recursion for setting a new mode or ACL
99  *     - processed Unicode file names and displayed them as UTF-8
100  *     - allocated dynamically memory for file names when recursing
101  *
102  *  May 2008, version 1.1.4
103  *     - all Unicode/UTF-8 strings checked and processed
104  *
105  *  Jul 2008, version 1.1.5
106  *     - made Windows owner consistent with Linux owner when changing mode
107  *     - allowed owner change on Windows when it does not match Linux owner
108  *     - skipped currently unused code
109  *
110  *  Aug 2008, version 1.2.0
111  *     - processed \.NTFS-3G\UserMapping on Windows
112  *     - made use of user mappings through the security API or direct access
113  *     - fixed a bug in restore
114  *     - fixed UTF-8 conversions
115  *
116  *  Sep 2008, version 1.3.0
117  *     - split the code to have part of it shared with ntfs-3g library
118  *     - fixed testing for end of SDS block
119  *     - added samples checking in selftest for easier debugging
120  *
121  *  Oct 2008, version 1.3.1
122  *     - fixed outputting long long data when testing on a Palm organizer
123  *
124  *  Dec 2008, version 1.3.2
125  *     - fixed restoring ACLs
126  *     - added optional logging of ACL hashes to facilitate restore checks
127  *     - fixed collecting SACLs
128  *     - fixed setting special control flags
129  *     - fixed clearing existing SACLs (Linux only) and DACLs
130  *     - changed the sequencing of items when quering a security descriptor
131  *     - avoided recursing on junctions and symlinks on Windows
132  *
133  *  Jan 2009, version 1.3.3
134  *     - save/restore Windows attributes (code from Faisal)
135  *
136  *  Mar 2009, version 1.3.4
137  *     - enabled displaying attributes of a mounted file over Linux
138  *
139  *  Apr 2009, version 1.3.5
140  *     - fixed initialisation of stand-alone user mapping
141  *     - fixed POSIXACL redefinition when included in the ntfs-3g package
142  *     - fixed displaying of options
143  *     - fixed a dependency on the shared library version used
144  *
145  *  May 2009, version 1.3.6
146  *     - added new samples for self testing
147  *
148  *  Jun 2009, version 1.3.7
149  *     - fixed displaying owner and group of a mounted file over Linux
150  *
151  *  Jul 2009, version 1.3.8
152  *     - fixed again displaying owner and group of a mounted file over Linux
153  *     - cleaned some code to avoid warnings
154  *
155  *  Nov 2009, version 1.3.9
156  *     - allowed security descriptors up to 64K
157  *
158  *  Nov 2009, version 1.3.10
159  *     - applied patches for MacOSX from Erik Larsson
160  *
161  *  Nov 2009, version 1.3.11
162  *     - replace <attr/xattr.h> by <sys/xattr.h> (provided by glibc)
163  *
164  *  Dec 2009, version 1.3.12
165  *     - worked around "const" possibly redefined in config.h
166  *
167  *  Dec 2009, version 1.3.13
168  *     - fixed the return code of dorestore()
169  *
170  *  Dec 2009, version 1.3.14
171  *     - adapted to opensolaris
172  *
173  *  Jan 2010, version 1.3.15
174  *     - more adaptations to opensolaris
175  *     - removed the fix for return code of dorestore()
176  *
177  *  Jan 2010, version 1.3.16
178  *     - repeated the fix for return code of dorestore()
179  *
180  *  Mar 2010, version 1.3.17
181  *     - adapted to new default user mapping
182  *     - fixed #ifdef'd code for selftest
183  *
184  *  May 2010, version 1.3.18
185  *     - redefined early error logging
186  *
187  *  Mar 2011, version 1.3.19
188  *     - fixed interface to ntfs_initialize_file_security()
189  *
190  *  Apr 2011, version 1.3.20
191  *     - fixed false memory leak detection
192  *
193  *  Jun 2011, version 1.3.21
194  *     - cleaned a few unneeded variables
195  *
196  *  Nov 2011, version 1.3.22
197  *     - added a distinctive prefix to owner and group SID
198  *     - fixed a false memory leak detection
199  *
200  *  Jun 2012, version 1.3.23
201  *     - added support for SACL (nickgarvey)
202  *
203  *  Jul 2012, version 1.3.24
204  *     - added self-tests for authenticated users
205  *     - added display of ace-inherited flag
206  *     - made runnable on OpenIndiana
207  *
208  *  Aug 2012, version 1.4.0
209  *     - added an option for user mapping proposal
210  *
211  *  Sep 2013, version 1.4.1
212  *     - silenced an aliasing warning by gcc >= 4.8
213  *
214  *  May 2014, version 1.4.2
215  *     - decoded GENERIC_ALL permissions
216  *     - decoded more "well-known" and generic SIDs
217  *     - showed Windows ownership in verbose situations
218  *     - fixed apparent const violations
219  *
220  *  Dec 2014, version 1.4.3
221  *     - fixed displaying "UserMapping" as a file name
222  *
223  *  Mar 2015, version 1.4.5
224  *     - adapted to new NTFS ACLs when owner is same as group
225  *
226  *  May 2015, version 1.4.6
227  *     - made to load shared library based on generic name
228  *
229  *  Mar 2016, Version 1.5.0
230  *     - reorganized to rely on libntfs-3g even on Windows
231  */
232 
233 /*
234  * This program is free software; you can redistribute it and/or modify
235  * it under the terms of the GNU General Public License as published by
236  * the Free Software Foundation; either version 2 of the License, or
237  * (at your option) any later version.
238  *
239  * This program is distributed in the hope that it will be useful,
240  * but WITHOUT ANY WARRANTY; without even the implied warranty of
241  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
242  * GNU General Public License for more details.
243  *
244  * You should have received a copy of the GNU General Public License
245  * along with this program (in the main directory of the NTFS-3G
246  * distribution in the file COPYING); if not, write to the Free Software
247  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
248  */
249 
250 /*
251  *		General parameters which may have to be adapted to needs
252  */
253 
254 #define AUDT_VERSION "1.5.0"
255 
256 #define SELFTESTS 0
257 #define NOREVBOM 0 /* still unclear what this should be */
258 
259 /*
260  *			External declarations
261  */
262 
263 #include "config.h"
264 
265 #ifdef HAVE_STDIO_H
266 #include <stdio.h>
267 #endif /* HAVE_STDIO_H */
268 #ifdef HAVE_STDLIB_H
269 #include <stdlib.h>
270 #endif /* HAVE_STDLIB_H */
271 #ifdef HAVE_STRING_H
272 #include <string.h>
273 #endif /* HAVE_STRING_H */
274 #ifdef HAVE_UNISTD_H
275 #include <unistd.h>
276 #endif /* HAVE_UNISTD_H */
277 #ifdef HAVE_TIME_H
278 #include <time.h>
279 #endif /* HAVE_TIME_H */
280 #ifdef HAVE_GETOPT_H
281 #include <getopt.h>
282 #endif /* HAVE_GETOPT_H */
283 #ifdef HAVE_ERRNO_H
284 #include <errno.h>
285 #endif /* HAVE_ERRNO_H */
286 #ifdef HAVE_FCNTL_H
287 #include <fcntl.h>
288 #endif /* HAVE_FCNTL_H */
289 #ifdef HAVE_SYS_TYPES_H
290 #include <sys/types.h>
291 #endif /* HAVE_SYS_TYPES_H */
292 #ifdef HAVE_SYS_STAT_H
293 #include <sys/stat.h>
294 #endif /* HAVE_SYS_STAT_H */
295 #ifdef HAVE_SETXATTR
296 #include <sys/xattr.h>
297 #else /* HAVE_SETXATTR */
298 #warning "The extended attribute package is not available"
299 #endif /* HAVE_SETXATTR */
300 
301 #include "types.h"
302 #include "endians.h"
303 #include "support.h"
304 #include "layout.h"
305 #include "param.h"
306 #include "ntfstime.h"
307 #include "device_io.h"
308 #include "device.h"
309 #include "logging.h"
310 #include "runlist.h"
311 #include "mft.h"
312 #include "inode.h"
313 #include "attrib.h"
314 #include "bitmap.h"
315 #include "index.h"
316 #include "volume.h"
317 #include "unistr.h"
318 #include "mst.h"
319 #include "security.h"
320 #include "acls.h"
321 #include "realpath.h"
322 #include "utils.h"
323 #include "misc.h"
324 
325 struct CALLBACK;
326 
327 typedef int (*dircallback)(void *context, const ntfschar *ntfsname,
328 	const int length, const int type, const s64 pos,
329 	const MFT_REF mft_ref, const unsigned int dt_type);
330 
331 #if POSIXACLS
332 
333 static BOOL same_posix(struct POSIX_SECURITY *pxdesc1,
334 			struct POSIX_SECURITY *pxdesc2);
335 
336 #endif /* POSIXACLS */
337 
338 #ifndef HAVE_SYSLOG_H
339 void ntfs_log_early_error(const char *format, ...)
340 			__attribute__((format(printf, 1, 2)));
341 #endif /* HAVE_SYSLOG_H */
342 
343 #define ACCOUNTSIZE 256  /* maximum size of an account name */
344 #define MAXFILENAME 4096
345 #define MAXATTRSZ 65536 /* Max sec attr size (16448 met for WinXP) */
346 #define MAXLINE 80 /* maximum processed size of a line */
347 #define BUFSZ 1024		/* buffer size to read mapping file */
348 #define LINESZ 120		/* maximum useful size of a mapping line */
349 
350 typedef enum { RECSHOW, RECSET, RECSETPOSIX } RECURSE;
351 typedef enum { MAPNONE, MAPEXTERN, MAPLOCAL, MAPDUMMY } MAPTYPE;
352 typedef enum { CMD_AUDIT, CMD_BACKUP, CMD_HEX, CMD_HELP, CMD_SET,
353 			CMD_TEST, CMD_USERMAP, CMD_VERSION, CMD_NONE } CMDS;
354 
355 
356 #define MAXSECURID 262144
357 #define SECBLKSZ 8
358 #define MAPDIR ".NTFS-3G"
359 #define MAPFILE "UserMapping"
360 
361 #ifdef HAVE_WINDOWS_H
362 #define DIRSEP "\\"
363 #else
364 #define DIRSEP "/"
365 #endif
366 
367 	/* standard owner (and administrator) rights */
368 
369 #define OWNER_RIGHTS (DELETE | READ_CONTROL | WRITE_DAC | WRITE_OWNER \
370 			| SYNCHRONIZE \
371 			| FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES \
372 			| FILE_READ_EA | FILE_WRITE_EA)
373 
374 	/* standard world rights */
375 
376 #define WORLD_RIGHTS (READ_CONTROL | FILE_READ_ATTRIBUTES | FILE_READ_EA \
377 			| SYNCHRONIZE)
378 
379 	  /* inheritance flags for files and directories */
380 
381 #define FILE_INHERITANCE NO_PROPAGATE_INHERIT_ACE
382 #define DIR_INHERITANCE (OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE)
383 
384 /*
385  *		To identify NTFS ACL meaning Posix ACL granted to root
386  *	we use rights always granted to anybody, so they have no impact
387  *	either on Windows or on Linux.
388  */
389 
390 #define ROOT_OWNER_UNMARK SYNCHRONIZE	/* ACL granted to root as owner */
391 #define ROOT_GROUP_UNMARK FILE_READ_EA	/* ACL granted to root as group */
392 
393 #define INSDS1 1
394 #define INSDS2 2
395 #define INSII 4
396 #define INSDH 8
397 
398 struct SII {		/* this is an image of an $SII index entry */
399 	le16 offs;
400 	le16 size;
401 	le32 fill1;
402 	le16 indexsz;
403 	le16 indexksz;
404 	le16 flags;
405 	le16 fill2;
406 	le32 keysecurid;
407 
408 	/* did not find official description for the following */
409 	le32 hash;
410 	le32 securid;
411 	le32 dataoffsl; /* documented as badly aligned */
412 	le32 dataoffsh;
413 	le32 datasize;
414 } ;
415 
416 struct SDH {		/* this is an image of an $SDH index entry */
417 	le16 offs;
418 	le16 size;
419 	le32 fill1;
420 	le16 indexsz;
421 	le16 indexksz;
422 	le16 flags;
423 	le16 fill2;
424 	le32 keyhash;
425 	le32 keysecurid;
426 
427 	/* did not find official description for the following */
428 	le32 hash;
429 	le32 securid;
430 	le32 dataoffsl;
431 	le32 dataoffsh;
432 	le32 datasize;
433 	le32 fill3;
434 	} ;
435 
436 #ifdef HAVE_WINDOWS_H
437 /*
438  *	Including <windows.h> leads to numerous conflicts with layout.h
439  *	so define a few needed Windows calls unrelated to ntfs-3g
440  */
441 BOOL WINAPI LookupAccountSidA(const char*, void*, char*, u32*,
442 		char*, u32*, s32*);
443 u32 WINAPI GetFileAttributesA(const char*);
444 #endif /* HAVE_WINDOWS_H */
445 
446 #define INVALID_FILE_ATTRIBUTES (-1)/* error from ntfs_get_file_attributes() */
447 
448 /*
449  *		Structures for collecting directory contents
450  */
451 
452 struct LINK {
453 	struct LINK *next;
454 	char name[1];
455 } ;
456 
457 struct CALLBACK {
458 	struct LINK *head;
459 	const char *dir;
460 } ;
461 
462 static int callback(void *context, const ntfschar *ntfsname,
463 	const int length, const int type, const s64 pos,
464 	const MFT_REF mft_ref, const unsigned int dt_type);
465 
466 struct SECURITY_DATA {
467         u64 offset;
468         char *attr;
469         u32 hash;
470         u32 length;
471         unsigned int filecount:16;
472         unsigned int mode:12;
473         unsigned int flags:4;
474 } ;
475 
476 /*
477  *		  Global constants
478  */
479 
480 #define BANNER "ntfssecaudit " AUDT_VERSION " : NTFS security data auditing"
481 
482 #ifdef SELFTESTS
483 
484 /*
485  *		Dummy mapping file (self tests only)
486  */
487 
488 #define DUMMYAUTH "S-1-5-21-3141592653-589793238-462843383-"
489 char dummymapping[] =	"500::" DUMMYAUTH "1000\n"
490 			"501::" DUMMYAUTH "1001\n"
491 			"502::" DUMMYAUTH "1002\n"
492 			"503::" DUMMYAUTH "1003\n"
493 			"516::" DUMMYAUTH "1016\n"
494 			":500:" DUMMYAUTH "513\r\n"
495 			":511:S-1-5-21-1607551490-981732888-1819828000-513\n"
496 			":516:" DUMMYAUTH "1012\r\n"
497 			"::"	DUMMYAUTH "10000\n";
498 
499 /*
500  *		SID for authenticated user (S-1-5-11)
501  */
502 
503 static const char authsidbytes[] = {
504 		1,		/* revision */
505 		1,		/* auth count */
506 		0, 0, 0, 0, 0, 5,	/* base */
507 		11, 0, 0, 0	/* 1st level */
508 };
509 
510 static const SID *authsid = (const SID*)authsidbytes;
511 
512 /*
513  *		SID for local users (S-1-5-32-545)
514  */
515 
516 static const char localsidbytes[] = {
517 		1,		/* revision */
518 		2,		/* auth count */
519 		0, 0, 0, 0, 0, 5,	/* base */
520 		32, 0, 0, 0,	/* 1st level */
521 		33, 2, 0, 0	/* 2nd level */
522 };
523 
524 static const SID *localsid = (const SID*)localsidbytes;
525 
526 /*
527  *		SID for system (S-1-5-18)
528  */
529 
530 static const char systemsidbytes[] = {
531 		1,		/* revision */
532 		1,		/* auth count */
533 		0, 0, 0, 0, 0, 5,	/* base */
534 		18, 0, 0, 0	/* 1st level */
535 	};
536 
537 static const SID *systemsid = (const SID*)systemsidbytes;
538 
539 #endif /* SELFTESTS */
540 
541 /*
542  *		  Global variables
543  */
544 
545 BOOL opt_e;	/* restore extra (currently windows attribs) */
546 BOOL opt_r;	/* recursively apply to subdirectories */
547 BOOL opt_u;	/* user mapping proposal */
548 int opt_v;  /* verbose or very verbose*/
549 CMDS cmd; /* command to process */
550 struct SECURITY_DATA *securdata[(MAXSECURID + (1 << SECBLKSZ) - 1)/(1 << SECBLKSZ)];
551 unsigned int errors; /* number of severe errors */
552 unsigned int warnings; /* number of non-severe errors */
553 
554 struct SECURITY_CONTEXT context;
555 MAPTYPE mappingtype;
556 struct SECURITY_API *ntfs_context = (struct SECURITY_API*)NULL;
557 
558 /*
559  *		Open and close the security API (obsolete)
560  */
561 
open_security_api(void)562 static BOOL open_security_api(void)
563 {
564 	return (TRUE);
565 }
566 
close_security_api(void)567 static BOOL close_security_api(void)
568 {
569 	return (0);
570 }
571 
572 /*
573  *		Open and close a volume
574  *	Assumes a single volume is opened
575  */
576 
open_volume(const char * volume,unsigned long flags)577 static BOOL open_volume(const char *volume, unsigned long flags)
578 {
579 	BOOL ok;
580 
581 	ok = FALSE;
582 	if (!ntfs_context) {
583 		ntfs_context = ntfs_initialize_file_security(volume, flags);
584 		if (ntfs_context) {
585 			if (*(u32*)ntfs_context != MAGIC_API) {
586 				fprintf(stderr,"Versions of ntfs-3g and ntfssecaudit"
587 						" are not compatible\n");
588 			} else {
589 				fprintf(stderr,"\"%s\" opened %s\n",volume,
590 					(flags & NTFS_MNT_RDONLY
591 						 ? "read-only" : "read-write"));
592 				mappingtype = MAPEXTERN;
593 				ok = TRUE;
594 			}
595 		} else {
596 			fprintf(stderr,"Could not open \"%s\"\n",volume);
597 #ifdef HAVE_WINDOWS_H
598 			switch (errno) {
599 			case EACCES :
600 				fprintf(stderr,"You need Administrator rights to open \"%s\"\n",
601 							volume);
602 				break;
603 			case EBUSY :
604 				fprintf(stderr,"Looks like \"%s\" is mounted,\n",volume);
605 				fprintf(stderr,"close all applications using it\n");
606 				break;
607 			default :
608 				fprintf(stderr,"Close all applications using %s\n", volume);
609 				fprintf(stderr,"to make sure it is not mounted\n");
610 			}
611 #else
612 			fprintf(stderr,"Make sure \"%s\" is not mounted\n",volume);
613 #endif
614 		}
615 	} else
616 		fprintf(stderr,"A volume is already open\n");
617 	return (ok);
618 }
619 
close_volume(const char * volume)620 static BOOL close_volume(const char *volume)
621 {
622 	BOOL r;
623 
624 	r = ntfs_leave_file_security(ntfs_context);
625 	if (r)
626 		fprintf(stderr,"\"%s\" closed\n",volume);
627 	else
628 		fprintf(stderr,"Could not close \"%s\"\n",volume);
629 	ntfs_context = (struct SECURITY_API*)NULL;
630 	return (r);
631 }
632 
633 #ifdef HAVE_WINDOWS_H
634 
635 /*
636  *		Make a path suitable for feeding to libntfs-3g
637  *
638  *	Use '/' as a directory separator and remove /../ and /./
639  */
640 
cleanpath(char * path)641 static int cleanpath(char *path)
642 {
643 	int err;
644 	char *p;
645 	char *s, *d;
646 
647 	err = 0;
648 	for (p=path; *p; p++)
649 		if (*p == '\\')
650 			*p = '/';
651 	do {
652 		s = (char*)NULL;
653 		p = strstr(path, "/./");
654 		if (p) {
655 			s = p + 3;
656 			d = p + 1;
657 		} else {
658 			p = strstr(path, "/../");
659 			if (p) {
660 				d = p;
661 				while ((d != path) && (*--d != '/'))
662 					d--;
663 				if ((d != p) && (*d == '/')) {
664 					s = p + 3;
665 				} else
666 					err = 1;
667 			}
668 		}
669 		if (s) {
670 			while (*s)
671 				*d++ = *s++;
672 			*d = 0;
673 		}
674 	} while (s && !err);
675 	return (err);
676 }
677 
678 /*
679  *		Build a path with Unix-type separators
680  *
681  *	The path from the ntfs root is required for libntfs-3g calls
682  */
683 
unixname(const char * name)684 static char *unixname(const char *name)
685 {
686 	char *uname;
687 
688 	uname = (char*)malloc(strlen(name) + 1);
689 	if (uname) {
690 		strcpy(uname, name);
691 		if (cleanpath(uname)) {
692 			fprintf(stderr,"Bad path %s\n",name);
693 			free(uname);
694 			uname = (char*)NULL;
695 		}
696 	}
697 	return (uname);
698 }
699 
700 #endif /* HAVE_WINDOWS_H */
701 
702 /*
703  *		Extract small or big endian data from an array of bytes
704  */
705 
get2l(const char * attr,int p)706 static unsigned int get2l(const char *attr, int p)
707 {
708 	int i;
709 	unsigned int v;
710 
711 	v = 0;
712 	for (i=0; i<2; i++)
713 		v += (attr[p+i] & 255) << (8*i);
714 	return (v);
715 }
716 
get4l(const char * attr,int p)717 static unsigned long get4l(const char *attr, int p)
718 {
719 	int i;
720 	unsigned long v;
721 
722 	v = 0;
723 	for (i=0; i<4; i++)
724 		v += ((long)(attr[p+i] & 255)) << (8*i);
725 	return (v);
726 }
727 
get6h(const char * attr,int p)728 static u64 get6h(const char *attr, int p)
729 {
730 	int i;
731 	u64 v;
732 
733 	v = 0;
734 	for (i=0; i<6; i++)
735 		v = (v << 8) + (attr[p+i] & 255);
736 	return (v);
737 }
738 
get8l(const char * attr,int p)739 static u64 get8l(const char *attr, int p)
740 {
741 	int i;
742 	u64 v;
743 
744 	v = 0;
745 	for (i=0; i<8; i++)
746 		v += ((long long)(attr[p+i] & 255)) << (8*i);
747 	return (v);
748 }
749 
750 /*
751  *		Set small or big endian data into an array of bytes
752  */
753 
set2l(char * p,unsigned int v)754 static void set2l(char *p, unsigned int v)
755 {
756 	int i;
757 
758 	for (i=0; i<2; i++)
759 		p[i] = ((v >> 8*i) & 255);
760 }
761 
set4l(char * p,unsigned long v)762 static void set4l(char *p, unsigned long v)
763 {
764 	int i;
765 
766 	for (i=0; i<4; i++)
767 		p[i] = ((v >> 8*i) & 255);
768 }
769 
770 
771 /*
772  *		hexadecimal dump of an array of bytes
773  */
774 
hexdump(const char * attr,int size,int level)775 static void hexdump(const char *attr, int size, int level)
776 {
777 	int i,j;
778 
779 	for (i=0; i<size; i+=16) {
780 		if (level)
781 			printf("%*c",level,' ');
782 		printf("%06x ",i);
783 		for (j=i; (j<(i+16)) && (j<size); j++)
784 			printf((j & 3 ? "%02x" : " %02x"),attr[j] & 255);
785 		printf("\n");
786 	}
787 }
788 
hash(const le32 * buf,int size)789 static u32 hash(const le32 *buf, int size /* bytes */)
790 {
791 	u32 h;
792 	int i;
793 
794 	h = 0;
795 	for (i=0; 4*i<size; i++)
796 		h = le32_to_cpu(buf[i]) + (h << 3) + ((h >> 29) & 7);
797 	return (h);
798 }
799 
800 /*
801  *		Evaluate the size of UTS-8 conversion of a UTF-16LE text
802  *	trailing '\0' not accounted for
803  *	Returns 0 for invalid input
804  */
805 
utf8size(const ntfschar * utf16,int length)806 static unsigned int utf8size(const ntfschar *utf16, int length)
807 {
808 	int i;
809 	int count = 0;
810 	BOOL surrog;
811 	BOOL fail;
812 
813 	surrog = FALSE;
814 	fail = FALSE;
815 	for (i = 0; i < length && utf16[i] && !fail; i++) {
816 		unsigned short c = le16_to_cpu(utf16[i]);
817 		if (surrog) {
818 			if ((c >= 0xdc00) && (c < 0xe000)) {
819 				surrog = FALSE;
820 				count += 4;
821 			} else
822 				fail = TRUE;
823 		} else
824 			if (c < 0x80)
825 				count++;
826 			else if (c < 0x800)
827 				count += 2;
828 			else if (c < 0xd800)
829 				count += 3;
830 			else if (c < 0xdc00)
831 				surrog = TRUE;
832 #if NOREVBOM
833 			else if ((c >= 0xe000) && (c < 0xfffe))
834 #else
835 			else if (c >= 0xe000)
836 #endif
837 				count += 3;
838 			else
839 				fail = TRUE;
840 	}
841 	if (surrog)
842 		fail = TRUE;
843 
844 	return (fail ? 0 : count);
845 }
846 
847 /*
848  *		Convert a UTF-16LE text to UTF-8
849  *	Note : wcstombs() not used because on Linux it fails for characters
850  *	not present in current locale
851  *	Returns size or zero for invalid input
852  */
853 
makeutf8(char * utf8,const ntfschar * utf16,int length)854 static unsigned int makeutf8(char *utf8, const ntfschar *utf16, int length)
855 {
856 	int size;
857 
858 	size = ntfs_ucstombs(utf16, length, &utf8, MAXFILENAME);
859 	return (size < 0 ? 0 : size);
860 }
861 
862 /*
863  *		Print a file name
864  *	on Windows it prints UTF-16LE names as UTF-8
865  */
866 
printname(FILE * file,const char * name)867 static void printname(FILE *file, const char *name)
868 {
869 #ifdef HAVE_WINDOWS_H
870 	char *wname;
871 	char *p;
872 
873 	wname = (char*)malloc(strlen(name) + 1);
874 	if (wname) {
875 		strcpy(wname, name);
876 		for (p=wname; *p; p++)
877 			if (*p == '/')
878 				*p = '\\';
879 		fprintf(file,"%s", wname);
880 		free(wname);
881 	}
882 #else /* HAVE_WINDOWS_H */
883 	fprintf(file,"%s",name);
884 #endif /* HAVE_WINDOWS_H */
885 }
886 
887 /*
888  *		Print the last error code
889  */
890 
printerror(FILE * file)891 static void printerror(FILE *file)
892 {
893 	if (errno)
894 		fprintf(file,"Error code %d : %s\n",errno,strerror(errno));
895 	switch (errno) {
896 	case EACCES :
897 		fprintf(file,"You probably need Administrator rights\n");
898 		break;
899 	case EBUSY :
900 		fprintf(file,"You probably try to write to a mounted device\n");
901 		break;
902 	default :
903 		break;
904 	}
905 }
906 
907 #ifndef HAVE_SYSLOG_H
908 
909 /*
910  *		Redefine early error messages in stand-alone situations
911  */
912 
ntfs_log_early_error(const char * format,...)913 static void ntfs_log_early_error(const char *format, ...)
914 {
915 	va_list args;
916 
917 	va_start(args, format);
918 	vfprintf(stderr,format,args);
919 	va_end(args);
920 }
921 
922 #endif /* HAVE_SYSLOG_H */
923 
924 /*
925  *	Guess whether a security attribute is intended for a directory
926  *	based on the presence of inheritable ACE
927  *	(not 100% reliable)
928  */
929 
guess_dir(const char * attr)930 static BOOL guess_dir(const char *attr)
931 {
932 	int off;
933 	int isdir;
934 	int cnt;
935 	int i;
936 	int x;
937 
938 	isdir = 0;
939 	off = get4l(attr,16);
940 	if (off) {
941 		cnt = get2l(attr,off+4);
942 		x = 8;
943 		for (i=0; i<cnt; i++) {
944 			if (attr[off + x + 1] & 3)
945 				isdir = 1;
946 			x += get2l(attr,off + x + 2);
947 		}
948 	}
949 	return (isdir);
950 }
951 
952 /*
953  *		   Display a SID
954  *   See http://msdn2.microsoft.com/en-us/library/aa379649.aspx
955  */
956 
showsid(const char * attr,int off,const char * prefix,int level)957 static void showsid(const char *attr, int off, const char *prefix, int level)
958 {
959 	int cnt;
960 	int i;
961 	BOOL known;
962 	u64 auth;
963 	unsigned long first;
964 	unsigned long second;
965 	unsigned long last;
966 	char marker;
967 
968 	if (cmd == CMD_BACKUP)
969 		marker = '#';
970 	else
971 		marker = ' ';
972 	cnt = attr[off+1] & 255;
973 	auth = get6h(attr,off+2);
974 	known = FALSE;
975 	/* SID names taken from https://support.microsoft.com/en-us/kb/243330 */
976 	if ((attr[off] == 1) /* revision */
977 	     && cnt
978 	     && (auth < 100)) {
979 		first = get4l(attr,off+8);
980 		switch (cnt) {
981 		case 0 : /* no level (error) */
982 			break;
983 		case 1 : /* single level */
984 			switch (auth) {
985 			case 0 :
986 				if (first == 0) {
987 					known = TRUE;
988 					printf("%*cNull SID\n",-level,marker);
989 				}
990 				break;
991 			case 1 :
992 				if (first == 0) {
993 					known = TRUE;
994 					printf("%*cWorld SID\n",-level,marker);
995 				}
996 				break;
997 			case 3 :
998 				switch (first) {
999 				case 0 :
1000 					known = TRUE;
1001 					printf("%*cCreator owner SID\n",-level,marker);
1002 					break;
1003 				case 1 :
1004 					known = TRUE;
1005 					printf("%*cCreator group SID\n",-level,marker);
1006 					break;
1007 				}
1008 				break;
1009 			case 5 :
1010 				switch (first) {
1011 				case 1 :
1012 					known = TRUE;
1013 					printf("%*cDialup SID\n",-level,marker);
1014 					break;
1015 				case 2 :
1016 					known = TRUE;
1017 					printf("%*cNetwork SID\n",-level,marker);
1018 					break;
1019 				case 3 :
1020 					known = TRUE;
1021 					printf("%*cBatch SID\n",-level,marker);
1022 					break;
1023 				case 4 :
1024 					known = TRUE;
1025 					printf("%*cInteractive SID\n",-level,marker);
1026 					break;
1027 				case 6 :
1028 					known = TRUE;
1029 					printf("%*cService SID\n",-level,marker);
1030 					break;
1031 				case 7 :
1032 					known = TRUE;
1033 					printf("%*cAnonymous SID\n",-level,marker);
1034 					break;
1035 				case 11 :
1036 					known = TRUE;
1037 					printf("%*cAuthenticated Users SID\n",-level,marker);
1038 					break;
1039 				case 13 :
1040 					known = TRUE;
1041 					printf("%*cTerminal Server Users SID\n",-level,marker);
1042 					break;
1043 				case 14 :
1044 					known = TRUE;
1045 					printf("%*cRemote Interactive Logon SID\n",-level,marker);
1046 					break;
1047 				case 18 :
1048 					known = TRUE;
1049 					printf("%*cLocal System SID\n",-level,marker);
1050 					break;
1051 				}
1052 				break;
1053 			}
1054 			break;
1055 		case 2 : /* double level */
1056 			second = get4l(attr,off+12);
1057 			switch (auth) {
1058 			case 5 :
1059 				if (first == 32) {
1060 					known = TRUE;
1061 					switch (second) {
1062 					case 544 :
1063 						printf("%*cAdministrators SID\n",-level,marker);
1064 						break;
1065 					case 545 :
1066 						printf("%*cUsers SID\n",-level,marker);
1067 						break;
1068 					case 546 :
1069 						printf("%*cGuests SID\n",-level,marker);
1070 						break;
1071 					default :
1072 						printf("%*cSome domain SID\n",-level,marker);
1073 						break;
1074 					}
1075 				}
1076 				break;
1077 			}
1078 			break;
1079 		default : /* three levels or more */
1080 			second = get4l(attr,off+12);
1081 			last = get4l(attr,off+4+4*cnt);
1082 			switch (auth) {
1083 			case 5 :
1084 				if (first == 21) {
1085 					known = TRUE;
1086 					switch (last) {
1087 					case 500 :
1088 						printf("%*cAdministrator SID\n",-level,marker);
1089 						break;
1090 					case 501 :
1091 						printf("%*cGuest SID\n",-level,marker);
1092 						break;
1093 					case 512 :
1094 						printf("%*cDomain Admins SID\n",-level,marker);
1095 						break;
1096 					case 513 :
1097 						printf("%*cDomain Users SID\n",-level,marker);
1098 						break;
1099 					case 514 :
1100 						printf("%*cDomain Guests SID\n",-level,marker);
1101 						break;
1102 					default :
1103 						printf("%*cLocal user-%lu SID\n",-level,marker,last);
1104 						break;
1105 					}
1106 				}
1107 				break;
1108 			}
1109 		}
1110 	}
1111 	if (!known)
1112 		printf("%*cUnknown SID\n",-level,marker);
1113 	printf("%*c%shex S-%x-",-level,marker,prefix,attr[off] & 255);
1114 	printf("%llx",(long long)auth);
1115 	for (i=0; i<cnt; i++)
1116 		printf("-%lx",get4l(attr,off+8+4*i));
1117 	printf("\n");
1118 	printf("%*c%sdec S-%u-",-level,marker,prefix,attr[off] & 255);
1119 	printf("%llu",(long long)auth);
1120 	for (i=0; i<cnt; i++)
1121 		printf("-%lu",get4l(attr,off+8+4*i));
1122 	printf("\n");
1123 }
1124 
showusid(const char * attr,int level)1125 static void showusid(const char *attr, int level)
1126 {
1127 	int off;
1128 	char marker;
1129 
1130 	if (cmd == CMD_BACKUP)
1131 		marker = '#';
1132 	else
1133 		marker = ' ';
1134 	if (level)
1135 		printf("%*c",-level,marker);
1136 	printf("Owner SID\n");
1137 	off = get4l(attr,4);
1138 	showsid(attr,off,"O:",level+4);
1139 }
1140 
showgsid(const char * attr,int level)1141 static void showgsid(const char *attr, int level)
1142 {
1143 	int off;
1144 	char marker;
1145 
1146 	if (cmd == CMD_BACKUP)
1147 		marker = '#';
1148 	else
1149 		marker = ' ';
1150 	if (level)
1151 		printf("%*c",-level,marker);
1152 	printf("Group SID\n");
1153 	off = get4l(attr,8);
1154 	showsid(attr,off,"G:",level+4);
1155 }
1156 
showownership(const char * attr)1157 static void showownership(const char *attr)
1158 {
1159 #ifdef HAVE_WINDOWS_H
1160 	char account[ACCOUNTSIZE];
1161 	BIGSID sidcopy;
1162 	s32 use;
1163 	u32 accountsz;
1164 	u32 domainsz;
1165 #endif /* HAVE_WINDOWS_H */
1166 	enum { SHOWOWN, SHOWGRP, SHOWINT, SHOWDONE } shown;
1167 	const char *sid;
1168 	const char *prefix;
1169 	u64 auth;
1170 	int cnt;
1171 	int off;
1172 	int i;
1173 
1174 	for (shown=SHOWOWN; shown<SHOWDONE; ) {
1175 		switch (shown) {
1176 		case SHOWOWN :
1177 			off = get4l(attr,4);
1178 			sid = &attr[off];
1179 			prefix = "Windows owner";
1180 			shown = SHOWGRP;
1181 			break;
1182 		case SHOWGRP :
1183 			off = get4l(attr,8);
1184 			sid = &attr[off];
1185 			prefix = "Windows group";
1186 			shown = SHOWINT;
1187 			break;
1188 #if OWNERFROMACL
1189 		case SHOWINT :
1190 			off = get4l(attr,4);
1191 			prefix = "Interpreted owner";
1192 			sid = (const char*)ntfs_acl_owner((const char*)attr);
1193 			if (ntfs_same_sid((const SID*)sid,
1194 						(const SID*)&attr[off]))
1195 				sid = (const char*)NULL;
1196 			shown = SHOWDONE;
1197 			break;
1198 #endif /* OWNERFROMACL */
1199 		default :
1200 			sid = (const char*)NULL;
1201 			prefix = (const char*)NULL;
1202 			shown = SHOWDONE;
1203 			break;
1204 		}
1205 		if (sid) {
1206 			cnt = sid[1] & 255;
1207 			auth = get6h(sid,2);
1208 			if (cmd == CMD_BACKUP)
1209 				printf("# %s S-%d-",prefix,sid[0] & 255);
1210 			else
1211 				printf("%s S-%d-",prefix,sid[0] & 255);
1212 			printf("%llu",(long long)auth);
1213 			for (i=0; i<cnt; i++)
1214 				printf("-%lu",get4l(sid,8+4*i));
1215 #ifdef HAVE_WINDOWS_H
1216 			memcpy(sidcopy,sid,ntfs_sid_size((const SID*)sid));
1217 			accountsz = ACCOUNTSIZE;
1218 			domainsz = ACCOUNTSIZE;
1219 			if (LookupAccountSidA((const char*)NULL, sidcopy,
1220 					account, &accountsz,
1221 					(char*)NULL, &domainsz, &use))
1222 				printf(" (%s)", account);
1223 #endif /* HAVE_WINDOWS_H */
1224 			printf("\n");
1225 		}
1226 	}
1227 }
1228 
showheader(const char * attr,int level)1229 static void showheader(const char *attr, int level)
1230 {
1231 	int flags;
1232 	char marker;
1233 
1234 	if (cmd == CMD_BACKUP)
1235 		marker = '#';
1236 	else
1237 		marker = ' ';
1238 	if (level)
1239 		printf("%*c",-level,marker);
1240 	printf("Global header\n");
1241 	printf("%*crevision %d\n",-level-4,marker,attr[0]);
1242 	flags = get2l(attr,2);
1243 	printf("%*cflags    0x%x\n",-level-4,marker,flags);
1244 	if (flags & 1)
1245 		printf("%*c    owner is defaulted\n",-level-4,marker);
1246 	if (flags & 2)
1247 		printf("%*c    group is defaulted\n",-level-4,marker);
1248 	if (flags & 4)
1249 		printf("%*c    DACL present\n",-level-4,marker);
1250 	if (flags & 8)
1251 		printf("%*c    DACL is defaulted\n",-level-4,marker);
1252 	if (flags & 0x10)
1253 		printf("%*c    SACL present\n",-level-4,marker);
1254 	if (flags & 0x20)
1255 		printf("%*c    SACL is defaulted\n",-level-4,marker);
1256 	if (flags & 0x100)
1257 		printf("%*c    DACL inheritance is requested\n",-level-4,marker);
1258 	if (flags & 0x200)
1259 		printf("%*c    SACL inheritance is requested\n",-level-4,marker);
1260 	if (flags & 0x400)
1261 		printf("%*c    DACL was inherited automatically\n",-level-4,marker);
1262 	if (flags & 0x800)
1263 		printf("%*c    SACL was inherited automatically\n",-level-4,marker);
1264 	if (flags & 0x1000)
1265 		printf("%*c    DACL cannot be modified by inheritable ACEs\n",-level-4,marker);
1266 	if (flags & 0x2000)
1267 		printf("%*c    SACL cannot be modified by inheritable ACEs\n",-level-4,marker);
1268 	if (flags & 0x8000)
1269 		printf("%*c    self relative descriptor\n",-level-4,marker);
1270 	if (flags & 0x43eb)
1271 		printf("%*c    unknown flags 0x%x present\n",-level-4,marker,
1272 				flags & 0x43eb);
1273 	printf("%*cOff USID 0x%x\n",-level-4,marker,(int)get4l(attr,4));
1274 	printf("%*cOff GSID 0x%x\n",-level-4,marker,(int)get4l(attr,8));
1275 	printf("%*cOff SACL 0x%x\n",-level-4,marker,(int)get4l(attr,12));
1276 	printf("%*cOff DACL 0x%x\n",-level-4,marker,(int)get4l(attr,16));
1277 }
1278 
showace(const char * attr,int off,int isdir,int level)1279 static void showace(const char *attr, int off, int isdir, int level)
1280 {
1281 	int flags;
1282 	u32 rights;
1283 	char marker;
1284 
1285 	if (cmd == CMD_BACKUP)
1286 		marker = '#';
1287 	else
1288 		marker = ' ';
1289 	printf("%*ctype     %d\n",-level,marker,attr[off]);
1290 	switch (attr[off]) {
1291 	case 0 :
1292 		printf("%*cAccess allowed\n",-level-4,marker);
1293 		break;
1294 	case 1 :
1295 		printf("%*cAccess denied\n",-level-4,marker);
1296 		break;
1297 	case 2 :
1298 		printf("%*cSystem audit\n",-level-4,marker);
1299 		break;
1300 	default :
1301 		printf("%*cunknown\n",-level-4,marker);
1302 		break;
1303 	}
1304 	flags = attr[off+1] & 255;
1305 	printf("%*cflags    0x%x\n",-level,marker,flags);
1306 	if (flags & 1)
1307 		printf("%*cObject inherits ACE\n",-level-4,marker);
1308 	if (flags & 2)
1309 		printf("%*cContainer inherits ACE\n",-level-4,marker);
1310 	if (flags & 4)
1311 		printf("%*cDon\'t propagate inherits ACE\n",-level-4,marker);
1312 	if (flags & 8)
1313 		printf("%*cInherit only ACE\n",-level-4,marker);
1314 	if (flags & 0x10)
1315 		printf("%*cACE was inherited\n",-level-4,marker);
1316 	if (flags & 0x40)
1317 		printf("%*cAudit on success\n",-level-4,marker);
1318 	if (flags & 0x80)
1319 		printf("%*cAudit on failure\n",-level-4,marker);
1320 
1321 	printf("%*cSize     0x%x\n",-level,marker,get2l(attr,off+2));
1322 
1323 	rights = get4l(attr,off+4);
1324 	printf("%*cAcc rgts 0x%lx\n",-level,marker,(long)rights);
1325 	printf("%*cObj specific acc rgts 0x%lx\n",-level-4,marker,(long)rights & 65535);
1326 	if (isdir) /* a directory */ {
1327 		if (rights & 0x01)
1328 			printf("%*cList directory\n",-level-8,marker);
1329 		if (rights & 0x02)
1330 			printf("%*cAdd file\n",-level-8,marker);
1331 		if (rights & 0x04)
1332 			printf("%*cAdd subdirectory\n",-level-8,marker);
1333 		if (rights & 0x08)
1334 			printf("%*cRead EA\n",-level-8,marker);
1335 		if (rights & 0x10)
1336 			printf("%*cWrite EA\n",-level-8,marker);
1337 		if (rights & 0x20)
1338 			printf("%*cTraverse\n",-level-8,marker);
1339 		if (rights & 0x40)
1340 			printf("%*cDelete child\n",-level-8,marker);
1341 		if (rights & 0x80)
1342 			printf("%*cRead attributes\n",-level-8,marker);
1343 		if (rights & 0x100)
1344 			printf("%*cWrite attributes\n",-level-8,marker);
1345 	}
1346 	else {
1347 	     /* see FILE_READ_DATA etc in winnt.h */
1348 		if (rights & 0x01)
1349 			printf("%*cRead data\n",-level-8,marker);
1350 		if (rights & 0x02)
1351 			printf("%*cWrite data\n",-level-8,marker);
1352 		if (rights & 0x04)
1353 			printf("%*cAppend data\n",-level-8,marker);
1354 		if (rights & 0x08)
1355 			printf("%*cRead EA\n",-level-8,marker);
1356 		if (rights & 0x10)
1357 			printf("%*cWrite EA\n",-level-8,marker);
1358 		if (rights & 0x20)
1359 			printf("%*cExecute\n",-level-8,marker);
1360 		if (rights & 0x80)
1361 			printf("%*cRead attributes\n",-level-8,marker);
1362 		if (rights & 0x100)
1363 			printf("%*cWrite attributes\n",-level-8,marker);
1364 	}
1365 	printf("%*cstandard acc rgts 0x%lx\n",-level-4,marker,(long)(rights >> 16) & 127);
1366 	if (rights & 0x10000)
1367 		printf("%*cDelete\n",-level-8,marker);
1368 	if (rights & 0x20000)
1369 		printf("%*cRead control\n",-level-8,marker);
1370 	if (rights & 0x40000)
1371 		printf("%*cWrite DAC\n",-level-8,marker);
1372 	if (rights & 0x80000)
1373 		printf("%*cWrite owner\n",-level-8,marker);
1374 	if (rights & 0x100000)
1375 		printf("%*cSynchronize\n",-level-8,marker);
1376 	if (rights & 0x800000)
1377 		printf("%*cCan access security ACL\n",-level-4,marker);
1378 	if (rights & 0x10000000)
1379 		printf("%*cGeneric all\n",-level-4,marker);
1380 	if (rights & 0x20000000)
1381 		printf("%*cGeneric execute\n",-level-4,marker);
1382 	if (rights & 0x40000000)
1383 		printf("%*cGeneric write\n",-level-4,marker);
1384 	if (rights & 0x80000000)
1385 		printf("%*cGeneric read\n",-level-4,marker);
1386 
1387 	printf("%*cSID at 0x%x\n",-level,marker,off+8);
1388 	showsid(attr,off+8,"",level+4);
1389 	printf("%*cSummary :",-level,marker);
1390 	if (attr[off] == 0)
1391 		printf(" grant");
1392 	if (attr[off] == 1)
1393 		printf(" deny");
1394 	if (rights & le32_to_cpu(FILE_GREAD | FILE_GWRITE | FILE_GEXEC)) {
1395 		printf(" ");
1396 		if (rights & le32_to_cpu(FILE_GREAD))
1397 			printf("r");
1398 		if (rights & le32_to_cpu(FILE_GWRITE))
1399 			printf("w");
1400 		if (rights & le32_to_cpu(FILE_GEXEC))
1401 			printf("x");
1402 	} else
1403 		printf(" none");
1404 	if (flags & 11)
1405 		printf(" inherited");
1406 	if (!(flags & 8)) {
1407 		int sz;
1408 
1409 		printf(" applied");
1410 		sz = attr[off+9]*4 + 8;
1411 		if (!memcmp(&attr[off+8],&attr[get4l(attr,4)],sz))
1412 			printf(" to owner");
1413 		if (!memcmp(&attr[off+8],&attr[get4l(attr,8)],sz))
1414 			printf(" to group");
1415 	}
1416 	printf("\n");
1417 
1418 }
1419 
showacl(const char * attr,int off,int isdir,int level)1420 static void showacl(const char *attr, int off, int isdir, int level)
1421 {
1422 	int i;
1423 	int cnt;
1424 	int size;
1425 	int x;
1426 	char marker;
1427 
1428 	if (cmd == CMD_BACKUP)
1429 		marker = '#';
1430 	else
1431 		marker = ' ';
1432 	size = get2l(attr,off+2);
1433 	printf("%*crevision %d\n",-level,marker,attr[off]);
1434 	printf("%*cACL size %d\n",-level,marker,size);
1435 	cnt = get2l(attr,off+4);
1436 	printf("%*cACE cnt  %d\n",-level,marker,cnt);
1437 	x = 8;
1438 	for (i=0; (i<cnt) && (x < size); i++) {
1439 		printf("%*cACE %d at 0x%x\n",-level,marker,i + 1,off+x);
1440 		showace(attr,off + x,isdir,level+4);
1441 		x += get2l(attr,off + x + 2);
1442 	}
1443 }
1444 
showdacl(const char * attr,int isdir,int level)1445 static void showdacl(const char *attr, int isdir, int level)
1446 {
1447 	int off;
1448 	char marker;
1449 
1450 	if (cmd == CMD_BACKUP)
1451 		marker = '#';
1452 	else
1453 		marker = ' ';
1454 	off = get4l(attr,16);
1455 	if (off) {
1456 		if (level)
1457 			printf("%*c",-level,marker);
1458 		printf("DACL\n");
1459 		showacl(attr,off,isdir,level+4);
1460 	} else {
1461 		if (level)
1462 			printf("%*c",-level,marker);
1463 		printf("No DACL\n");
1464 	}
1465 }
1466 
showsacl(const char * attr,int isdir,int level)1467 static void showsacl(const char *attr, int isdir, int level)
1468 {
1469 	int off;
1470 	char marker;
1471 
1472 	if (cmd == CMD_BACKUP)
1473 		marker = '#';
1474 	else
1475 		marker = ' ';
1476 	off = get4l(attr,12);
1477 	if (off) {
1478 		if (level)
1479 			printf("%*c",-level,marker);
1480 		printf("SACL\n");
1481 		showacl(attr,off,isdir,level+4);
1482 	}
1483 	else {
1484 		if (level)
1485 			printf("%*c",-level,marker);
1486 		printf("No SACL\n");
1487 	}
1488 }
1489 
showall(const char * attr,int level)1490 static void showall(const char *attr, int level)
1491 {
1492 	BOOL isdir;
1493 
1494 	isdir = guess_dir(attr);
1495 	showheader(attr,level);
1496 	showusid(attr,level);
1497 	showgsid(attr,level);
1498 	showdacl(attr,isdir,level);
1499 	showsacl(attr,isdir,level);
1500 }
1501 
1502 #if POSIXACLS
1503 /*
1504  *		Display a Posix descriptor
1505  */
1506 
showposix(const struct POSIX_SECURITY * pxdesc)1507 static void showposix(const struct POSIX_SECURITY *pxdesc)
1508 {
1509 	char txperm[4];
1510 	const char *txtag;
1511 	const char *txtype;
1512 	const struct POSIX_ACL *acl;
1513 	const struct POSIX_ACE *pxace;
1514 	int acccnt;
1515 	int defcnt;
1516 	int firstdef;
1517 	int perms;
1518 	u16 tag;
1519 	s32 id;
1520 	int k,l;
1521 
1522 	if (pxdesc) {
1523 		acccnt = pxdesc->acccnt;
1524 		defcnt = pxdesc->defcnt;
1525 		firstdef = pxdesc->firstdef;
1526 		acl = &pxdesc->acl;
1527 		printf("Posix descriptor :\n");
1528 		printf("    acccnt %d\n",acccnt);
1529 		printf("    defcnt %d\n",defcnt);
1530 		printf("    firstdef %d\n",firstdef);
1531 		printf("    mode : 0%03o\n",(int)pxdesc->mode);
1532 		printf("    tagsset : 0x%02x\n",(int)pxdesc->tagsset);
1533 		printf("Posix ACL :\n");
1534 		printf("    version %d\n",(int)acl->version);
1535 		printf("    flags 0x%02x\n",(int)acl->flags);
1536 		for (k=0; k<(acccnt + defcnt); k++) {
1537 			if (k < acccnt)
1538 				l = k;
1539 			else
1540 				l = firstdef + k - acccnt;
1541 			pxace = &acl->ace[l];
1542 			tag = pxace->tag;
1543 			perms = pxace->perms;
1544 			if (tag == POSIX_ACL_SPECIAL) {
1545 				txperm[0] = (perms & S_ISVTX ? 's' : '-');
1546 				txperm[1] = (perms & S_ISUID ? 'u' : '-');
1547 				txperm[2] = (perms & S_ISGID ? 'g' : '-');
1548 			} else {
1549 				txperm[0] = (perms & 4 ? 'r' : '-');
1550 				txperm[1] = (perms & 2 ? 'w' : '-');
1551 				txperm[2] = (perms & 1 ? 'x' : '-');
1552 			}
1553 			txperm[3] = 0;
1554 			if (k >= acccnt)
1555 				txtype = "default";
1556 			else
1557 				txtype = "access ";
1558 			switch (tag) {
1559 			case POSIX_ACL_USER :
1560 				txtag = "USER ";
1561 				break;
1562 			case POSIX_ACL_USER_OBJ :
1563 				txtag = "USR-O";
1564 				break;
1565 			case POSIX_ACL_GROUP :
1566 				txtag = "GROUP";
1567 				break;
1568 			case POSIX_ACL_GROUP_OBJ :
1569 				txtag = "GRP-O";
1570 				break;
1571 			case POSIX_ACL_MASK :
1572 				txtag = "MASK ";
1573 				break;
1574 			case POSIX_ACL_OTHER :
1575 				txtag = "OTHER";
1576 				break;
1577 			case POSIX_ACL_SPECIAL :
1578 				txtag = "SPECL";
1579 				break;
1580 			default :
1581 				txtag = "UNKWN";
1582 				break;
1583 			}
1584 			id = pxace->id;
1585 			printf("ace %d : %s %s %4ld perms 0%03o %s\n",
1586 				l,txtype,txtag,(long)id,
1587 				perms,txperm);
1588 		}
1589 	} else
1590 		printf("** NULL ACL\n");
1591 }
1592 
1593 #endif /* POSIXACLS */
1594 
1595 /*
1596  *		Relay to get uid as defined during mounting
1597  */
1598 
relay_find_user(const struct MAPPING * mapping,const SID * usid)1599 static uid_t relay_find_user(const struct MAPPING *mapping __attribute__((unused)),
1600 			const SID *usid)
1601 {
1602 	int uid;
1603 
1604 	uid = ntfs_get_user(ntfs_context, usid);
1605 	return (uid < 0 ? 0 : uid);
1606 }
1607 
1608 /*
1609  *		Relay to get gid as defined during mounting
1610  */
1611 
relay_find_group(const struct MAPPING * mapping,const SID * gsid)1612 static gid_t relay_find_group(const struct MAPPING *mapping __attribute__((unused)),
1613 			const SID *gsid)
1614 {
1615 	int gid;
1616 
1617 	gid = ntfs_get_group(ntfs_context, gsid);
1618 	return (gid < 0 ? 0 : gid);
1619 }
1620 
1621 #if POSIXACLS
1622 
linux_permissions_posix(const char * attr,BOOL isdir)1623 static struct POSIX_SECURITY *linux_permissions_posix(const char *attr, BOOL isdir)
1624 {
1625 	const SECURITY_DESCRIPTOR_RELATIVE *phead;
1626 #if OWNERFROMACL
1627 	const SID *osid;
1628 #endif /* OWNERFROMACL */
1629 	const SID *usid;
1630 	const SID *gsid;
1631 	struct POSIX_SECURITY *posix_desc;
1632 
1633 	phead = (const SECURITY_DESCRIPTOR_RELATIVE*)attr;
1634 	gsid = (const SID*)&attr[le32_to_cpu(phead->group)];
1635 #if OWNERFROMACL
1636 	osid = (const SID*)&attr[le32_to_cpu(phead->owner)];
1637 	usid = ntfs_acl_owner((const char*)attr);
1638 #ifdef SELFTESTS
1639 	if ((cmd != CMD_TEST) && !ntfs_same_sid(usid,osid))
1640 		printf("== Linux owner is different from Windows owner\n");
1641 #else /* SELFTESTS */
1642 	if (!ntfs_same_sid(usid,osid))
1643 		printf("== Linux owner is different from Windows owner\n");
1644 #endif /* SELFTESTS */
1645 #else /* OWNERFROMACL */
1646 	usid = (const SID*)&attr[le32_to_cpu(phead->owner)];
1647 #endif /* OWNERFROMACL */
1648 	if (mappingtype == MAPEXTERN)
1649 		posix_desc = ntfs_build_permissions_posix(
1650 				ntfs_context->security.mapping,
1651 				(const char*)attr, usid, gsid, isdir);
1652 	else
1653 		posix_desc = ntfs_build_permissions_posix(context.mapping,
1654 				(const char*)attr, usid, gsid, isdir);
1655 	if (!posix_desc) {
1656 		printf("** Failed to build a Posix descriptor\n");
1657 		errors++;
1658 	}
1659 	return (posix_desc);
1660 }
1661 
1662 #endif /* POSIXACLS */
1663 
linux_permissions(const char * attr,BOOL isdir)1664 static int linux_permissions(const char *attr, BOOL isdir)
1665 {
1666 	const SECURITY_DESCRIPTOR_RELATIVE *phead;
1667 #if OWNERFROMACL
1668 	const SID *osid;
1669 #endif /* OWNERFROMACL */
1670 	const SID *usid;
1671 	const SID *gsid;
1672 	int perm;
1673 
1674 	phead = (const SECURITY_DESCRIPTOR_RELATIVE*)attr;
1675 	gsid = (const SID*)&attr[le32_to_cpu(phead->group)];
1676 #if OWNERFROMACL
1677 	osid = (const SID*)&attr[le32_to_cpu(phead->owner)];
1678 	usid = ntfs_acl_owner((const char*)attr);
1679 #ifdef SELFTESTS
1680 	if ((cmd != CMD_TEST) && !ntfs_same_sid(usid,osid))
1681 		printf("== Linux owner is different from Windows owner\n");
1682 #else /* SELFTESTS */
1683 	if (!ntfs_same_sid(usid,osid))
1684 		printf("== Linux owner is different from Windows owner\n");
1685 #endif /* SELFTESTS */
1686 #else /* OWNERFROMACL */
1687 	usid = (const SID*)&attr[le32_to_cpu(phead->owner)];
1688 #endif /* OWNERFROMACL */
1689 	perm = ntfs_build_permissions((const char*)attr, usid, gsid, isdir);
1690 	if (perm < 0) {
1691 		printf("** Failed to build permissions\n");
1692 		errors++;
1693 	}
1694 	return (perm);
1695 }
1696 
linux_owner(const char * attr)1697 static uid_t linux_owner(const char *attr)
1698 {
1699 	const SID *usid;
1700 	uid_t uid;
1701 
1702 #if OWNERFROMACL
1703 	usid = ntfs_acl_owner((const char*)attr);
1704 #else /* OWNERFROMACL */
1705 	const SECURITY_DESCRIPTOR_RELATIVE *phead;
1706 
1707 	phead = (const SECURITY_DESCRIPTOR_RELATIVE*)attr;
1708 	usid = (const SID*)&attr[le32_to_cpu(phead->owner)];
1709 #endif /* OWNERFROMACL */
1710 #ifdef HAVE_WINDOWS_H
1711 	uid = ntfs_find_user(context.mapping[MAPUSERS],usid);
1712 #else /* defined(HAVE_WINDOWS_H) */
1713 	if (mappingtype == MAPEXTERN)
1714 		uid = relay_find_user(context.mapping[MAPUSERS],usid);
1715 	else
1716 		uid = ntfs_find_user(context.mapping[MAPUSERS],usid);
1717 #endif /* defined(HAVE_WINDOWS_H) */
1718 	return (uid);
1719 }
1720 
linux_group(const char * attr)1721 static gid_t linux_group(const char *attr)
1722 {
1723 	const SECURITY_DESCRIPTOR_RELATIVE *phead;
1724 	const SID *gsid;
1725 	gid_t gid;
1726 
1727 	phead = (const SECURITY_DESCRIPTOR_RELATIVE*)attr;
1728 	gsid = (const SID*)&attr[le32_to_cpu(phead->group)];
1729 #ifdef HAVE_WINDOWS_H
1730 	gid = ntfs_find_group(context.mapping[MAPGROUPS],gsid);
1731 #else /* defined(HAVE_WINDOWS_H) */
1732 	if (mappingtype == MAPEXTERN)
1733 		gid = relay_find_group(context.mapping[MAPGROUPS],gsid);
1734 	else
1735 		gid = ntfs_find_group(context.mapping[MAPGROUPS],gsid);
1736 #endif /* defined(HAVE_WINDOWS_H) */
1737 	return (gid);
1738 }
1739 
newblock(s32 key)1740 static void newblock(s32 key)
1741 {
1742 	struct SECURITY_DATA *psecurdata;
1743 	int i;
1744 
1745 	if ((key > 0) && (key < MAXSECURID) && !securdata[key >> SECBLKSZ]) {
1746 		securdata[key >> SECBLKSZ] =
1747 			(struct SECURITY_DATA*)malloc((1 << SECBLKSZ)*sizeof(struct SECURITY_DATA));
1748 		if (securdata[key >> SECBLKSZ])
1749 			for (i=0; i<(1 << SECBLKSZ); i++) {
1750 				psecurdata = &securdata[key >> SECBLKSZ][i];
1751 				psecurdata->filecount = 0;
1752 				psecurdata->mode = 0;
1753 				psecurdata->flags = 0;
1754 				psecurdata->attr = (char*)NULL;
1755 			}
1756 	}
1757 }
1758 
freeblocks(void)1759 static void freeblocks(void)
1760 {
1761 	int i,j;
1762 	struct SECURITY_DATA *psecurdata;
1763 
1764 	for (i=0; i<(MAXSECURID + (1 << SECBLKSZ) - 1)/(1 << SECBLKSZ); i++)
1765 		if (securdata[i]) {
1766 			for (j=0; j<(1 << SECBLKSZ); j++) {
1767 				psecurdata = &securdata[i][j];
1768 				if (psecurdata->attr)
1769 					free(psecurdata->attr);
1770 			}
1771 			free(securdata[i]);
1772 		}
1773 }
1774 
1775 /*
1776  *		Basic read from a user mapping file (Win32)
1777  */
1778 
basicread(void * fileid,char * buf,size_t size,off_t pos)1779 static int basicread(void *fileid, char *buf, size_t size,
1780 		off_t pos __attribute__((unused)))
1781 {
1782 	return (read(*(int*)fileid, buf, size));
1783 }
1784 
1785 #ifdef SELFTESTS
1786 
1787 /*
1788  *		Read a dummy mapping file for tests
1789  */
1790 
dummyread(void * fileid,char * buf,size_t size,off_t pos)1791 static int dummyread(void *fileid  __attribute__((unused)),
1792 		char *buf, size_t size, off_t pos)
1793 {
1794 	size_t sz;
1795 
1796 	if (pos >= (off_t)(sizeof(dummymapping) - 1))
1797 		sz = 0;
1798 	else
1799 		if ((size + pos) >= (sizeof(dummymapping) - 1))
1800 			sz = sizeof(dummymapping) - 1 - pos;
1801 		else
1802 			sz = size;
1803 	if (sz > 0)
1804 		memcpy(buf,&dummymapping[pos],sz);
1805 	return (sz);
1806 }
1807 
1808 #endif /* SELFTESTS */
1809 
1810 /*
1811  *		Apply default single user mapping
1812  *	returns zero if successful
1813  */
1814 
do_default_mapping(struct MAPPING * mapping[],const SID * usid)1815 static int do_default_mapping(struct MAPPING *mapping[],
1816 			 const SID *usid)
1817 {
1818 	struct MAPPING *usermapping;
1819 	struct MAPPING *groupmapping;
1820 	SID *sid;
1821 	int sidsz;
1822 	int res;
1823 
1824 	res = -1;
1825 	sidsz = ntfs_sid_size(usid);
1826 	sid = (SID*)ntfs_malloc(sidsz);
1827 	if (sid) {
1828 		memcpy(sid,usid,sidsz);
1829 		usermapping = (struct MAPPING*)ntfs_malloc(sizeof(struct MAPPING));
1830 		if (usermapping) {
1831 			groupmapping = (struct MAPPING*)ntfs_malloc(sizeof(struct MAPPING));
1832 			if (groupmapping) {
1833 				usermapping->sid = sid;
1834 				usermapping->xid = 0;
1835 				usermapping->next = (struct MAPPING*)NULL;
1836 				groupmapping->sid = sid;
1837 				groupmapping->xid = 0;
1838 				groupmapping->next = (struct MAPPING*)NULL;
1839 				mapping[MAPUSERS] = usermapping;
1840 				mapping[MAPGROUPS] = groupmapping;
1841 				res = 0;
1842 			}
1843 		}
1844 	}
1845 	return (res);
1846 }
1847 
1848 /*
1849  *		Build the user mapping
1850  *	- according to a mapping file if defined (or default present),
1851  *	- or try default single user mapping if possible
1852  *
1853  *	The mapping is specific to a mounted device
1854  *	No locking done, mounting assumed non multithreaded
1855  *
1856  *	returns zero if mapping is successful
1857  *	(failure should not be interpreted as an error)
1858  */
1859 
local_build_mapping(struct MAPPING * mapping[],const char * usermap_path)1860 static int local_build_mapping(struct MAPPING *mapping[], const char *usermap_path)
1861 {
1862 #ifdef HAVE_WINDOWS_H
1863 	char mapfile[sizeof(MAPDIR) + sizeof(MAPFILE) + 6];
1864 	char currpath[MAXFILENAME];
1865 #else /* HAVE_WINDOWS_H */
1866 	char *mapfile;
1867 	char *p;
1868 #endif /* HAVE_WINDOWS_H */
1869 	int fd;
1870 	struct MAPLIST *item;
1871 	struct MAPLIST *firstitem = (struct MAPLIST*)NULL;
1872 	struct MAPPING *usermapping;
1873 	struct MAPPING *groupmapping;
1874 	static struct {
1875 		u8 revision;
1876 		u8 levels;
1877 		be16 highbase;
1878 		be32 lowbase;
1879 		le32 level1;
1880 		le32 level2;
1881 		le32 level3;
1882 		le32 level4;
1883 		le32 level5;
1884 	} defmap = {
1885 		1, 5, const_cpu_to_be16(0), const_cpu_to_be32(5),
1886 		const_cpu_to_le32(21),
1887 		const_cpu_to_le32(DEFSECAUTH1), const_cpu_to_le32(DEFSECAUTH2),
1888 		const_cpu_to_le32(DEFSECAUTH3), const_cpu_to_le32(DEFSECBASE)
1889 	} ;
1890 
1891 	/* be sure not to map anything until done */
1892 	mapping[MAPUSERS] = (struct MAPPING*)NULL;
1893 	mapping[MAPGROUPS] = (struct MAPPING*)NULL;
1894 
1895 	if (usermap_path) {
1896 #ifdef HAVE_WINDOWS_H
1897 /* TODO : check whether the device can store acls */
1898 		strcpy(mapfile,"x:\\" MAPDIR "\\" MAPFILE);
1899 		if (((const le16*)usermap_path)[1] == ':')
1900   			mapfile[0] = usermap_path[0];
1901 		else {
1902 			getcwd(currpath,MAXFILENAME);
1903 			mapfile[0] = currpath[0];
1904 		}
1905 		fd = open(mapfile,O_RDONLY);
1906 #else /* HAVE_WINDOWS_H */
1907 		fd = 0;
1908 		mapfile = (char*)malloc(MAXFILENAME);
1909 		if (mapfile) {
1910 			/* build a full path to locate the mapping file */
1911 /*
1912 			if ((usermap_path[0] != '/')
1913 			   && getcwd(mapfile,MAXFILENAME)) {
1914 				strcat(mapfile,"/");
1915 				strcat(mapfile,usermap_path);
1916 			} else
1917 				strcpy(mapfile,usermap_path);
1918 */
1919 			p = ntfs_realpath(usermap_path, mapfile);
1920 			if (p)
1921 				p = strrchr(mapfile,'/');
1922 			if (p)
1923 				do {
1924 					strcpy(p,"/" MAPDIR "/" MAPFILE);
1925 					fd = open(mapfile,O_RDONLY);
1926 					if (fd <= 0) {
1927 						*p = 0;
1928 						p = strrchr(mapfile,'/');
1929 						if (p == mapfile)
1930 							p = (char*)NULL;
1931 					}
1932 				} while ((fd <= 0) && p);
1933 			free(mapfile);
1934 			if (!p) {
1935 				printf("** Could not find the user mapping file\n");
1936 				if (usermap_path[0] != '/')
1937 					printf("   Retry with full path of file\n");
1938 				errors++;
1939 			}
1940 		}
1941 #endif /* HAVE_WINDOWS_H */
1942 		if (fd > 0) {
1943 			firstitem = ntfs_read_mapping(basicread, (void*)&fd);
1944 			close(fd);
1945 		}
1946 	} else {
1947 #ifdef SELFTESTS
1948 		firstitem = ntfs_read_mapping(dummyread, (void*)NULL);
1949 #endif /* SELFTESTS */
1950 	}
1951 
1952 	if (firstitem) {
1953 		usermapping = ntfs_do_user_mapping(firstitem);
1954 		groupmapping = ntfs_do_group_mapping(firstitem);
1955 		if (usermapping && groupmapping) {
1956 			mapping[MAPUSERS] = usermapping;
1957 			mapping[MAPGROUPS] = groupmapping;
1958 		} else
1959 			ntfs_log_error("There were no valid user or no valid group\n");
1960 		/* now we can free the memory copy of input text */
1961 		/* and rely on internal representation */
1962 		while (firstitem) {
1963 			item = firstitem->next;
1964 			free(firstitem);
1965 			firstitem = item;
1966 		}
1967 	} else {
1968 		do_default_mapping(mapping,(const SID*)&defmap);
1969 	}
1970 	if (mapping[MAPUSERS])
1971 		mappingtype = MAPLOCAL;
1972 	return (!mapping[MAPUSERS]);
1973 }
1974 
1975 /*
1976  *		Get an hexadecimal number (source with MSB first)
1977  */
1978 
getmsbhex(const char * text)1979 static u32 getmsbhex(const char *text)
1980 {
1981 	u32 v;
1982 	int b;
1983 	BOOL ok;
1984 
1985 	v = 0;
1986 	ok = TRUE;
1987 	do {
1988 		b = *text++;
1989 		if ((b >= '0') && (b <= '9'))
1990 			v = (v << 4) + b - '0';
1991 		else
1992 			if ((b >= 'a') && (b <= 'f'))
1993 				v = (v << 4) + b - 'a' + 10;
1994 			else
1995 				if ((b >= 'A') && (b <= 'F'))
1996 					v = (v << 4) + b - 'A' + 10;
1997 				else ok = FALSE;
1998 	} while (ok);
1999 	return (v);
2000 }
2001 
2002 
2003 /*
2004  *		Get an hexadecimal number (source with LSB first)
2005  *	An odd number of digits might yield a strange result
2006  */
2007 
getlsbhex(const char * text)2008 static u32 getlsbhex(const char *text)
2009 {
2010 	u32 v;
2011 	int b;
2012 	BOOL ok;
2013 	int pos;
2014 
2015 	v = 0;
2016 	ok = TRUE;
2017 	pos = 0;
2018 	do {
2019 		b = *text++;
2020 		if ((b >= '0') && (b <= '9'))
2021 			v |= (u32)(b - '0') << (pos ^ 4);
2022 		else
2023 			if ((b >= 'a') && (b <= 'f'))
2024 				v |= (u32)(b - 'a' + 10) << (pos ^ 4);
2025 			else
2026 				if ((b >= 'A') && (b <= 'F'))
2027 					v |= (u32)(b - 'A' + 10) << (pos ^ 4);
2028 				else ok = FALSE;
2029 		pos += 4;
2030 	} while (ok);
2031 	return (v);
2032 }
2033 
2034 
2035 /*
2036  *		Check whether a line looks like an hex dump
2037  */
2038 
ishexdump(const char * line,int first,int lth)2039 static BOOL ishexdump(const char *line, int first, int lth)
2040 {
2041 	BOOL ok;
2042 	int i;
2043 	int b;
2044 
2045 	ok = (first >= 0) && (lth >= (first + 16));
2046 	for (i=0; ((first+i)<lth) && ok; i++) {
2047 		b = line[first + i];
2048 		if ((i == 6)
2049 		    || (i == 7)
2050 		    || (i == 16)
2051 		    || (i == 25)
2052 		    || (i == 34)
2053 		    || (i == 43))
2054 			ok = (b == ' ') || (b == '\n');
2055 		else
2056 			ok = ((b >= '0') && (b <= '9'))
2057 			    || ((b >= 'a') && (b <= 'f'))
2058 			    || ((b >= 'A') && (b <= 'F'));
2059 	}
2060 	return (ok);
2061 }
2062 
2063 
2064 /*
2065  *		Display security descriptors from a file
2066  *	This is typically to convert a verbose output to a very verbose one
2067  */
2068 
showhex(FILE * fd)2069 static void showhex(FILE *fd)
2070 {
2071 	static char attr[MAXATTRSZ];
2072 	char line[MAXLINE+1];
2073 #if POSIXACLS
2074 	struct POSIX_SECURITY *pxdesc;
2075 #endif /* POSIXACLS */
2076 	int lth;
2077 	int first;
2078 	unsigned int pos;
2079 	u32 v;
2080 	int c;
2081 	int isdir;
2082 	int mode;
2083 	unsigned int off;
2084 	int i;
2085 	le32 *pattr;
2086 	BOOL acceptable;
2087 	BOOL isdump;
2088 	BOOL done;
2089 
2090 	pos = 0;
2091 	off = 0;
2092 	done = FALSE;
2093 	do {
2094 			/* input a (partial) line without displaying */
2095 		lth = 0;
2096 		first = -1;
2097 		do {
2098 			c = getc(fd);
2099 			if ((c != ' ') && (first < 0))
2100 				first = lth;
2101 			if (c == EOF)
2102 				done = TRUE;
2103 			else
2104 				if (c != '\r')
2105 					line[lth++] = c;
2106 		} while (!done && (c != '\n') && (lth < MAXLINE));
2107 			/* check whether this looks like an hexadecimal dump */
2108 		isdump = ishexdump(line, first, lth);
2109 		if (isdump) off = getmsbhex(&line[first]);
2110 			/* line is not an hexadecimal dump */
2111 			/* display what we have in store if acceptable */
2112 		acceptable = ((!isdump || !off)
2113 				&& (pos >= 20))
2114 				&& (pos > get4l(attr,4))
2115 				&& (pos > get4l(attr,8))
2116 				&& (pos > get4l(attr,12))
2117 				&& (pos > get4l(attr,16))
2118 				&& (pos >= ntfs_attr_size(attr));
2119 		if (acceptable) {
2120 			printf("	Computed hash : 0x%08lx\n",
2121 				    (unsigned long)hash((le32*)attr,
2122 				    ntfs_attr_size(attr)));
2123 			isdir = guess_dir(attr);
2124 			printf("    Estimated type : %s\n",
2125 					(isdir ? "directory" : "file"));
2126 			if (!ntfs_valid_descr((char*)attr,pos)) {
2127 				printf("**  Bad descriptor,"
2128 					" trying to display anyway\n");
2129 				errors++;
2130 			}
2131 			showheader(attr,4);
2132 			showusid(attr,4);
2133 			showgsid(attr,4);
2134 			showdacl(attr,isdir,4);
2135 			showsacl(attr,isdir,4);
2136 			showownership(attr);
2137 			mode = linux_permissions(attr,isdir);
2138 			printf("Interpreted Unix mode 0%03o\n",mode);
2139 #if POSIXACLS
2140 				/*
2141 				 * Posix display not possible when user
2142 				 * mapping is not available (option -h)
2143 				 */
2144 			if (mappingtype != MAPNONE) {
2145 				pxdesc = linux_permissions_posix(attr,isdir);
2146 				if (pxdesc) {
2147 					showposix(pxdesc);
2148 					free(pxdesc);
2149 				}
2150 			}
2151 #endif /* POSIXACLS */
2152 			pos = 0;
2153 		}
2154 		if (isdump && !off)
2155 			pos = off;
2156 			/* line looks like an hexadecimal dump */
2157 			/* decode it into attribute */
2158 		if (isdump && (off == pos)) {
2159 			for (i=first+8; i<lth; i+=9) {
2160 				pattr = (le32*)&attr[pos];
2161 				v = getlsbhex(&line[i]);
2162 				*pattr = cpu_to_le32(v);
2163 				pos += 4;
2164 			}
2165 		}
2166 			/* display (full) current line */
2167 		if (lth) printf("! ");
2168 		for (i=0; i<lth; i++) {
2169 			c = line[i];
2170 			putchar(c);
2171 		}
2172 		while (!done && (c != '\n')) {
2173 			c = getc(fd);
2174 			if (c == EOF)
2175 				done = TRUE;
2176 			else
2177 				putchar(c);
2178 		}
2179 	} while (!done);
2180 }
2181 
applyattr(const char * fullname,const char * attr,BOOL withattr,int attrib,s32 key)2182 static BOOL applyattr(const char *fullname, const char *attr,
2183 			BOOL withattr, int attrib, s32 key)
2184 {
2185 	struct SECURITY_DATA *psecurdata;
2186 	const char *curattr;
2187 	char *newattr;
2188 	int selection;
2189 	BOOL bad;
2190 	BOOL badattrib;
2191 	BOOL err;
2192 
2193 	err = FALSE;
2194 	psecurdata = (struct SECURITY_DATA*)NULL;
2195 	curattr = (const char*)NULL;
2196 	newattr = (char*)NULL;
2197 	if ((key > 0) && (key < MAXSECURID)) {
2198 		if (!securdata[key >> SECBLKSZ])
2199 			newblock(key);
2200 		if (securdata[key >> SECBLKSZ]) {
2201 			psecurdata = &securdata[key >> SECBLKSZ]
2202 					[key & ((1 << SECBLKSZ) - 1)];
2203 		}
2204 	}
2205 
2206 			/* If we have a usable attrib value. Try applying */
2207 	badattrib = FALSE;
2208 	if (opt_e && (attrib != INVALID_FILE_ATTRIBUTES)) {
2209 		badattrib = !ntfs_set_file_attributes(ntfs_context, fullname, attrib);
2210 		if (badattrib) {
2211 			printf("** Could not set Windows attrib of ");
2212 			printname(stdout,fullname);
2213 			printf(" to 0x%x\n", attrib);
2214 			printerror(stdout);
2215 			warnings++;
2216 		}
2217 	}
2218 
2219 	if (withattr) {
2220 		if (psecurdata) {
2221 			newattr = (char*)malloc(ntfs_attr_size(attr));
2222 			if (newattr) {
2223 				memcpy(newattr,attr,ntfs_attr_size(attr));
2224 				psecurdata->attr = newattr;
2225 			}
2226 		}
2227 		curattr = attr;
2228 	} else
2229 		/*
2230 		 * No explicit attr in backup, use attr defined
2231 		 * previously for the same id
2232 		 */
2233 		if (psecurdata)
2234 			curattr = psecurdata->attr;
2235 
2236 
2237 	if (curattr) {
2238 		selection = OWNER_SECURITY_INFORMATION
2239 			| GROUP_SECURITY_INFORMATION
2240 			| DACL_SECURITY_INFORMATION
2241 			| SACL_SECURITY_INFORMATION;
2242 		bad = !ntfs_set_file_security(ntfs_context,fullname,
2243 			selection, (const char*)curattr);
2244 		if (bad) {
2245 			printf("** Could not set the ACL of ");
2246 			printname(stdout,fullname);
2247 			printf("\n");
2248 			printerror(stdout);
2249 			err = TRUE;
2250 		} else
2251 			if (opt_v) {
2252 				if (opt_e && !badattrib)
2253 					printf("ACL and attrib have been applied to ");
2254 				else
2255 					printf("ACL has been applied to ");
2256 				printname(stdout,fullname);
2257 				printf("\n");
2258 
2259 			}
2260 	} else {
2261 		printf("** There was no valid ACL for ");
2262 		printname(stdout,fullname);
2263 		printf("\n");
2264 		err = TRUE;
2265 	}
2266 	return (!err);
2267 }
2268 
2269 /*
2270  *		Restore security descriptors from a file
2271  */
2272 
restore(FILE * fd)2273 static BOOL restore(FILE *fd)
2274 {
2275 	static char attr[MAXATTRSZ];
2276 	char line[MAXFILENAME+25];
2277 	char fullname[MAXFILENAME+25];
2278 	SECURITY_DESCRIPTOR_RELATIVE *phead;
2279 	int lth;
2280 	int first;
2281 	unsigned int pos;
2282 	int c;
2283 	int isdir;
2284 	int mode;
2285 	s32 key;
2286 	BOOL isdump;
2287 	unsigned int off;
2288 	u32 v;
2289 	u32 oldhash;
2290 	int i;
2291 	int count;
2292 	int attrib;
2293 	le32 *pattr;
2294 	BOOL withattr;
2295 	BOOL done;
2296 
2297 	pos = 0;
2298 	off = 0;
2299 	done = FALSE;
2300 	withattr = FALSE;
2301 	oldhash = 0;
2302 	key = 0;
2303 	errors = 0;
2304 	count = 0;
2305 	fullname[0] = 0;
2306 	attrib = INVALID_FILE_ATTRIBUTES;
2307 	do {
2308 			/* input a (partial) line without processing */
2309 		lth = 0;
2310 		first = -1;
2311 		do {
2312 			c = getc(fd);
2313 			if ((c != ' ') && (first < 0))
2314 				first = lth;
2315 			if (c == EOF)
2316 				done = TRUE;
2317 			else
2318 				if (c != '\r')
2319 					line[lth++] = c;
2320 		} while (!done && (c != '\n') && (lth < (MAXFILENAME + 24)));
2321 			/* check whether this looks like an hexadecimal dump */
2322 		isdump = ishexdump(line, first, lth);
2323 		if (isdump) off = getmsbhex(&line[first]);
2324 			/* line is not an hexadecimal dump */
2325 			/* apply what we have in store, only if valid */
2326 		if ((!isdump || !off) && pos && ntfs_valid_descr((char*)attr,pos)) {
2327 			withattr = TRUE;
2328 			if (opt_v >= 2) {
2329 				printf("	Computed hash : 0x%08lx\n",
2330 					    (unsigned long)hash((le32*)attr,
2331 					    ntfs_attr_size(attr)));
2332 				isdir = guess_dir(attr);
2333 				printf("    Estimated type : %s\n",(isdir ? "directory" : "file"));
2334 				showheader(attr,4);
2335 				showusid(attr,4);
2336 				showgsid(attr,4);
2337 				showdacl(attr,isdir,4);
2338 				showsacl(attr,isdir,4);
2339 				mode = linux_permissions(attr,isdir);
2340 				showownership(attr);
2341 				printf("Interpreted Unix mode 0%03o\n",mode);
2342 			}
2343 			pos = 0;
2344 		}
2345 		if (isdump && !off)
2346 			pos = off;
2347 			/* line looks like an hexadecimal dump */
2348 			/* decode it into attribute */
2349 		if (isdump && (off == pos)) {
2350 			for (i=first+8; i<lth; i+=9) {
2351 				pattr = (le32*)&attr[pos];
2352 				v = getlsbhex(&line[i]);
2353 				*pattr = cpu_to_le32(v);
2354 				pos += 4;
2355 			}
2356 		}
2357 			/* display (full) current line unless dump or verbose */
2358 		if (!isdump || opt_v) {
2359 			if(lth) printf("! ");
2360 			for (i=0; i<lth; i++) {
2361 				c = line[i];
2362 				putchar(c);
2363 			}
2364 		}
2365 		while (!done && (c != '\n')) {
2366 			c = getc(fd);
2367 			if (c == EOF)
2368 				done = TRUE;
2369 			else
2370 				if (!isdump || opt_v)
2371 					putchar(c);
2372 		}
2373 
2374 		line[lth] = 0;
2375 		while ((lth > 0)
2376 		    && ((line[lth-1] == '\n') || (line[lth-1] == '\r')))
2377 			line[--lth] = 0;
2378 		if (!strncmp(line,"Computed hash : 0x",18))
2379 			oldhash = getmsbhex(&line[18]);
2380 		if (!strncmp(line,"Security key : 0x",17))
2381 			key = getmsbhex(&line[17]);
2382 		if (!strncmp(line,"Windows attrib : 0x",19))
2383 			attrib = getmsbhex(&line[19]);
2384 		if (done
2385 		    || !strncmp(line,"File ",5)
2386 		    || !strncmp(line,"Directory ",10)) {
2387 			/*
2388 			 *  New file or directory (or end of file) :
2389 			 *  apply attribute just collected
2390 			 *  or apply attribute defined from current key
2391 			 */
2392 
2393 			if (withattr
2394 			    && oldhash
2395 			    && (hash((const le32*)attr,ntfs_attr_size(attr)) != oldhash)) {
2396 				printf("** ACL rejected, its hash is not as expected\n");
2397 				errors++;
2398 			} else
2399 				if (fullname[0]) {
2400 					phead = (SECURITY_DESCRIPTOR_RELATIVE*)attr;
2401 					/* set the request for auto-inheritance */
2402 					if (phead->control & SE_DACL_AUTO_INHERITED)
2403 						phead->control |= SE_DACL_AUTO_INHERIT_REQ;
2404 					if (!applyattr(fullname,attr,withattr,
2405 							attrib,key))
2406 						errors++;
2407 					else
2408 						count++;
2409 				}
2410 			/* save current file or directory name */
2411 			withattr = FALSE;
2412 			key = 0;
2413 			oldhash = 0;
2414 			attrib = INVALID_FILE_ATTRIBUTES;
2415 			if (!done) {
2416 				if (!strncmp(line,"File ",5))
2417 					strcpy(fullname,&line[5]);
2418 				else
2419 					strcpy(fullname,&line[10]);
2420 #ifdef HAVE_WINDOWS_H
2421 				cleanpath(fullname);
2422 #endif /* HAVE_WINDOWS_H */
2423 			}
2424 		}
2425 	} while (!done);
2426 	printf("%d ACLs have been applied\n",count);
2427 	return (FALSE);
2428 }
2429 
dorestore(const char * volume,FILE * fd)2430 static BOOL dorestore(const char *volume, FILE *fd)
2431 {
2432 	BOOL err;
2433 
2434 	err = FALSE;
2435 	if (!getuid()) {
2436  		if (open_security_api()) {
2437 			if (open_volume(volume,NTFS_MNT_NONE)) {
2438 				if (restore(fd)) err = TRUE;
2439 				close_volume(volume);
2440 			} else {
2441 				fprintf(stderr,"Could not open volume %s\n",volume);
2442 				printerror(stderr);
2443 				err = TRUE;
2444 			}
2445 			close_security_api();
2446 		} else {
2447 			fprintf(stderr,"Could not open security API\n");
2448 			printerror(stderr);
2449 			err = TRUE;
2450 		}
2451 	} else {
2452 		fprintf(stderr,"Restore is only possible as root\n");
2453 		err = TRUE;
2454 	}
2455 	return (err);
2456 }
2457 
2458 #if POSIXACLS
2459 
2460 /*
2461  *		Merge Posix ACL rights into an u32 (self test only)
2462  *
2463  *	Result format : -----rwxrwxrwxrwxrwx---rwxrwxrwx
2464  *                           U1 U2 G1 G2  M     o  g  w
2465  *
2466  *	Only two users (U1, U2) and two groups (G1, G2) taken into account
2467  */
merge_rights(const struct POSIX_SECURITY * pxdesc,BOOL def)2468 static u32 merge_rights(const struct POSIX_SECURITY *pxdesc, BOOL def)
2469 {
2470 	const struct POSIX_ACE *pxace;
2471 	int i;
2472 	int users;
2473 	int groups;
2474 	int first;
2475 	int last;
2476 	u32 rights;
2477 
2478 	rights = 0;
2479 	users = 0;
2480 	groups = 0;
2481 	if (def) {
2482 		first = pxdesc->firstdef;
2483 		last = pxdesc->firstdef + pxdesc->defcnt - 1;
2484 	} else {
2485 		first = 0;
2486 		last = pxdesc->acccnt - 1;
2487 	}
2488 	pxace = pxdesc->acl.ace;
2489 	for (i=first; i<=last; i++) {
2490 		switch (pxace[i].tag) {
2491 		case POSIX_ACL_USER_OBJ :
2492 			rights |= (pxace[i].perms & 7) << 6;
2493 			break;
2494 		case POSIX_ACL_USER :
2495 			if (users < 2)
2496 				rights |= ((u32)pxace[i].perms & 7) << (24 - 3*users);
2497 			users++;
2498 			break;
2499 		case POSIX_ACL_GROUP_OBJ :
2500 			rights |= (pxace[i].perms & 7) << 3;
2501 			break;
2502 		case POSIX_ACL_GROUP :
2503 			if (groups < 2)
2504 				rights |= ((u32)pxace[i].perms & 7) << (18 - 3*groups);
2505 			groups++;
2506 			break;
2507 		case POSIX_ACL_MASK :
2508 			rights |= ((u32)pxace[i].perms & 7) << 12;
2509 			break;
2510 		case POSIX_ACL_OTHER :
2511 			rights |= (pxace[i].perms & 7);
2512 			break;
2513 		default :
2514 			break;
2515 		}
2516 	}
2517 	return (rights);
2518 }
2519 
same_posix(struct POSIX_SECURITY * pxdesc1,struct POSIX_SECURITY * pxdesc2)2520 static BOOL same_posix(struct POSIX_SECURITY *pxdesc1,
2521 			struct POSIX_SECURITY *pxdesc2)
2522 {
2523 	BOOL same;
2524 	int i;
2525 
2526 	same = pxdesc1
2527 		&& pxdesc2
2528 		&& (pxdesc1->mode == pxdesc2->mode)
2529 		&& (pxdesc1->acccnt == pxdesc2->acccnt)
2530 		&& (pxdesc1->defcnt == pxdesc2->defcnt)
2531 		&& (pxdesc1->firstdef == pxdesc2->firstdef)
2532 		&& (pxdesc1->tagsset == pxdesc2->tagsset)
2533 		&& (pxdesc1->acl.version == pxdesc2->acl.version)
2534 		&& (pxdesc1->acl.flags == pxdesc2->acl.flags);
2535 	i = 0;
2536 	while (same && (i < pxdesc1->acccnt)) {
2537 		same = (pxdesc1->acl.ace[i].tag == pxdesc2->acl.ace[i].tag)
2538 		   && (pxdesc1->acl.ace[i].perms == pxdesc2->acl.ace[i].perms)
2539 		   && (pxdesc1->acl.ace[i].id == pxdesc2->acl.ace[i].id);
2540 		i++;
2541 	}
2542 	i = pxdesc1->firstdef;
2543 	while (same && (i < pxdesc1->firstdef + pxdesc1->defcnt)) {
2544 		same = (pxdesc1->acl.ace[i].tag == pxdesc2->acl.ace[i].tag)
2545 		   && (pxdesc1->acl.ace[i].perms == pxdesc2->acl.ace[i].perms)
2546 		   && (pxdesc1->acl.ace[i].id == pxdesc2->acl.ace[i].id);
2547 		i++;
2548 	}
2549 	return (same);
2550 }
2551 
2552 #endif /* POSIXACLS */
2553 
2554 #if POSIXACLS & SELFTESTS
2555 
tryposix(struct POSIX_SECURITY * pxdesc)2556 static void tryposix(struct POSIX_SECURITY *pxdesc)
2557 {
2558 	le32 owner_sid[] = /* S-1-5-21-3141592653-589793238-462843383-1016 */
2559 		{
2560 		cpu_to_le32(0x501), cpu_to_le32(0x05000000), cpu_to_le32(21),
2561 		cpu_to_le32(DEFSECAUTH1), cpu_to_le32(DEFSECAUTH2),
2562 		cpu_to_le32(DEFSECAUTH3), cpu_to_le32(1016)
2563 		} ;
2564 	le32 group_sid[] = /* S-1-5-21-3141592653-589793238-462843383-513 */
2565 		{
2566 		cpu_to_le32(0x501), cpu_to_le32(0x05000000), cpu_to_le32(21),
2567 		cpu_to_le32(DEFSECAUTH1), cpu_to_le32(DEFSECAUTH2),
2568 		cpu_to_le32(DEFSECAUTH3), cpu_to_le32(513)
2569 		} ;
2570 
2571 	char *attr;
2572 	BOOL isdir;
2573 	mode_t mode;
2574 	struct POSIX_SECURITY *newpxdesc;
2575 	struct POSIX_SECURITY *oldpxdesc;
2576 	static char *oldattr = (char*)NULL;
2577 
2578 	isdir = FALSE;
2579 	if (oldattr) {
2580 		oldpxdesc = linux_permissions_posix(oldattr, isdir);
2581 		newpxdesc = ntfs_merge_descr_posix(pxdesc, oldpxdesc);
2582 		if (!newpxdesc)
2583 			newpxdesc = pxdesc;
2584 		free(oldpxdesc);
2585 		if (opt_v) {
2586 			printf("merged descriptors :\n");
2587 			showposix(newpxdesc);
2588 		}
2589 	} else
2590 		newpxdesc = pxdesc;
2591 	attr = ntfs_build_descr_posix(context.mapping,newpxdesc,
2592 			isdir,(SID*)owner_sid,(SID*)group_sid);
2593 	if (attr && ntfs_valid_descr(attr, ntfs_attr_size(attr))) {
2594 		if (opt_v)
2595 			hexdump(attr,ntfs_attr_size(attr),8);
2596 		if (opt_v >= 2) {
2597 			showheader(attr,4);
2598 			showusid(attr,4);
2599 			showgsid(attr,4);
2600 			showdacl(attr,isdir,4);
2601 			showsacl(attr,isdir,4);
2602 			mode = linux_permissions(attr,isdir);
2603 			printf("Interpreted Unix mode 0%03o\n",mode);
2604 			printf("Interpreted back Posix descriptor :\n");
2605 			newpxdesc = linux_permissions_posix(attr,isdir);
2606 			showposix(newpxdesc);
2607 			free(newpxdesc);
2608 		}
2609 		if (oldattr) free(oldattr);
2610 		oldattr = attr;
2611 	}
2612 }
2613 
2614 #endif /* POSIXACLS & SELFTESTS */
2615 
2616 #ifdef SELFTESTS
2617 
2618 /*
2619  *		Build a dummy security descriptor
2620  *	returns descriptor in allocated memory, must free() after use
2621  */
2622 
build_dummy_descr(BOOL isdir,const SID * usid,const SID * gsid,int cnt,...)2623 static char *build_dummy_descr(BOOL isdir __attribute__((unused)),
2624 			const SID *usid, const SID *gsid,
2625 			int cnt,
2626 			 /* seq of int allow, SID *sid, int flags, u32 mask */
2627 			...)
2628 {
2629 	char *attr;
2630 	int attrsz;
2631 	SECURITY_DESCRIPTOR_RELATIVE *pnhead;
2632 	ACL *pacl;
2633 	ACCESS_ALLOWED_ACE *pace;
2634 	va_list ap;
2635 	const SID *sid;
2636 	u32 umask;
2637 	le32 mask;
2638 	int flags;
2639 	BOOL allow;
2640 	int pos;
2641 	int usidsz;
2642 	int gsidsz;
2643 	int sidsz;
2644 	int aclsz;
2645 	int i;
2646 
2647 	if (usid)
2648 		usidsz = ntfs_sid_size(usid);
2649 	else
2650 		usidsz = 0;
2651 	if (gsid)
2652 		gsidsz = ntfs_sid_size(gsid);
2653 	else
2654 		gsidsz = 0;
2655 
2656 
2657 	/* allocate enough space for the new security attribute */
2658 	attrsz = sizeof(SECURITY_DESCRIPTOR_RELATIVE)	/* header */
2659 	    + usidsz + gsidsz	/* usid and gsid */
2660 	    + sizeof(ACL)	/* acl header */
2661 	    + cnt*40;
2662 
2663 	attr = (char*)ntfs_malloc(attrsz);
2664 	if (attr) {
2665 		/* build the main header part */
2666 		pnhead = (SECURITY_DESCRIPTOR_RELATIVE*) attr;
2667 		pnhead->revision = SECURITY_DESCRIPTOR_REVISION;
2668 		pnhead->alignment = 0;
2669 			/*
2670 			 * The flag SE_DACL_PROTECTED prevents the ACL
2671 			 * to be changed in an inheritance after creation
2672 			 */
2673 		pnhead->control = SE_DACL_PRESENT | SE_DACL_PROTECTED
2674 				    | SE_SELF_RELATIVE;
2675 			/*
2676 			 * Windows prefers ACL first, do the same to
2677 			 * get the same hash value and avoid duplication
2678 			 */
2679 		/* build the ACL header */
2680 		pos = sizeof(SECURITY_DESCRIPTOR_RELATIVE);
2681 		pacl = (ACL*)&attr[pos];
2682 		pacl->revision = ACL_REVISION;
2683 		pacl->alignment1 = 0;
2684 		pacl->size = cpu_to_le16(0); /* fixed later */
2685 		pacl->ace_count = cpu_to_le16(cnt);
2686 		pacl->alignment2 = cpu_to_le16(0);
2687 
2688 		/* enter the ACEs */
2689 
2690 		pos += sizeof(ACL);
2691 		aclsz = sizeof(ACL);
2692 		va_start(ap,cnt);
2693 		for (i=0; i<cnt; i++) {
2694 			pace = (ACCESS_ALLOWED_ACE*)&attr[pos];
2695 			allow = va_arg(ap,int);
2696 			sid = va_arg(ap,SID*);
2697 			flags = va_arg(ap,int);
2698 			umask = va_arg(ap,u32);
2699 			mask = cpu_to_le32(umask);
2700 			sidsz = ntfs_sid_size(sid);
2701 			pace->type = (allow ? ACCESS_ALLOWED_ACE_TYPE : ACCESS_DENIED_ACE_TYPE);
2702 			pace->flags = flags;
2703 			pace->size = cpu_to_le16(sidsz + 8);
2704 			pace->mask = mask;
2705 			memcpy(&pace->sid,sid,sidsz);
2706 			aclsz += sidsz + 8;
2707 			pos += sidsz + 8;
2708 		}
2709 		va_end(ap);
2710 
2711 		/* append usid and gsid if defined */
2712 		/* positions of ACL, USID and GSID into header */
2713 		pnhead->owner = cpu_to_le32(0);
2714 		pnhead->group = cpu_to_le32(0);
2715 		if (usid) {
2716 			memcpy(&attr[pos], usid, usidsz);
2717 			pnhead->owner = cpu_to_le32(pos);
2718 		}
2719 		if (gsid) {
2720 			memcpy(&attr[pos + usidsz], gsid, gsidsz);
2721 			pnhead->group = cpu_to_le32(pos + usidsz);
2722 		}
2723 		/* positions of DACL and SACL into header */
2724 		pnhead->sacl = cpu_to_le32(0);
2725 		if (cnt) {
2726 			pacl->size = cpu_to_le16(aclsz);
2727 			pnhead->dacl =
2728 			    cpu_to_le32(sizeof(SECURITY_DESCRIPTOR_RELATIVE));
2729 		} else
2730 			pnhead->dacl = cpu_to_le32(0);
2731 		if (!ntfs_valid_descr(attr,pos+usidsz+gsidsz)) {
2732 			printf("** Bad sample descriptor\n");
2733 			free(attr);
2734 			attr = (char*)NULL;
2735 			errors++;
2736 		}
2737 	} else
2738 		errno = ENOMEM;
2739 	return (attr);
2740 }
2741 
2742 /*
2743  *		Check a few samples with special conditions
2744  */
2745 
check_samples(void)2746 static void check_samples(void)
2747 {
2748 	char *descr = (char*)NULL;
2749 	BOOL isdir = FALSE;
2750 	mode_t perms;
2751 	mode_t expect = 0;
2752 	int cnt;
2753 	u32 expectacc;
2754 	u32 expectdef;
2755 #if POSIXACLS
2756 	u32 accrights;
2757 	u32 defrights;
2758 	mode_t mixmode;
2759 	struct POSIX_SECURITY *pxdesc;
2760 	struct POSIX_SECURITY *pxsample;
2761 	const char *txsample;
2762 #endif /* POSIXACLS */
2763 	le32 owner1[] = /* S-1-5-21-1833069642-4243175381-1340018762-1003 */
2764 		{
2765 		cpu_to_le32(0x501), cpu_to_le32(0x05000000), cpu_to_le32(21),
2766 		cpu_to_le32(1833069642), cpu_to_le32(4243175381U),
2767 		cpu_to_le32(1340018762), cpu_to_le32(1003)
2768 		} ;
2769 	le32 group1[] = /* S-1-5-21-1833069642-4243175381-1340018762-513 */
2770 		{
2771 		cpu_to_le32(0x501), cpu_to_le32(0x05000000), cpu_to_le32(21),
2772 		cpu_to_le32(1833069642), cpu_to_le32(4243175381U),
2773 		cpu_to_le32(1340018762), cpu_to_le32(513)
2774 		} ;
2775 	le32 group2[] = /* S-1-5-21-1607551490-981732888-1819828000-513 */
2776 		{
2777 		cpu_to_le32(0x501), cpu_to_le32(0x05000000), cpu_to_le32(21),
2778 		cpu_to_le32(1607551490), cpu_to_le32(981732888),
2779 		cpu_to_le32(1819828000), cpu_to_le32(513)
2780 		} ;
2781 	le32 owner3[] = /* S-1-5-21-3141592653-589793238-462843383-1016 */
2782 		{
2783 		cpu_to_le32(0x501), cpu_to_le32(0x05000000), cpu_to_le32(21),
2784 		cpu_to_le32(DEFSECAUTH1), cpu_to_le32(DEFSECAUTH2),
2785 		cpu_to_le32(DEFSECAUTH3), cpu_to_le32(1016)
2786 		} ;
2787 	le32 group3[] = /* S-1-5-21-3141592653-589793238-462843383-513 */
2788 		{
2789 		cpu_to_le32(0x501), cpu_to_le32(0x05000000), cpu_to_le32(21),
2790 		cpu_to_le32(DEFSECAUTH1), cpu_to_le32(DEFSECAUTH2),
2791 		cpu_to_le32(DEFSECAUTH3), cpu_to_le32(513)
2792 		} ;
2793 
2794 #if POSIXACLS
2795 	struct {
2796 		struct POSIX_SECURITY head;
2797 		struct POSIX_ACE ace[4];
2798 	} sampletry1 =
2799 	{
2800 		{ 0645, 4, 0, 4, 0x35, 0,
2801 			{ POSIX_VERSION, 0, 0 }
2802 		},
2803 		{
2804 			{ 1, 6, -1 },
2805 			{ 4, 5, -1 },
2806 			{ 16, 4, -1 },
2807 			{ 32, 5, -1 }
2808 		}
2809 	} ;
2810 
2811 	struct {
2812 		struct POSIX_SECURITY head;
2813 		struct POSIX_ACE ace[6];
2814 	} sampletry3 =
2815 	{
2816 		{ 0100, 6, 0, 6, 0x3f, 0,
2817 			{ POSIX_VERSION, 0, 0 }
2818 		},
2819 		{
2820 			{ 1, 1, -1 },
2821 			{ 2, 3, 1000 },
2822 			{ 4, 1, -1 },
2823 			{ 8, 3, 1002 },
2824 			{ 16, 0, -1 },
2825 			{ 32, 0, -1 }
2826 		}
2827 	} ;
2828 
2829 	struct {
2830 		struct POSIX_SECURITY head;
2831 		struct POSIX_ACE ace[8];
2832 	} sampletry4 =
2833 	{
2834 		{ 0140, 8, 0, 8, 0x3f, 0,
2835 			{ POSIX_VERSION, 0, 0 }
2836 		},
2837 		{
2838 			{ 1, 1, -1 },
2839 			{ 2, 3, 516 },
2840 			{ 2, 6, 1000 },
2841 			{ 4, 1, -1 },
2842 			{ 8, 6, 500 },
2843 			{ 8, 3, 1002 },
2844 			{ 16, 4, -1 },
2845 			{ 32, 0, -1 }
2846 		}
2847 	} ;
2848 
2849 	struct {
2850 		struct POSIX_SECURITY head;
2851 		struct POSIX_ACE ace[6];
2852 	} sampletry5 =
2853 	{
2854 		{ 0454, 6, 0, 6, 0x3f, 0,
2855 			{ POSIX_VERSION, 0, 0 }
2856 		},
2857 		{
2858 			{ 1, 4, -1 },
2859 			{ 2, 5, 516 },
2860 			{ 4, 4, -1 },
2861 			{ 8, 6, 500 },
2862 			{ 16, 5, -1 },
2863 			{ 32, 4, -1 }
2864 		}
2865 	} ;
2866 
2867 	struct {
2868 		struct POSIX_SECURITY head;
2869 		struct POSIX_ACE ace[8];
2870 	} sampletry6 =
2871 	{
2872 		{ 0332, 8, 0, 8, 0x3f, 0,
2873 			{ POSIX_VERSION, 0, 0 }
2874 		},
2875 		{
2876 			{ 1, 3, -1 },
2877 			{ 2, 1,  0 },
2878 			{ 2, 2,  1000 },
2879 			{ 4, 6, -1 },
2880 			{ 8, 4,  0 },
2881 			{ 8, 5,  1002 },
2882 			{ 16, 3, -1 },
2883 			{ 32, 2, -1 }
2884 		}
2885 	} ;
2886 
2887 	struct {
2888 		struct POSIX_SECURITY head;
2889 		struct POSIX_ACE ace[4];
2890 	} sampletry8 =
2891 	{
2892 		{ 0677, 4, 0, 4, 0x35, 0,
2893 			{ POSIX_VERSION, 0, 0 }
2894 		},
2895 		{
2896 			{ 1, 6, -1 },
2897 			{ 4, 7, -1 },
2898 			{ 16, 7, -1 },
2899 			{ 32, 7, -1 }
2900 		}
2901 	} ;
2902 
2903 #endif /* POSIXACLS */
2904 
2905 
2906 #if POSIXACLS
2907 	for (cnt=1; cnt<=8; cnt++) {
2908 		switch (cnt) {
2909 		case 1 :
2910 			pxsample = &sampletry1.head;
2911 			txsample = "sampletry1-a";
2912 			isdir = FALSE;
2913 			descr = ntfs_build_descr_posix(context.mapping,&sampletry1.head,
2914 				isdir, (const SID*)owner3, (const SID*)group3);
2915 			break;
2916 		case 2 :
2917 			pxsample = &sampletry1.head;
2918 			txsample = "sampletry1-b";
2919 			isdir = FALSE;
2920 			descr = ntfs_build_descr_posix(context.mapping,&sampletry1.head,
2921 				isdir, (const SID*)adminsid, (const SID*)group3);
2922 			break;
2923 		case 3 :
2924 			isdir = FALSE;
2925 			pxsample = &sampletry3.head;
2926 			txsample = "sampletry3";
2927 			descr = ntfs_build_descr_posix(context.mapping,pxsample,
2928 				isdir, (const SID*)group3, (const SID*)group3);
2929 			break;
2930 		case 4 :
2931 			isdir = FALSE;
2932 			pxsample = &sampletry4.head;
2933 			txsample = "sampletry4";
2934 			descr = ntfs_build_descr_posix(context.mapping,pxsample,
2935 				isdir, (const SID*)owner3, (const SID*)group3);
2936 			break;
2937 		case 5 :
2938 			isdir = FALSE;
2939 			pxsample = &sampletry5.head;
2940 			txsample = "sampletry5";
2941 			descr = ntfs_build_descr_posix(context.mapping,pxsample,
2942 				isdir, (const SID*)owner3, (const SID*)group3);
2943 			break;
2944 		case 6 :
2945 			isdir = FALSE;
2946 			pxsample = &sampletry6.head;
2947 			txsample = "sampletry6-a";
2948 			descr = ntfs_build_descr_posix(context.mapping,pxsample,
2949 				isdir, (const SID*)owner3, (const SID*)group3);
2950 			break;
2951 		case 7 :
2952 			isdir = FALSE;
2953 			pxsample = &sampletry6.head;
2954 			txsample = "sampletry6-b";
2955 			descr = ntfs_build_descr_posix(context.mapping,pxsample,
2956 				isdir, (const SID*)adminsid, (const SID*)adminsid);
2957 			break;
2958 		case 8 :
2959 			pxsample = &sampletry8.head;
2960 			txsample = "sampletry8";
2961 			isdir = FALSE;
2962 			descr = ntfs_build_descr_posix(context.mapping,&sampletry8.head,
2963 				isdir, (const SID*)owner3, (const SID*)group3);
2964 			break;
2965 		default :
2966 			pxsample = (struct POSIX_SECURITY*)NULL;
2967 			txsample = (const char*)NULL;
2968 		}
2969 				/* check we get original back */
2970 		if (descr)
2971 			pxdesc = linux_permissions_posix(descr, isdir);
2972 		else
2973 			pxdesc = (struct POSIX_SECURITY*)NULL;
2974 		if (!descr || !pxdesc || !same_posix(pxsample,pxdesc)) {
2975 			printf("** Error in %s (errno %d)\n",txsample,errno);
2976 			showposix(pxsample);
2977 			if (descr)
2978 				showall(descr,0);
2979 			if (pxdesc)
2980 				showposix(pxdesc);
2981 			errors++;
2982 		}
2983 		free(descr);
2984 		free(pxdesc);
2985 	}
2986 
2987 #endif /* POSIXACLS */
2988 
2989 
2990 		/*
2991 		 *		Check a few samples built by Windows,
2992 		 *	which cannot be generated by Linux
2993 		 */
2994 
2995 	for (cnt=1; cnt<=10; cnt++) {
2996 		switch(cnt) {
2997 		case 1 :  /* hp/tmp */
2998 			isdir = TRUE;
2999 			descr = build_dummy_descr(isdir,
3000 				(const SID*)owner1, (const SID*)group1,
3001 				1,
3002 				(int)TRUE, worldsid, (int)0x3, (u32)0x1f01ff);
3003 			expect = expectacc = 0777;
3004 			expectdef = 0;
3005 			break;
3006 		case 2 :  /* swsetup */
3007 			isdir = TRUE;
3008 			descr = build_dummy_descr(isdir, adminsid, (const SID*)group2,
3009 				2,
3010 				(int)TRUE, worldsid, (int)0x0, (u32)0x1f01ff,
3011 				(int)TRUE, worldsid, (int)0xb, (u32)0x1f01ff);
3012 			expectacc = expect = 0777;
3013 			expectdef = 0777;
3014 			break;
3015 		case 3 :  /* Dr Watson */
3016 			isdir = TRUE;
3017 			descr = build_dummy_descr(isdir, (const SID*)owner3, (const SID*)group3,
3018 				0);
3019 			expectacc = expect = 0700;
3020 			expectdef = 0;
3021 			break;
3022 		case 4 :
3023 			isdir = FALSE;
3024 			descr = build_dummy_descr(isdir,
3025 				(const SID*)owner3, (const SID*)group3,
3026 				4,
3027 				(int)TRUE, (const SID*)owner3, 0,
3028 					le32_to_cpu(FILE_READ_DATA | OWNER_RIGHTS),
3029 				(int)TRUE, (const SID*)group3, 0,
3030 					le32_to_cpu(FILE_WRITE_DATA),
3031 				(int)TRUE, (const SID*)group2, 0,
3032 					le32_to_cpu(FILE_WRITE_DATA | FILE_READ_DATA),
3033 				(int)TRUE, (const SID*)worldsid, 0,
3034 					le32_to_cpu(FILE_EXECUTE));
3035 			expect = 0731;
3036 			expectacc = 07070731;
3037 			expectdef = 0;
3038 			break;
3039 		case 5 :  /* Vista/JP */
3040 			isdir = TRUE;
3041 			descr = build_dummy_descr(isdir, systemsid, systemsid,
3042 				6,
3043 				(int)TRUE, owner1, (int)0x0, (u32)0x1f01ff,
3044 				(int)TRUE, systemsid, (int)0x0, (u32)0x1f01ff,
3045 				(int)TRUE, adminsid, (int)0x0, (u32)0x1f01ff,
3046 				(int)TRUE, owner1, (int)0xb, (u32)0x10000000,
3047 				(int)TRUE, systemsid, (int)0xb, (u32)0x10000000,
3048 				(int)TRUE, adminsid, (int)0xb, (u32)0x10000000);
3049 			expectacc = expect = 0700;
3050 			expectdef = 0700;
3051 			break;
3052 		case 6 :  /* Vista/JP2 */
3053 			isdir = TRUE;
3054 			descr = build_dummy_descr(isdir, systemsid, systemsid,
3055 				7,
3056 				(int)TRUE, owner1,    (int)0x0, (u32)0x1f01ff,
3057 				(int)TRUE, systemsid, (int)0x0, (u32)0x1f01ff,
3058 				(int)TRUE, adminsid,  (int)0x0, (u32)0x1f01ff,
3059 				(int)TRUE, owner1,    (int)0xb, (u32)0x1f01ff,
3060 				(int)TRUE, systemsid, (int)0xb, (u32)0x1f01ff,
3061 				(int)TRUE, adminsid,  (int)0xb, (u32)0x1f01ff,
3062 				(int)TRUE, owner3,    (int)0x3, (u32)0x1200a9);
3063 			expectacc = 0500070700;
3064 			expectdef = expect = 0700;
3065 			break;
3066 		case 7 :  /* WinXP/JP */
3067 			isdir = TRUE;
3068 			descr = build_dummy_descr(isdir, adminsid, systemsid,
3069 				6,
3070 				(int)TRUE, owner1, (int)0x0, (u32)0x1f01ff,
3071 				(int)TRUE, systemsid, (int)0x0, (u32)0x1f01ff,
3072 				(int)TRUE, adminsid, (int)0x0, (u32)0x1f01ff,
3073 				(int)TRUE, owner1, (int)0xb, (u32)0x10000000,
3074 				(int)TRUE, systemsid, (int)0xb, (u32)0x10000000,
3075 				(int)TRUE, adminsid, (int)0xb, (u32)0x10000000);
3076 			expectacc = expect = 0700;
3077 			expectdef = 0700;
3078 			break;
3079 		case 8 :  /* WinXP/JP2 */
3080 			isdir = TRUE;
3081 			descr = build_dummy_descr(isdir, adminsid, systemsid,
3082 				6,
3083 				(int)TRUE, owner1,    (int)0x0, (u32)0x1f01ff,
3084 				(int)TRUE, systemsid, (int)0x0, (u32)0x1f01ff,
3085 				(int)TRUE, adminsid,  (int)0x0, (u32)0x1f01ff,
3086 				(int)TRUE, owner1,    (int)0xb, (u32)0x10000000,
3087 				(int)TRUE, systemsid, (int)0xb, (u32)0x10000000,
3088 				(int)TRUE, adminsid,  (int)0xb, (u32)0x10000000);
3089 			expectacc = expect = 0700;
3090 			expectdef = 0700;
3091 			break;
3092 		case 9 :  /* Win8/bin */
3093 			isdir = TRUE;
3094 			descr = build_dummy_descr(isdir,
3095 				(const SID*)owner3, (const SID*)owner3,
3096 				6,
3097 				(int)TRUE, authsid,   (int)0x3,  (u32)0x1f01ff,
3098 				(int)TRUE, adminsid,  (int)0x13, (u32)0x1f01ff,
3099 				(int)TRUE, systemsid, (int)0x13, (u32)0x1f01ff,
3100 				(int)TRUE, localsid,  (int)0x13, (u32)0x1200a9,
3101 				(int)TRUE, authsid,   (int)0x10, (u32)0x1301bf,
3102 				(int)TRUE, authsid,   (int)0x1b, (u32)0xe0010000);
3103 			expectacc = expect = 0777;
3104 			expectdef = 0777;
3105 			break;
3106 		case 10 :  /* Win8/bin/linem.exe */
3107 			isdir = FALSE;
3108 			descr = build_dummy_descr(isdir,
3109 				(const SID*)owner3, (const SID*)owner3,
3110 				4,
3111 				(int)TRUE, authsid,   (int)0x10, (u32)0x1f01ff,
3112 				(int)TRUE, adminsid,  (int)0x10, (u32)0x1f01ff,
3113 				(int)TRUE, systemsid, (int)0x10, (u32)0x1ff,
3114 				(int)TRUE, localsid,  (int)0x10, (u32)0x1200a9);
3115 			expectacc = expect = 0777;
3116 			expectdef = 0;
3117 			break;
3118 		default :
3119 			expectacc = expectdef = 0;
3120 			break;
3121 		}
3122 		if (descr) {
3123 			perms = linux_permissions(descr, isdir);
3124 			if (perms != expect) {
3125 				printf("** Error in sample %d, perms 0%03o expected 0%03o\n",
3126 					cnt,perms,expect);
3127 				showall(descr,0);
3128 				errors++;
3129 			} else {
3130 #if POSIXACLS
3131 				pxdesc = linux_permissions_posix(descr, isdir);
3132 				if (pxdesc) {
3133 					accrights = merge_rights(pxdesc,FALSE);
3134 					defrights = merge_rights(pxdesc,TRUE);
3135 					if (!(pxdesc->tagsset & ~(POSIX_ACL_USER_OBJ | POSIX_ACL_GROUP_OBJ | POSIX_ACL_OTHER)))
3136 						mixmode = expect;
3137 					else
3138 						mixmode = (expect & 07707) | ((accrights >> 9) & 070);
3139 					if ((pxdesc->mode != mixmode)
3140 					  || (accrights != expectacc)
3141 					  || (defrights != expectdef)) {
3142 						printf("** Error in sample %d : mode %03o expected 0%03o\n",
3143 							cnt,pxdesc->mode,mixmode);
3144 						printf("     Posix access rights 0%03lo expected 0%03lo\n",
3145 							(long)accrights,(long)expectacc);
3146 						printf("          default rights 0%03lo expected 0%03lo\n",
3147 							(long)defrights,(long)expectdef);
3148 						showall(descr,0);
3149 						showposix(pxdesc);
3150 					}
3151 					free(pxdesc);
3152 				}
3153 #endif /* POSIXACLS */
3154 			}
3155 		free(descr);
3156 		}
3157 	}
3158 }
3159 
3160 
3161 /*
3162  *		Check whether any basic permission setting is interpreted
3163  *	back exactly as set
3164  */
3165 
basictest(int kind,BOOL isdir,const SID * owner,const SID * group)3166 static void basictest(int kind, BOOL isdir, const SID *owner, const SID *group)
3167 {
3168 	char *attr;
3169 	mode_t perm;
3170 	mode_t gotback;
3171 	u32 count;
3172 	u32 acecount;
3173 	u32 globhash;
3174 	const SECURITY_DESCRIPTOR_RELATIVE *phead;
3175 	const ACL *pacl;
3176 	enum { ERRNO,
3177 		ERRMA, ERRPA, /* error converting mode or Posix ACL to NTFS */
3178 		ERRAM, ERRAP, /* error converting NTFS to mode or Posix ACL */
3179 	} err;
3180 	u32 expectcnt[] = {
3181 		27800, 31896,
3182 		24064, 28160,
3183 		24064, 28160,
3184 		24064, 28160,
3185 		24904, 29000
3186 	} ;
3187 	u32 expecthash[] = {
3188 		0x8f80865b, 0x7bc7960,
3189 		0x8fd9ecfe, 0xddd4db0,
3190 		0xa8b07400, 0xa189c20,
3191 		0xc5689a00, 0xb6c09000,
3192 		0xb040e509, 0x4f4db7f7
3193 	} ;
3194 #if POSIXACLS
3195 	struct POSIX_SECURITY *pxdesc;
3196 	char *pxattr;
3197 	u32 pxcount;
3198 	u32 pxacecount;
3199 	u32 pxglobhash;
3200 #endif /* POSIXACLS */
3201 
3202 	count = 0;
3203 	acecount = 0;
3204 	globhash = 0;
3205 #if POSIXACLS
3206 	pxcount = 0;
3207 	pxacecount = 0;
3208 	pxglobhash = 0;
3209 #endif /* POSIXACLS */
3210 	for (perm=0; (perm<=07777) && (errors < 10); perm++) {
3211 		err = ERRNO;
3212 		/* file owned by plain user and group */
3213 		attr = ntfs_build_descr(perm,isdir,owner,(const SID*)group);
3214 		if (attr && ntfs_valid_descr(attr, ntfs_attr_size(attr))) {
3215 			phead = (const SECURITY_DESCRIPTOR_RELATIVE*)attr;
3216 			pacl = (const ACL*)&attr[le32_to_cpu(phead->dacl)];
3217 			acecount += le16_to_cpu(pacl->ace_count);
3218 			globhash += hash((const le32*)attr,ntfs_attr_size(attr));
3219 			count++;
3220 #if POSIXACLS
3221 			/*
3222 			 * Build a NTFS ACL from a mode, and
3223 			 * decode to a Posix ACL, expecting to
3224 			 * get the original mode back.
3225 			 */
3226 			pxdesc = linux_permissions_posix(attr, isdir);
3227 			if (!pxdesc || (pxdesc->mode != perm)) {
3228 				err = ERRAP;
3229 				if (pxdesc)
3230 					gotback = pxdesc->mode;
3231 				else
3232 					gotback = 0;
3233 			} else {
3234 			/*
3235 			 * Build a NTFS ACL from the Posix ACL, expecting to
3236 			 * get exactly the same NTFS ACL, then decode to a
3237 			 * mode, expecting to get the original mode back.
3238 			 */
3239 				pxattr = ntfs_build_descr_posix(context.mapping,
3240 						pxdesc,isdir,owner,
3241 						(const SID*)group);
3242 				if (pxattr && !memcmp(pxattr,attr,
3243 						 ntfs_attr_size(attr))) {
3244 					phead = (const SECURITY_DESCRIPTOR_RELATIVE*)attr;
3245 					pacl = (const ACL*)&attr[le32_to_cpu(phead->dacl)];
3246 					pxacecount += le16_to_cpu(pacl->ace_count);
3247 					pxglobhash += hash((const le32*)attr,ntfs_attr_size(attr));
3248 					pxcount++;
3249 					gotback = linux_permissions(pxattr, isdir);
3250 					if (gotback != perm)
3251 						err = ERRAM;
3252 					else
3253 						free(pxattr);
3254 				} else
3255 					err = ERRPA;
3256 				free(attr);
3257 			}
3258 			free(pxdesc);
3259 #else /* POSIXACLS */
3260 			gotback = linux_permissions(attr, isdir);
3261 			if (gotback != perm)
3262 				err = ERRAM;
3263 			else
3264 				free(attr);
3265 #endif /* POSIXACLS */
3266 		} else
3267 			err = ERRMA;
3268 
3269 		switch (err) {
3270 		case ERRMA :
3271 			printf("** no or wrong permission settings "
3272 				"for kind %d perm %03o\n",kind,perm);
3273 			if (attr && opt_v)
3274 				hexdump(attr,ntfs_attr_size(attr),8);
3275 			if (attr && (opt_v >= 2)) {
3276 				showheader(attr,4);
3277 				showusid(attr,4);
3278 				showgsid(attr,4);
3279 				showdacl(attr,isdir,4);
3280 				showsacl(attr,isdir,4);
3281 			}
3282 			errors++;
3283 			break;
3284 		case ERRPA :
3285 			printf("** no or wrong permission settings from PX "
3286 				"for kind %d perm %03o\n",kind,perm);
3287 			errors++;
3288 			break;
3289 #if POSIXACLS
3290 		case ERRAM :
3291 			printf("** wrong permission settings, "
3292 				"kind %d perm 0%03o, gotback %03o\n",
3293 				kind, perm, gotback);
3294 			if (opt_v)
3295 				hexdump(pxattr,ntfs_attr_size(pxattr),8);
3296 			if (opt_v >= 2) {
3297 				showheader(pxattr,4);
3298 				showusid(pxattr,4);
3299 				showgsid(pxattr,4);
3300 				showdacl(pxattr,isdir,4);
3301 				showsacl(pxattr,isdir,4);
3302 			}
3303 			errors++;
3304 			break;
3305 		case ERRAP :
3306 			/* continued */
3307 #else /* POSIXACLS */
3308 		case ERRAM :
3309 		case ERRAP :
3310 #endif /* POSIXACLS */
3311 			printf("** wrong permission settings, "
3312 				"kind %d perm 0%03o, gotback %03o\n",
3313 				kind, perm, gotback);
3314 			if (opt_v)
3315 				hexdump(attr,ntfs_attr_size(attr),8);
3316 			if (opt_v >= 2) {
3317 				showheader(attr,4);
3318 				showusid(attr,4);
3319 				showgsid(attr,4);
3320 				showdacl(attr,isdir,4);
3321 				showsacl(attr,isdir,4);
3322 			}
3323 			errors++;
3324 			free(attr);
3325 			break;
3326 		default :
3327 			break;
3328 		}
3329 	}
3330 	printf("%lu ACLs built from mode, %lu ACE built, mean count %lu.%02lu\n",
3331 		(unsigned long)count,(unsigned long)acecount,
3332 		(unsigned long)acecount/count,acecount*100L/count%100L);
3333 	if (acecount != expectcnt[kind]) {
3334 		printf("** Error : ACE count %lu instead of %lu\n",
3335 			(unsigned long)acecount,
3336 			(unsigned long)expectcnt[kind]);
3337 		errors++;
3338 	}
3339 	if (globhash != expecthash[kind]) {
3340 		printf("** Error : wrong global hash 0x%lx instead of 0x%lx\n",
3341 			(unsigned long)globhash, (unsigned long)expecthash[kind]);
3342 		errors++;
3343 	}
3344 #if POSIXACLS
3345 	printf("%lu ACLs built from Posix ACLs, %lu ACE built, mean count %lu.%02lu\n",
3346 		(unsigned long)pxcount,(unsigned long)pxacecount,
3347 		(unsigned long)pxacecount/pxcount,pxacecount*100L/pxcount%100L);
3348 	if (pxacecount != expectcnt[kind]) {
3349 		printf("** Error : ACE count %lu instead of %lu\n",
3350 			(unsigned long)pxacecount,
3351 			(unsigned long)expectcnt[kind]);
3352 		errors++;
3353 	}
3354 	if (pxglobhash != expecthash[kind]) {
3355 		printf("** Error : wrong global hash 0x%lx instead of 0x%lx\n",
3356 			(unsigned long)pxglobhash, (unsigned long)expecthash[kind]);
3357 		errors++;
3358 	}
3359 #endif /* POSIXACLS */
3360 }
3361 
3362 #if POSIXACLS
3363 
3364 /*
3365  *		Check whether Posix ACL settings are interpreted
3366  *	back exactly as set
3367  */
3368 
posixtest(int kind,BOOL isdir,const SID * owner,const SID * group)3369 static void posixtest(int kind, BOOL isdir,
3370 			const SID *owner, const SID *group)
3371 {
3372 	struct POSIX_SECURITY *pxdesc;
3373 	struct {
3374 		struct POSIX_SECURITY pxdesc;
3375 		struct POSIX_ACE aces[10];
3376 	} desc;
3377 	int ownobj;
3378 	int grpobj;
3379 	int usr;
3380 	int grp;
3381 	int wrld;
3382 	int mask;
3383 	int mindes, maxdes;
3384 	int minmsk, maxmsk;
3385 	char *pxattr;
3386 	u32 count;
3387 	u32 acecount;
3388 	u32 globhash;
3389 	const SECURITY_DESCRIPTOR_RELATIVE *phead;
3390 	const ACL *pacl;
3391 	struct POSIX_SECURITY *gotback;
3392 	enum { ERRNO,
3393 		ERRMA, ERRPA, /* error converting mode or Posix ACL to NTFS */
3394 		ERRAM, ERRAP, /* error converting NTFS to mode or Posix ACL */
3395 	} ;
3396 	u32 expectcnt[] = {
3397 		252720, 273456,
3398 		199584, 220320,
3399 		199584, 220320,
3400 		199584, 220320,
3401 		203904, 224640,
3402 		0, 0,
3403 		0, 0,
3404 		0, 0,
3405 		196452, 217188,
3406 		165888, 186624,
3407 		165888, 186624,
3408 		165888, 186624,
3409 		168480, 189216,
3410 		0, 0,
3411 		0, 0,
3412 		0, 0,
3413 		16368, 18672,
3414 		0, 0,
3415 		13824, 0,
3416 		0, 0,
3417 		14640, 0
3418 	} ;
3419 	u32 expecthash[] = {
3420 		0x1808a6cd, 0xd82f7c60,
3421 		0x5ad29e85, 0x518c7620,
3422 		0x188ce270, 0x7e44e590,
3423 		0x48a64800, 0x5bdf0030,
3424 		0x1c64aec6, 0x8b0168fa,
3425 		0, 0,
3426 		0, 0,
3427 		0, 0,
3428 		0x169fb80e, 0x382d9a59,
3429 		0xf9c28164, 0x1855d352,
3430 		0xf9685700, 0x44d16700,
3431 		0x587ebe90, 0xf7c51480,
3432 		0x2cb1b518, 0x52408df6,
3433 		0, 0,
3434 		0, 0,
3435 		0, 0,
3436 		0x905f2e38, 0xd40c22f0,
3437 		0, 0,
3438 		0xdd76da00, 0,
3439 		0, 0,
3440 		0x718e34a0, 0
3441 	};
3442 
3443 	count = 0;
3444 	acecount = 0;
3445 	globhash = 0;
3446 				/* fill headers */
3447 	pxdesc = &desc.pxdesc;
3448 	pxdesc->mode = 0;
3449 	pxdesc->defcnt = 0;
3450 	if (kind & 32) {
3451 		pxdesc->acccnt = 4;
3452 		pxdesc->firstdef = 4;
3453 		pxdesc->tagsset = 0x35;
3454 	} else {
3455 		pxdesc->acccnt = 6;;
3456 		pxdesc->firstdef = 6;
3457 		pxdesc->tagsset = 0x3f;
3458 	}
3459 	pxdesc->acl.version = POSIX_VERSION;
3460 	pxdesc->acl.flags = 0;
3461 	pxdesc->acl.filler = 0;
3462 				/* prefill aces */
3463 	pxdesc->acl.ace[0].tag = POSIX_ACL_USER_OBJ;
3464 	pxdesc->acl.ace[0].id = -1;
3465 	if (kind & 32) {
3466 		pxdesc->acl.ace[1].tag = POSIX_ACL_GROUP_OBJ;
3467 		pxdesc->acl.ace[1].id = -1;
3468 		pxdesc->acl.ace[2].tag = POSIX_ACL_MASK;
3469 		pxdesc->acl.ace[2].id = -1;
3470 		pxdesc->acl.ace[3].tag = POSIX_ACL_OTHER;
3471 		pxdesc->acl.ace[3].id = -1;
3472 	} else {
3473 		pxdesc->acl.ace[1].tag = POSIX_ACL_USER;
3474 		pxdesc->acl.ace[1].id = (kind & 16 ? 0 : 1000);
3475 		pxdesc->acl.ace[2].tag = POSIX_ACL_GROUP_OBJ;
3476 		pxdesc->acl.ace[2].id = -1;
3477 		pxdesc->acl.ace[3].tag = POSIX_ACL_GROUP;
3478 		pxdesc->acl.ace[3].id = (kind & 16 ? 0 : 1002);
3479 		pxdesc->acl.ace[4].tag = POSIX_ACL_MASK;
3480 		pxdesc->acl.ace[4].id = -1;
3481 		pxdesc->acl.ace[5].tag = POSIX_ACL_OTHER;
3482 		pxdesc->acl.ace[5].id = -1;
3483 	}
3484 
3485 	mindes = 3;
3486 	maxdes = (kind & 32 ? mindes : 6);
3487 	minmsk = 0;
3488 	maxmsk = 7;
3489 	for (mask=minmsk; mask<=maxmsk; mask++)
3490 	for (ownobj=1; ownobj<7; ownobj++)
3491 	for (grpobj=1; grpobj<7; grpobj++)
3492 	for (wrld=0; wrld<8; wrld++)
3493 	for (usr=mindes; usr<=maxdes; usr++)
3494 	if (usr != 4)
3495 	for (grp=mindes; grp<=maxdes; grp++)
3496 	if (grp != 4) {
3497 		pxdesc->mode = (ownobj << 6) | (mask << 3) | wrld;
3498 
3499 		pxdesc->acl.ace[0].perms = ownobj;
3500 		if (kind & 32) {
3501 			pxdesc->acl.ace[1].perms = grpobj;
3502 			pxdesc->acl.ace[2].perms = mask;
3503 			pxdesc->acl.ace[3].perms = wrld;
3504 		} else {
3505 			pxdesc->acl.ace[1].perms = usr;
3506 			pxdesc->acl.ace[2].perms = grpobj;
3507 			pxdesc->acl.ace[3].perms = grp;
3508 			pxdesc->acl.ace[4].perms = mask;
3509 			pxdesc->acl.ace[5].perms = wrld;
3510 		}
3511 
3512 		gotback = (struct POSIX_SECURITY*)NULL;
3513 		pxattr = ntfs_build_descr_posix(context.mapping,
3514 				pxdesc,isdir,owner,group);
3515 		if (pxattr && ntfs_valid_descr(pxattr, ntfs_attr_size(pxattr))) {
3516 			phead = (const SECURITY_DESCRIPTOR_RELATIVE*)pxattr;
3517 			pacl = (const ACL*)&pxattr[le32_to_cpu(phead->dacl)];
3518 			acecount += le16_to_cpu(pacl->ace_count);
3519 			globhash += hash((const le32*)pxattr,ntfs_attr_size(pxattr));
3520 			count++;
3521 			gotback = linux_permissions_posix(pxattr, isdir);
3522 			if (gotback) {
3523 				if (ntfs_valid_posix(gotback)) {
3524 					if (!same_posix(pxdesc,gotback)) {
3525 						printf("Non matching got back Posix ACL\n");
3526 						printf("input ACL\n");
3527 						showposix(pxdesc);
3528 						printf("NTFS owner\n");
3529 						showusid(pxattr,4);
3530 						printf("NTFS group\n");
3531 						showgsid(pxattr,4);
3532 						printf("NTFS DACL\n");
3533 						showdacl(pxattr,isdir,4);
3534 						printf("gotback ACL\n");
3535 						showposix(gotback);
3536 						errors++;
3537 					}
3538 				} else {
3539 					printf("Got back an invalid Posix ACL\n");
3540 					errors++;
3541 				}
3542 				free(gotback);
3543 			} else {
3544 				printf("Could not get Posix ACL back\n");
3545 				errors++;
3546 			}
3547 
3548 		} else {
3549 			printf("NTFS ACL incorrect or not build\n");
3550 			printf("input ACL\n");
3551 			showposix(pxdesc);
3552 			printf("NTFS DACL\n");
3553 			if (pxattr)
3554 				showdacl(pxattr,isdir,4);
3555 			else
3556 				printf("   (none)\n");
3557 			if (gotback) {
3558 				printf("gotback ACL\n");
3559 				showposix(gotback);
3560 			} else
3561 				printf("no gotback ACL\n");
3562 			errors++;
3563 		}
3564 		if (pxattr)
3565 			free(pxattr);
3566 	}
3567 	printf("%lu ACLs built from Posix ACLs, %lu ACE built, mean count %lu.%02lu\n",
3568 		(unsigned long)count,(unsigned long)acecount,
3569 		(unsigned long)acecount/count,acecount*100L/count%100L);
3570 	if (acecount != expectcnt[kind]) {
3571 		printf("** Error ! expected ACE count %lu\n",
3572 			(unsigned long)expectcnt[kind]);
3573 		errors++;
3574 	}
3575 	if (globhash != expecthash[kind]) {
3576 		printf("** Error : wrong global hash 0x%lx instead of 0x%lx\n",
3577 			(unsigned long)globhash, (unsigned long)expecthash[kind]);
3578 		errors++;
3579 	}
3580 }
3581 
3582 #endif /* POSIXACLS */
3583 
selftests(void)3584 static void selftests(void)
3585 {
3586 	le32 owner_sid[] = /* S-1-5-21-3141592653-589793238-462843383-1016 */
3587 		{
3588 		cpu_to_le32(0x501), cpu_to_le32(0x05000000), cpu_to_le32(21),
3589 		cpu_to_le32(DEFSECAUTH1), cpu_to_le32(DEFSECAUTH2),
3590 		cpu_to_le32(DEFSECAUTH3), cpu_to_le32(1016)
3591 		} ;
3592 	le32 group_sid[] = /* S-1-5-21-3141592653-589793238-462843383-513 */
3593 		{
3594 		cpu_to_le32(0x501), cpu_to_le32(0x05000000), cpu_to_le32(21),
3595 		cpu_to_le32(DEFSECAUTH1), cpu_to_le32(DEFSECAUTH2),
3596 		cpu_to_le32(DEFSECAUTH3), cpu_to_le32(513)
3597 		} ;
3598 #if POSIXACLS
3599 	unsigned char kindlist[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
3600 			   16, 17, 18, 20, 22, 24, 19, 21, 23, 25,
3601 			   32, 33, 36, 40 } ;
3602 	unsigned int k;
3603 #endif /* POSIXACLS */
3604 	int kind;
3605 	const SID *owner;
3606 	const SID *group;
3607 	BOOL isdir;
3608 
3609 #if POSIXACLS
3610 	local_build_mapping(context.mapping, (const char*)NULL);
3611 #endif /* POSIXACLS */
3612 			/* first check samples */
3613 	mappingtype = MAPDUMMY;
3614 	check_samples();
3615 		/*
3616 		 * kind is oring of :
3617 		 *   1 : directory
3618 		 *   2 : owner is root
3619 		 *   4 : group is root
3620 		 *   8 : group is owner
3621 		 *  16 : root is designated user/group
3622 		 *  32 : mask present with no designated user/group
3623 		 */
3624 	for (kind=0; (kind<10) && (errors<10); kind++) {
3625 		isdir = kind & 1;
3626 		if (kind & 8)
3627 			owner = (const SID*)group_sid;
3628 		else
3629 			owner = (kind & 2 ? adminsid : (const SID*)owner_sid);
3630 		group = (kind & 4 ? adminsid : (const SID*)group_sid);
3631 		basictest(kind, isdir, owner, group);
3632 	}
3633 #if POSIXACLS
3634 	for (k=0; (k<sizeof(kindlist)) && (errors<10); k++) {
3635 		kind = kindlist[k];
3636 		isdir = kind & 1;
3637 		if (kind & 8)
3638 			owner = (const SID*)group_sid;
3639 		else
3640 			owner = (kind & 2 ? adminsid : (const SID*)owner_sid);
3641 		group = (kind & 4 ? adminsid : (const SID*)group_sid);
3642 		posixtest(kind, isdir, owner, group);
3643 	}
3644 	ntfs_free_mapping(context.mapping);
3645 #endif /* POSIXACLS */
3646 	if (errors >= 10)
3647 		printf("** too many errors, test aborted\n");
3648 }
3649 #endif /* SELFTESTS */
3650 
3651 /*
3652  *		   Get the security descriptor of a file
3653  */
3654 
getfull(char * attr,const char * fullname)3655 static unsigned int getfull(char *attr, const char *fullname)
3656 {
3657 	static char part[MAXATTRSZ];
3658 	BIGSID ownsid;
3659 	int xowner;
3660 	int ownersz;
3661 	u16 ownerfl;
3662 	u32 attrsz;
3663 	u32 partsz;
3664 	BOOL overflow;
3665 
3666 	attrsz = 0;
3667 	partsz = 0;
3668 	overflow = FALSE;
3669 	if (ntfs_get_file_security(ntfs_context,fullname,
3670 				OWNER_SECURITY_INFORMATION,
3671 				(char*)part,MAXATTRSZ,&partsz)) {
3672 		xowner = get4l(part,4);
3673 		if (xowner) {
3674 			ownerfl = get2l(part,2);
3675 			ownersz = ntfs_sid_size((SID*)&part[xowner]);
3676 			if (ownersz <= (int)sizeof(BIGSID))
3677 				memcpy(ownsid,&part[xowner],ownersz);
3678 			else
3679 				overflow = TRUE;
3680 		} else {
3681 			ownerfl = 0;
3682 			ownersz = 0;
3683 		}
3684 			/*
3685 			 *  SACL : just feed in or clean
3686 			 */
3687 		if (!ntfs_get_file_security(ntfs_context,fullname,
3688 				SACL_SECURITY_INFORMATION,
3689 				(char*)attr,MAXATTRSZ,&attrsz)) {
3690 			attrsz = 20;
3691 			set4l(attr,0);
3692 			attr[0] = SECURITY_DESCRIPTOR_REVISION;
3693 			set4l(&attr[12],0);
3694 			if (opt_v >= 2)
3695 				printf("   No SACL\n");
3696 		}
3697 			/*
3698 			 *  append DACL and merge its flags
3699 			 */
3700 		partsz = 0;
3701 		set4l(&attr[16],0);
3702 		if (ntfs_get_file_security(ntfs_context,fullname,
3703 		    DACL_SECURITY_INFORMATION,
3704 		    (char*)part,MAXATTRSZ,&partsz)) {
3705 			if ((attrsz + partsz - 20) <= MAXATTRSZ) {
3706 				memcpy(&attr[attrsz],&part[20],partsz-20);
3707 				set4l(&attr[16],(partsz > 20 ? attrsz : 0));
3708 				set2l(&attr[2],get2l(attr,2) | (get2l(part,2)
3709 					& const_le16_to_cpu(SE_DACL_PROTECTED
3710 						   | SE_DACL_AUTO_INHERITED
3711 						   | SE_DACL_PRESENT)));
3712 				attrsz += partsz - 20;
3713 			} else
3714 				overflow = TRUE;
3715 		} else
3716 			if (partsz > MAXATTRSZ)
3717 				overflow = TRUE;
3718 			else {
3719 				if (cmd == CMD_BACKUP)
3720 					printf("#   No discretionary access control list\n");
3721 				else
3722 					printf("   No discretionary access control list\n");
3723 				warnings++;
3724 			}
3725 
3726 			/*
3727 			 *  append owner and merge its flag
3728 			 */
3729 		if (xowner && !overflow) {
3730 			memcpy(&attr[attrsz],ownsid,ownersz);
3731 			set4l(&attr[4],attrsz);
3732 			set2l(&attr[2],get2l(attr,2)
3733 			   | (ownerfl & const_le16_to_cpu(SE_OWNER_DEFAULTED)));
3734 			attrsz += ownersz;
3735 		} else
3736 			set4l(&attr[4],0);
3737 			/*
3738 			 * append group
3739 			 */
3740 		partsz = 0;
3741 		set4l(&attr[8],0);
3742 		if (ntfs_get_file_security(ntfs_context,fullname,
3743 		    GROUP_SECURITY_INFORMATION,
3744 		    (char*)part,MAXATTRSZ,&partsz)) {
3745 			if ((attrsz + partsz - 20) <= MAXATTRSZ) {
3746 				memcpy(&attr[attrsz],&part[20],partsz-20);
3747 				set4l(&attr[8],(partsz > 20 ? attrsz : 0));
3748 				set2l(&attr[2],get2l(attr,2) | (get2l(part,2)
3749 					& const_le16_to_cpu(SE_GROUP_DEFAULTED)));
3750 				attrsz += partsz - 20;
3751 			} else
3752 				overflow = TRUE;
3753 		} else
3754 			if (partsz > MAXATTRSZ)
3755 				overflow = TRUE;
3756 			else {
3757 				printf("**   No group SID\n");
3758 				warnings++;
3759 			}
3760 		if (overflow) {
3761 			printf("** Descriptor was too long (> %d)\n",MAXATTRSZ);
3762 			warnings++;
3763 			attrsz = 0;
3764 		} else
3765 			if (!ntfs_valid_descr((char*)attr,attrsz)) {
3766 				printf("** Descriptor for %s is not valid\n",fullname);
3767 				errors++;
3768 				attrsz = 0;
3769 			}
3770 
3771 	} else {
3772 		printf("** Could not get owner of %s\n",fullname);
3773 		warnings++;
3774 		attrsz = 0;
3775 	}
3776 	return (attrsz);
3777 }
3778 
3779 /*
3780  *		Update a security descriptor
3781  */
3782 
updatefull(const char * name,u32 flags,char * attr)3783 static BOOL updatefull(const char *name, u32 flags, char *attr)
3784 {
3785 	BOOL err;
3786 
3787 // Why was the error not seen before ?
3788 	err = !ntfs_set_file_security(ntfs_context, name, flags, attr);
3789 	if (err) {
3790 		printf("** Could not change attributes of %s\n",name);
3791 		printerror(stdout);
3792 		errors++;
3793 	}
3794 	return (!err);
3795 }
3796 
3797 
3798 #if POSIXACLS
3799 
3800 /*
3801  *		   Set all the parameters associated to a file
3802  */
3803 
setfull_posix(const char * fullname,const struct POSIX_SECURITY * pxdesc,BOOL isdir)3804 static BOOL setfull_posix(const char *fullname, const struct POSIX_SECURITY *pxdesc,
3805 			BOOL isdir)
3806 {
3807 	static char attr[MAXATTRSZ];
3808 	struct POSIX_SECURITY *oldpxdesc;
3809 	struct POSIX_SECURITY *newpxdesc;
3810 	const SECURITY_DESCRIPTOR_RELATIVE *phead;
3811 	char *newattr;
3812 	int err;
3813 	unsigned int attrsz;
3814 	int newattrsz;
3815 	const SID *usid;
3816 	const SID *gsid;
3817 #if OWNERFROMACL
3818 	const SID *osid;
3819 #endif /* OWNERFROMACL */
3820 
3821 	printf("%s ",(isdir ? "Directory" : "File"));
3822 	printname(stdout,fullname);
3823 	if (pxdesc->acccnt)
3824 		printf("\n");
3825 	else
3826 		printf(" mode 0%03o\n",pxdesc->mode);
3827 
3828 	err = FALSE;
3829 	attrsz = getfull(attr, fullname);
3830 	if (attrsz) {
3831 		oldpxdesc = linux_permissions_posix(attr, isdir);
3832 		if (opt_v >= 2) {
3833 			printf("Posix equivalent of old ACL :\n");
3834 			showposix(oldpxdesc);
3835 		}
3836 		if (oldpxdesc) {
3837 			if (!pxdesc->defcnt
3838 			   && !(pxdesc->tagsset &
3839 			     (POSIX_ACL_USER | POSIX_ACL_GROUP | POSIX_ACL_MASK))) {
3840 				if (!ntfs_merge_mode_posix(oldpxdesc,pxdesc->mode))
3841 					newpxdesc = oldpxdesc;
3842 				else {
3843 					newpxdesc = (struct POSIX_SECURITY*)NULL;
3844 					free(oldpxdesc);
3845 				}
3846 			} else {
3847 				newpxdesc = ntfs_merge_descr_posix(pxdesc, oldpxdesc);
3848 				free(oldpxdesc);
3849 			}
3850 			if (opt_v) {
3851 				printf("New Posix ACL :\n");
3852 				showposix(newpxdesc);
3853 			}
3854 		} else
3855 			newpxdesc = (struct POSIX_SECURITY*)NULL;
3856 		if (newpxdesc) {
3857 			phead = (const SECURITY_DESCRIPTOR_RELATIVE*)attr;
3858 			gsid = (const SID*)&attr[le32_to_cpu(phead->group)];
3859 #if OWNERFROMACL
3860 			osid = (const SID*)&attr[le32_to_cpu(phead->owner)];
3861 			usid = ntfs_acl_owner((const char*)attr);
3862 			if (!ntfs_same_sid(usid,osid))
3863 				printf("== Windows owner might change\n");
3864 #else /* OWNERFROMACL */
3865 			usid = (const SID*)&attr[le32_to_cpu(phead->owner)];
3866 #endif /* OWNERFROMACL */
3867 			if (mappingtype == MAPEXTERN)
3868 				newattr = ntfs_build_descr_posix(
3869 					ntfs_context->security.mapping,
3870 					newpxdesc,isdir,usid,gsid);
3871 			else
3872 				newattr = ntfs_build_descr_posix(
3873 					context.mapping,
3874 					newpxdesc,isdir,usid,gsid);
3875 			free(newpxdesc);
3876 		} else
3877 			newattr = (char*)NULL;
3878 		if (newattr) {
3879 			newattrsz = ntfs_attr_size(newattr);
3880 			if (opt_v) {
3881 				printf("New NTFS security descriptor\n");
3882 				hexdump(newattr,newattrsz,4);
3883 			}
3884 			if (opt_v >= 2) {
3885 				printf("Expected hash : 0x%08lx\n",
3886 					(unsigned long)hash((le32*)newattr,ntfs_attr_size(newattr)));
3887 				showheader(newattr,0);
3888 				showusid(newattr,0);
3889 				showgsid(newattr,0);
3890 				showdacl(newattr,isdir,0);
3891 				showsacl(newattr,isdir,0);
3892 			}
3893 
3894 			if (!updatefull(fullname,
3895 				DACL_SECURITY_INFORMATION
3896 				| GROUP_SECURITY_INFORMATION
3897 				| OWNER_SECURITY_INFORMATION,
3898 					newattr))
3899 				err = TRUE;
3900 /*
3901 {
3902 struct POSIX_SECURITY *interp;
3903 printf("Reinterpreted new Posix :\n");
3904 interp = linux_permissions_posix(newattr,isdir);
3905 showposix(interp);
3906 free(interp);
3907 }
3908 */
3909 			free(newattr);
3910 		} else
3911 			err = TRUE;
3912 	} else
3913 		err = TRUE;
3914 	return (!err);
3915 }
3916 
3917 #else /* POSIXACLS */
3918 
setfull(const char * fullname,int mode,BOOL isdir)3919 static BOOL setfull(const char *fullname, int mode, BOOL isdir)
3920 {
3921 	static char attr[MAXATTRSZ];
3922 	const SECURITY_DESCRIPTOR_RELATIVE *phead;
3923 	char *newattr;
3924 	int err;
3925 	unsigned int attrsz;
3926 	int newattrsz;
3927 	const SID *usid;
3928 	const SID *gsid;
3929 #if OWNERFROMACL
3930 	const SID *osid;
3931 #endif /* OWNERFROMACL */
3932 
3933 	printf("%s ",(isdir ? "Directory" : "File"));
3934 	printname(stdout,fullname);
3935 	printf(" mode 0%03o\n",mode);
3936 	attrsz = getfull(attr, fullname);
3937 	err = FALSE;
3938 	if (attrsz) {
3939 		phead = (const SECURITY_DESCRIPTOR_RELATIVE*)attr;
3940 		gsid = (const SID*)&attr[le32_to_cpu(phead->group)];
3941 #if OWNERFROMACL
3942 		osid = (const SID*)&attr[le32_to_cpu(phead->owner)];
3943 		usid = ntfs_acl_owner((const char*)attr);
3944 		if (!ntfs_same_sid(usid,osid))
3945 			printf("== Windows owner might change\n");
3946 #else /* OWNERFROMACL */
3947 		usid = (const SID*)&attr[le32_to_cpu(phead->owner)];
3948 #endif /* OWNERFROMACL */
3949 		newattr = ntfs_build_descr(mode,isdir,usid,gsid);
3950 		if (newattr) {
3951 			newattrsz = ntfs_attr_size(newattr);
3952 			if (opt_v) {
3953 				printf("Security descriptor\n");
3954 				hexdump(newattr,newattrsz,4);
3955 			}
3956 			if (opt_v >= 2) {
3957 				printf("Expected hash : 0x%08lx\n",
3958 					(unsigned long)hash((le32*)newattr,ntfs_attr_size(newattr)));
3959 				showheader(newattr,0);
3960 				showusid(newattr,0);
3961 				showgsid(newattr,0);
3962 				showdacl(newattr,isdir,0);
3963 				showsacl(newattr,isdir,0);
3964 			}
3965 
3966 			if (!updatefull(fullname,
3967 				DACL_SECURITY_INFORMATION
3968 				| GROUP_SECURITY_INFORMATION
3969 				| OWNER_SECURITY_INFORMATION,
3970 					newattr))
3971 				err = TRUE;
3972 			free(newattr);
3973 		}
3974 
3975 	} else
3976 		err = TRUE;
3977 	return (err);
3978 }
3979 
3980 #endif /* POSIXACLS */
3981 
proposal(const char * name,const char * attr)3982 static BOOL proposal(const char *name, const char *attr)
3983 {
3984 	char fullname[MAXFILENAME];
3985 	int uoff, goff;
3986 	int i;
3987 	u64 uauth, gauth;
3988 	int ucnt, gcnt;
3989 	int uid, gid;
3990 	BOOL err;
3991 #ifdef HAVE_WINDOWS_H
3992 	char driveletter;
3993 #else /* HAVE_WINDOWS_H */
3994 	struct stat st;
3995 	char *p,*q;
3996 #endif /* HAVE_WINDOWS_H */
3997 
3998 	err = FALSE;
3999 #ifdef HAVE_WINDOWS_H
4000 	uid = gid = 0;
4001 #else /* HAVE_WINDOWS_H */
4002 	uid = getuid();
4003 	gid = getgid();
4004 #endif /* HAVE_WINDOWS_H */
4005 	uoff = get4l(attr,4);
4006 	uauth = get6h(attr,uoff+2);
4007 	ucnt = attr[uoff+1] & 255;
4008 	goff = get4l(attr,8);
4009 	gauth = get6h(attr,goff+2);
4010 	gcnt = attr[goff+1] & 255;
4011 
4012 	if ((ucnt == 5) && (gcnt == 5)
4013 	    && (uauth == 5) && (gauth == 5)
4014 	    && (get4l(attr,uoff+8) == 21) && (get4l(attr,goff+8) == 21)) {
4015 		printf("# User mapping proposal :\n");
4016 		printf("# -------------------- cut here -------------------\n");
4017 		if (uid)
4018 			printf("%d::",uid);
4019 		else
4020 			printf("user::");
4021 		printf("S-%d-%llu",attr[uoff] & 255,(long long)uauth);
4022 		for (i=0; i<ucnt; i++)
4023 			printf("-%lu",get4l(attr,uoff+8+4*i));
4024 		printf("\n");
4025 		if (gid)
4026 			printf(":%d:",gid);
4027 		else
4028 			printf(":group:");
4029 		printf("S-%d-%llu",attr[goff] & 255,(long long)gauth);
4030 		for (i=0; i<gcnt; i++)
4031 			printf("-%lu",get4l(attr,goff+8+4*i));
4032 		printf("\n");
4033 			/* generic rule, based on group */
4034 		printf("::S-%d-%llu",attr[goff] & 255,(long long)gauth);
4035 		for (i=0; i<gcnt-1; i++)
4036 			printf("-%lu",get4l(attr,goff+8+4*i));
4037 		printf("-10000\n");
4038 		printf("# -------------------- cut here -------------------\n");
4039 		if (!uid || !gid) {
4040 			printf("# Please replace \"user\" and \"group\" above by the uid\n");
4041 			printf("# and gid of the Linux owner and group of ");
4042 			printname(stdout,name);
4043 			printf(", then\n");
4044 			printf("# insert the modified lines into .NTFS-3G/UserMapping, with .NTFS-3G\n");
4045 		} else
4046 			printf("# Insert the above lines into .NTFS-3G/UserMapping, with .NTFS-3G\n");
4047 #ifdef HAVE_WINDOWS_H
4048 		printf("# being a directory of the root of the NTFS file system.\n");
4049 
4050 		/* Get the drive letter to the file system */
4051 		driveletter = 0;
4052 		if ((((name[0] >= 'a') && (name[0] <= 'z'))
4053 			|| ((name[0] >= 'A') && (name[0] <= 'Z')))
4054 		    && (name[1] == ':'))
4055 			driveletter = name[0];
4056 		else {
4057 			if (getcwd(fullname, MAXFILENAME)
4058 					&& (fullname[1] == ':'))
4059 				driveletter = fullname[0];
4060 		}
4061 		if (driveletter) {
4062 			printf("# Example : %c:\\.NTFS-3G\\UserMapping\n",
4063 				driveletter);
4064 		}
4065 #else /* HAVE_WINDOWS_H */
4066 		printf("# being a hidden subdirectory of the root of the NTFS file system.\n");
4067 
4068 		/* Get the path to the root of the file system */
4069 /*
4070 		if (name[0] != '/') {
4071 			p = getcwd(fullname,MAXFILENAME);
4072 			if (p) {
4073 				strcat(fullname,"/");
4074 				strcat(fullname,name);
4075 			}
4076 		} else {
4077 			strcpy(fullname,name);
4078 			p = fullname;
4079 		}
4080 */
4081 		p = ntfs_realpath(name, fullname);
4082 		if (p) {
4083 			/* go down the path to inode 5 */
4084 			do {
4085 				lstat(fullname,&st);
4086 				q = strrchr(p,'/');
4087 				if (q && (st.st_ino != 5))
4088 					*q = 0;
4089 			} while (strchr(p,'/') && (st.st_ino != 5));
4090 		}
4091 		if (p && (st.st_ino == 5)) {
4092 			printf("# Example : ");
4093 			printname(stdout,p);
4094 			printf("/.NTFS-3G/UserMapping\n");
4095 		}
4096 #endif /* HAVE_WINDOWS_H */
4097 	} else {
4098 		printf("** Not possible : ");
4099 		printname(stdout,name);
4100 		printf(" was not created by a Windows user\n");
4101 		err = TRUE;
4102 	}
4103 	return (err);
4104 }
4105 
4106 /*
4107  *		   Display all the parameters associated to a file
4108  */
4109 
showfull(const char * fullname,BOOL isdir)4110 static void showfull(const char *fullname, BOOL isdir)
4111 {
4112 	static char attr[MAXATTRSZ];
4113 	static char part[MAXATTRSZ];
4114 #if POSIXACLS
4115 	struct POSIX_SECURITY *pxdesc;
4116 #endif /* POSIXACLS */
4117 	struct SECURITY_DATA *psecurdata;
4118 	char *newattr;
4119 	int securindex;
4120 	int mode;
4121 	int level;
4122 	int attrib;
4123 	u32 attrsz;
4124 	u32 partsz;
4125 	uid_t uid;
4126 	gid_t gid;
4127 
4128 	if (opt_v || (cmd == CMD_BACKUP)) {
4129 		printf("%s ",(isdir ? "Directory" : "File"));
4130 		printname(stdout, fullname);
4131 		printf("\n");
4132 	}
4133 
4134        /* get individual parameters, as when trying to get them */
4135        /* all, and one (typically SACL) is missing, we get none */
4136        /* and concatenate them, to be able to compute the checksum */
4137 
4138 	partsz = 0;
4139 	securindex = ntfs_get_file_security(ntfs_context,fullname,
4140 				OWNER_SECURITY_INFORMATION,
4141 				(char*)part,MAXATTRSZ,&partsz);
4142 
4143 	attrib = ntfs_get_file_attributes(ntfs_context, fullname);
4144 	if (attrib == INVALID_FILE_ATTRIBUTES) {
4145 		printf("** Could not get file attrib\n");
4146 		errors++;
4147 	}
4148 	if ((securindex < 0)
4149 	    || (securindex >= MAXSECURID)
4150 	    || ((securindex > 0)
4151 		&& ((!opt_r && (cmd != CMD_BACKUP))
4152 		   || !securdata[securindex >> SECBLKSZ]
4153 		   || !securdata[securindex >> SECBLKSZ][securindex & ((1 << SECBLKSZ) - 1)].filecount)))
4154 		{
4155 		if (opt_v || (cmd == CMD_BACKUP)) {
4156 			if ((securindex < -1) || (securindex >= MAXSECURID))
4157 				printf("Security key : 0x%x out of range\n",securindex);
4158 			else
4159 				if (securindex == -1)
4160 					printf("Security key : none\n");
4161 				else
4162 					printf("Security key : 0x%x\n",securindex);
4163 		} else {
4164 			printf("%s ",(isdir ? "Directory" : "File"));
4165 			printname(stdout, fullname);
4166 			if ((securindex < -1) || (securindex >= MAXSECURID))
4167 				printf(" : key 0x%x out of range\n",securindex);
4168 			else
4169 				if (securindex == -1)
4170 					printf(" : no key\n");
4171 				else
4172 					printf(" : key 0x%x\n",securindex);
4173 		}
4174 
4175 		attrsz = getfull(attr, fullname);
4176 		if (attrsz) {
4177 			psecurdata = (struct SECURITY_DATA*)NULL;
4178 			if ((securindex < MAXSECURID) && (securindex > 0)) {
4179 				if (!securdata[securindex >> SECBLKSZ])
4180 					newblock(securindex);
4181 				if (securdata[securindex >> SECBLKSZ])
4182 					psecurdata = &securdata[securindex >> SECBLKSZ]
4183 					   [securindex & ((1 << SECBLKSZ) - 1)];
4184 			}
4185 			if (((cmd == CMD_AUDIT) || (cmd == CMD_BACKUP))
4186 			    && opt_v && psecurdata) {
4187 				newattr = (char*)malloc(attrsz);
4188 				printf("# %s ",(isdir ? "Directory" : "File"));
4189 				printname(stdout, fullname);
4190 				printf(" hash 0x%lx\n",
4191 					(unsigned long)hash((le32*)attr,attrsz));
4192 				if (newattr) {
4193 					memcpy(newattr,attr,attrsz);
4194 					psecurdata->attr = newattr;
4195 				}
4196 			}
4197 			if ((opt_v || (cmd == CMD_BACKUP))
4198 				&& ((securindex >= MAXSECURID)
4199 				   || (securindex <= 0)
4200 				   || !psecurdata
4201 				   || (!psecurdata->filecount
4202 					&& !psecurdata->flags))) {
4203 				hexdump(attr,attrsz,8);
4204 				printf("Computed hash : 0x%08lx\n",
4205 					(unsigned long)hash((le32*)attr,attrsz));
4206 			}
4207 			if (ntfs_valid_descr((char*)attr,attrsz)) {
4208 #if POSIXACLS
4209 				pxdesc = linux_permissions_posix(attr,isdir);
4210 				if (pxdesc)
4211 					mode = pxdesc->mode;
4212 				else
4213 					mode = 0;
4214 #else /* POSIXACLS */
4215 				mode = linux_permissions(attr,isdir);
4216 #endif /* POSIXACLS */
4217 				attrib = ntfs_get_file_attributes(ntfs_context,fullname);
4218 				if (opt_v >= 2) {
4219 					level = (cmd == CMD_BACKUP ? 4 : 0);
4220 					showheader(attr,level);
4221 					showusid(attr,level);
4222 					showgsid(attr,level);
4223 					showdacl(attr,isdir,level);
4224 					showsacl(attr,isdir,level);
4225 				}
4226 				if (attrib != INVALID_FILE_ATTRIBUTES)
4227 					printf("Windows attrib : 0x%x\n",attrib);
4228 				uid = linux_owner(attr);
4229 				gid = linux_group(attr);
4230 				if (cmd == CMD_BACKUP) {
4231 				        showownership(attr);
4232 					printf("# Interpreted Unix owner %d, group %d, mode 0%03o\n",
4233 						(int)uid,(int)gid,mode);
4234 				} else {
4235 				        showownership(attr);
4236 					printf("Interpreted Unix owner %d, group %d, mode 0%03o\n",
4237 						(int)uid,(int)gid,mode);
4238 				}
4239 #if POSIXACLS
4240 				if (pxdesc) {
4241 					if ((cmd != CMD_BACKUP)
4242 					    && (pxdesc->defcnt
4243 					       || (pxdesc->tagsset
4244 						   & (POSIX_ACL_USER
4245 							| POSIX_ACL_GROUP
4246 							| POSIX_ACL_MASK))))
4247 						showposix(pxdesc);
4248 					free(pxdesc);
4249 				}
4250 #endif /* POSIXACLS */
4251 				if ((opt_r || (cmd == CMD_BACKUP))
4252 				    && (securindex < MAXSECURID)
4253 				    && (securindex > 0) && psecurdata) {
4254 					psecurdata->filecount++;
4255 					psecurdata->mode = mode;
4256 				}
4257 			} else {
4258 				printf("** Descriptor fails sanity check\n");
4259 				errors++;
4260 			}
4261 		}
4262 	} else
4263 		if (securindex > 0) {
4264 			if (securdata[securindex >> SECBLKSZ]) {
4265 				psecurdata = &securdata[securindex >> SECBLKSZ]
4266 					[securindex & ((1 << SECBLKSZ) - 1)];
4267 				psecurdata->filecount++;
4268 				if ((cmd == CMD_BACKUP) || opt_r) {
4269 					if ((cmd != CMD_BACKUP) && !opt_v) {
4270 						printf("%s ",(isdir ? "Directory" : "File"));
4271 						printname(stdout,fullname);
4272 						printf("\n");
4273 					}
4274 					printf("Security key : 0x%x mode %03o (already displayed)\n",
4275 						securindex,psecurdata->mode);
4276 					if (attrib != INVALID_FILE_ATTRIBUTES)
4277 						printf("Windows attrib : 0x%x\n",attrib);
4278 				} else {
4279 					printf("%s ",(isdir ? "Directory" : "File"));
4280 					printname(stdout,fullname);
4281 					printf(" : key 0x%x\n",securindex);
4282 				}
4283 				if (((cmd == CMD_AUDIT) || (cmd == CMD_BACKUP))
4284 				    && opt_v
4285 				    && psecurdata
4286 				    && psecurdata->attr) {
4287 					printf("# %s ",(isdir ? "Directory" : "File"));
4288 					printname(stdout,fullname);
4289 					printf(" hash 0x%lx\n",
4290 						(unsigned long)hash((le32*)psecurdata->attr,
4291 							ntfs_attr_size(psecurdata->attr)));
4292 				}
4293 			}
4294 		} else {
4295 			if (!opt_v && (cmd != CMD_BACKUP)) {
4296 				printf("%s ",(isdir ? "Directory" : "File"));
4297 				printname(stdout, fullname);
4298 			}
4299 			printf("   (Failed)\n");
4300 			printf("** Could not get security data of ");
4301 			printname(stdout, fullname);
4302 			printf(", partsz %d\n", partsz);
4303 			printerror(stdout);
4304 			errors++;
4305 		}
4306 }
4307 
recurseshow(const char * path)4308 static BOOL recurseshow(const char *path)
4309 {
4310 	struct CALLBACK dircontext;
4311 	struct LINK *current;
4312 	BOOL isdir;
4313 	BOOL err;
4314 
4315 	err = FALSE;
4316 	dircontext.head = (struct LINK*)NULL;
4317 	dircontext.dir = path;
4318 	isdir = ntfs_read_directory(ntfs_context, path,
4319 			callback, &dircontext);
4320 	if (isdir) {
4321 		showfull(path,TRUE);
4322 		if (opt_v) {
4323 			if (cmd == CMD_BACKUP)
4324 				printf("#\n#\n");
4325 			else
4326 				printf("\n\n");
4327 		}
4328 		while (dircontext.head) {
4329 			current = dircontext.head;
4330 			if (recurseshow(current->name)) err = TRUE;
4331 			dircontext.head = dircontext.head->next;
4332 			free(current);
4333 		}
4334 	} else
4335 		if (errno == ENOTDIR) {
4336 			showfull(path,FALSE);
4337 			if (opt_v) {
4338 				if (cmd == CMD_BACKUP)
4339 					printf("#\n#\n");
4340 				else
4341 					printf("\n\n");
4342 			}
4343 		} else {
4344 			printf("** Could not access %s\n",path);
4345 			printerror(stdout);
4346 			errors++;
4347 			err = TRUE;
4348 		}
4349 	return (!err);
4350 }
4351 
4352 
singleshow(const char * path)4353 static BOOL singleshow(const char *path)
4354 {
4355 	BOOL isdir;
4356 	BOOL err;
4357 
4358 	err = FALSE;
4359 	isdir = ntfs_read_directory(ntfs_context, path,
4360 			callback, (struct CALLBACK*)NULL);
4361 	if (isdir || (errno == ENOTDIR))
4362 		showfull(path,isdir);
4363 	else {
4364 		printf("** Could not access %s\n",path);
4365 		printerror(stdout);
4366 		errors++;
4367 		err = TRUE;
4368 	}
4369 	return (err);
4370 }
4371 
4372 #ifndef HAVE_WINDOWS_H
4373 
4374 #ifdef HAVE_SETXATTR
4375 
ntfs_getxattr(const char * path,const char * name,void * value,size_t size)4376 static ssize_t ntfs_getxattr(const char *path, const char *name, void *value, size_t size)
4377 {
4378 #if defined(__APPLE__) || defined(__DARWIN__)
4379     return getxattr(path, name, value, size, 0, 0);
4380 #else /* defined(__APPLE__) || defined(__DARWIN__) */
4381     return getxattr(path, name, value, size);
4382 #endif /* defined(__APPLE__) || defined(__DARWIN__) */
4383 }
4384 
4385 /*
4386  *		   Display all the parameters associated to a mounted file
4387  *
4388  *	(Unix only)
4389  */
4390 
showmounted(const char * fullname)4391 static BOOL showmounted(const char *fullname)
4392 {
4393 
4394 	static char attr[MAXATTRSZ];
4395 	struct stat st;
4396 #if POSIXACLS
4397 	struct POSIX_SECURITY *pxdesc;
4398 #endif /* POSIXACLS */
4399 	BOOL mapped;
4400 	int attrsz;
4401 	int mode;
4402 	uid_t uid;
4403 	gid_t gid;
4404 	u32 attrib;
4405 	int level;
4406 	BOOL isdir;
4407 	BOOL err;
4408 
4409 	err = FALSE;
4410 	if (!stat(fullname,&st)) {
4411 		isdir = S_ISDIR(st.st_mode);
4412 		printf("%s ",(isdir ? "Directory" : "File"));
4413 		printname(stdout,fullname);
4414 		printf("\n");
4415 
4416 		attrsz = ntfs_getxattr(fullname,"system.ntfs_acl",attr,MAXATTRSZ);
4417 		if (attrsz > 0) {
4418 			if (opt_v) {
4419 				hexdump(attr,attrsz,8);
4420 				printf("Computed hash : 0x%08lx\n",
4421 					(unsigned long)hash((le32*)attr,attrsz));
4422 			}
4423 			if (ntfs_getxattr(fullname,"system.ntfs_attrib",&attrib,4) != 4) {
4424 				printf("** Could not get file attrib\n");
4425 				errors++;
4426 			} else
4427 				printf("Windows attrib : 0x%x\n",(int)attrib);
4428 			if (ntfs_valid_descr(attr,attrsz)) {
4429 				mapped = !local_build_mapping(context.mapping,fullname);
4430 #if POSIXACLS
4431 				if (mapped) {
4432 					pxdesc = linux_permissions_posix(attr,isdir);
4433 					if (pxdesc)
4434 						mode = pxdesc->mode;
4435 					else
4436 						mode = 0;
4437 				} else {
4438 					pxdesc = (struct POSIX_SECURITY*)NULL;
4439 					mode = linux_permissions(attr,isdir);
4440 					printf("No user mapping : "
4441 						"cannot display the Posix ACL\n");
4442 				}
4443 #else /* POSIXACLS */
4444 				mode = linux_permissions(attr,isdir);
4445 #endif /* POSIXACLS */
4446 				if (opt_v >= 2) {
4447 					level = (cmd == CMD_BACKUP ? 4 : 0);
4448 					showheader(attr,level);
4449 					showusid(attr,level);
4450 					showgsid(attr,level);
4451 					showdacl(attr,isdir,level);
4452 					showsacl(attr,isdir,level);
4453 				}
4454 			        showownership(attr);
4455 				if (mapped) {
4456 					uid = linux_owner(attr);
4457 					gid = linux_group(attr);
4458 					printf("Interpreted Unix owner %d, group %d, mode 0%03o\n",
4459 						(int)uid,(int)gid,mode);
4460 				} else {
4461 					printf("Interpreted Unix mode 0%03o (owner and group are unmapped)\n",
4462 						mode);
4463 				}
4464 #if POSIXACLS
4465 				if (pxdesc) {
4466 					if ((pxdesc->defcnt
4467 						|| (pxdesc->tagsset
4468 						    & (POSIX_ACL_USER
4469 							| POSIX_ACL_GROUP
4470 							| POSIX_ACL_MASK))))
4471 						showposix(pxdesc);
4472 					free(pxdesc);
4473 				}
4474 				if (mapped)
4475 					ntfs_free_mapping(context.mapping);
4476 #endif /* POSIXACLS */
4477 			} else {
4478 				printf("Descriptor fails sanity check\n");
4479 				errors++;
4480 			}
4481 		} else {
4482 			printf("** Could not get the NTFS ACL, check whether file is on NTFS\n");
4483 			errors++;
4484 		}
4485 	} else {
4486 		printf("%s not found\n",fullname);
4487 		err = TRUE;
4488 	}
4489 	return (err);
4490 }
4491 
processmounted(const char * fullname)4492 static BOOL processmounted(const char *fullname)
4493 {
4494 
4495 	static char attr[MAXATTRSZ];
4496 	struct stat st;
4497 	int attrsz;
4498 	BOOL err;
4499 
4500 	err = FALSE;
4501 	if (cmd != CMD_USERMAP)
4502 		err = showmounted(fullname);
4503 	else
4504 	if (!stat(fullname,&st)) {
4505 		attrsz = ntfs_getxattr(fullname,"system.ntfs_acl",attr,MAXATTRSZ);
4506 		if (attrsz > 0) {
4507 			if (opt_v) {
4508 				hexdump(attr,attrsz,8);
4509 				printf("Computed hash : 0x%08lx\n",
4510 					(unsigned long)hash((le32*)attr,attrsz));
4511 			}
4512 			if (ntfs_valid_descr(attr,attrsz)) {
4513 				err = proposal(fullname, attr);
4514 			} else {
4515 				printf("*** Descriptor fails sanity check\n");
4516 				errors++;
4517 			}
4518 		} else {
4519 			printf("** Could not get the NTFS ACL, check whether file is on NTFS\n");
4520 			errors++;
4521 		}
4522 	} else {
4523 		printf("%s not found\n",fullname);
4524 		err = TRUE;
4525 	}
4526 	return (err);
4527 }
4528 
4529 #else /* HAVE_SETXATTR */
4530 
processmounted(const char * fullname)4531 static BOOL processmounted(const char *fullname __attribute__((unused)))
4532 {
4533 	fprintf(stderr,"Not possible on this configuration,\n");
4534 	fprintf(stderr,"you have to use an unmounted partition\n");
4535 	return (TRUE);
4536 }
4537 
4538 #endif /* HAVE_SETXATTR */
4539 
4540 #endif /* HAVE_WINDOWS_H */
4541 
4542 #if POSIXACLS
4543 
recurseset_posix(const char * path,const struct POSIX_SECURITY * pxdesc)4544 static BOOL recurseset_posix(const char *path, const struct POSIX_SECURITY *pxdesc)
4545 {
4546 	struct CALLBACK dircontext;
4547 	struct LINK *current;
4548 	BOOL isdir;
4549 	BOOL err;
4550 
4551 	err = FALSE;
4552 	dircontext.head = (struct LINK*)NULL;
4553 	dircontext.dir = path;
4554 	isdir = ntfs_read_directory(ntfs_context, path,
4555 			callback, &dircontext);
4556 	if (isdir) {
4557 		err = !setfull_posix(path,pxdesc,TRUE);
4558 		if (err) {
4559 			printf("** Failed to update %s\n",path);
4560 			printerror(stdout);
4561 			errors++;
4562 		} else {
4563 			if (cmd == CMD_BACKUP)
4564 				printf("#\n#\n");
4565 			else
4566 				printf("\n\n");
4567 			while (dircontext.head) {
4568 				current = dircontext.head;
4569 				recurseset_posix(current->name,pxdesc);
4570 				dircontext.head = dircontext.head->next;
4571 				free(current);
4572 			}
4573 		}
4574 	} else
4575 		if (errno == ENOTDIR) {
4576 			err = !setfull_posix(path,pxdesc,FALSE);
4577 			if (err) {
4578 				printf("** Failed to update %s\n",path);
4579 				printerror(stdout);
4580 				errors++;
4581 			}
4582 		} else {
4583 			printf("** Could not access %s\n",path);
4584 			printerror(stdout);
4585 			errors++;
4586 			err = TRUE;
4587 		}
4588 	return (!err);
4589 }
4590 
4591 #else /* POSIXACLS */
4592 
recurseset(const char * path,int mode)4593 static BOOL recurseset(const char *path, int mode)
4594 {
4595 	struct CALLBACK dircontext;
4596 	struct LINK *current;
4597 	BOOL isdir;
4598 	BOOL err;
4599 
4600 	err = FALSE;
4601 	dircontext.head = (struct LINK*)NULL;
4602 	dircontext.dir = path;
4603 	isdir = ntfs_read_directory(ntfs_context, path,
4604 			callback, &dircontext);
4605 	if (isdir) {
4606 		setfull(path,mode,TRUE);
4607 		if (cmd == CMD_BACKUP)
4608 			printf("#\n#\n");
4609 		else
4610 			printf("\n\n");
4611 		while (dircontext.head) {
4612 			current = dircontext.head;
4613 			recurseset(current->name,mode);
4614 			dircontext.head = dircontext.head->next;
4615 			free(current);
4616 		}
4617 	} else
4618 		if (errno == ENOTDIR)
4619 			setfull(path,mode,FALSE);
4620 		else {
4621 			printf("** Could not access %s\n",path);
4622 			printerror(stdout);
4623 			errors++;
4624 			err = TRUE;
4625 		}
4626 	return (!err);
4627 }
4628 
4629 #endif /* POSIXACLS */
4630 
4631 #if POSIXACLS
4632 
singleset_posix(const char * path,const struct POSIX_SECURITY * pxdesc)4633 static BOOL singleset_posix(const char *path, const struct POSIX_SECURITY *pxdesc)
4634 {
4635 	BOOL isdir;
4636 	BOOL err;
4637 
4638 	err = FALSE;
4639 	isdir = ntfs_read_directory(ntfs_context, path,
4640 			callback, (struct CALLBACK*)NULL);
4641 	if (isdir || (errno == ENOTDIR)) {
4642 		err = !setfull_posix(path,pxdesc,isdir);
4643 		if (err) {
4644 			printf("** Failed to update %s\n",path);
4645 			printerror(stdout);
4646 			errors++;
4647 		}
4648 	} else {
4649 		printf("** Could not access %s\n",path);
4650 		printerror(stdout);
4651 		errors++;
4652 		err = TRUE;
4653 	}
4654 	return (!err);
4655 }
4656 
4657 #else /* POSIXACLS */
4658 
singleset(const char * path,int mode)4659 static BOOL singleset(const char *path, int mode)
4660 {
4661 	BOOL isdir;
4662 	BOOL err;
4663 
4664 	err = FALSE;
4665 	isdir = ntfs_read_directory(ntfs_context, path,
4666 			callback, (struct CALLBACK*)NULL);
4667 	if (isdir || (errno == ENOTDIR))
4668 		setfull(path,mode,isdir);
4669 	else {
4670 		printf("** Could not access %s\n",path);
4671 		printerror(stdout);
4672 		errors++;
4673 		err = TRUE;
4674 	}
4675 	return (!err);
4676 }
4677 
4678 #endif /* POSIXACLS */
4679 
callback(void * ctx,const ntfschar * ntfsname,const int length,const int type,const s64 pos,const MFT_REF mft_ref,const unsigned int dt_type)4680 static int callback(void *ctx, const ntfschar *ntfsname,
4681 	const int length, const int type,
4682 	const s64 pos  __attribute__((unused)),
4683 	const MFT_REF mft_ref __attribute__((unused)),
4684 	const unsigned int dt_type __attribute__((unused)))
4685 {
4686 	struct LINK *linkage;
4687 	struct CALLBACK *dircontext;
4688 	char *name;
4689 	int newlth;
4690 	int size;
4691 
4692 	dircontext = (struct CALLBACK*)ctx;
4693 	size = utf8size(ntfsname,length);
4694 	if (dircontext
4695 	    && (type != 2)     /* 2 : dos name (8+3) */
4696 	    && (size > 0)      /* chars convertible to utf8 */
4697 	    && ((length > 2)
4698 		|| (ntfsname[0] != const_cpu_to_le16('.'))
4699 		|| ((length > 1)
4700 		    && (ntfsname[1] != const_cpu_to_le16('.'))))) {
4701 		linkage = (struct LINK*)malloc(sizeof(struct LINK)
4702 				+ strlen(dircontext->dir)
4703 				+ size + 2);
4704 		if (linkage) {
4705 		/* may find ".fuse_hidden*" files */
4706 		/* recommendation is not to hide them, so that */
4707 		/* the user has a clue to delete them */
4708 			strcpy(linkage->name,dircontext->dir);
4709 			if (linkage->name[strlen(linkage->name) - 1] != '/')
4710 				strcat(linkage->name,"/");
4711 			name = &linkage->name[strlen(linkage->name)];
4712 			newlth = makeutf8(name,ntfsname,length);
4713 			name[newlth] = 0;
4714 			linkage->next = dircontext->head;
4715 			dircontext->head = linkage;
4716 		}
4717 	}
4718 	return (0);
4719 }
4720 
4721 /*
4722  *		 Backup security descriptors in a directory tree
4723  */
4724 
backup(const char * volume,const char * root)4725 static BOOL backup(const char *volume, const char *root)
4726 {
4727 	BOOL err;
4728 	int count;
4729 	int i,j;
4730 	time_t now;
4731 	const char *txtime;
4732 
4733 	now = time((time_t*)NULL);
4734 	txtime = ctime(&now);
4735 	if (!getuid() && open_security_api()) {
4736 		if (open_volume(volume,NTFS_MNT_RDONLY)) {
4737 			printf("#\n# Recursive ACL collection on %s#\n",txtime);
4738 			err = recurseshow(root);
4739 			count = 0;
4740 			for (i=0; i<(MAXSECURID + (1 << SECBLKSZ) - 1)/(1 << SECBLKSZ); i++)
4741 				if (securdata[i])
4742 					for (j=0; j<(1 << SECBLKSZ); j++)
4743 						if (securdata[i][j].filecount) {
4744 							count++;
4745 						}
4746 			printf("# %d security keys\n",count);
4747 			close_volume(volume);
4748 		} else {
4749 			fprintf(stderr,"Could not open volume %s\n",volume);
4750 			printerror(stdout);
4751 			err = TRUE;
4752 		}
4753 		close_security_api();
4754 	} else {
4755 		if (getuid())
4756 			fprintf(stderr,"This is only possible as root\n");
4757 		else
4758 			fprintf(stderr,"Could not open security API\n");
4759 		err = TRUE;
4760 	}
4761 	return (err);
4762 }
4763 
4764 /*
4765  *		 List security descriptors in a directory tree
4766  */
4767 
listfiles(const char * volume,const char * root)4768 static BOOL listfiles(const char *volume, const char *root)
4769 {
4770 	BOOL err;
4771 	int i,j;
4772 	int count;
4773 
4774 	if (!getuid() && open_security_api()) {
4775 		if (open_volume(volume,NTFS_MNT_RDONLY)) {
4776 			if (opt_r) {
4777 				printf("\nRecursive file check\n");
4778 				err = recurseshow(root);
4779 				printf("Summary\n");
4780 				count = 0;
4781 				for (i=0; i<(MAXSECURID + (1 << SECBLKSZ) - 1)/(1 << SECBLKSZ); i++)
4782 					if (securdata[i])
4783 						for (j=0; j<(1 << SECBLKSZ); j++)
4784 							if (securdata[i][j].filecount) {
4785 								printf("Key 0x%x : %d files, mode 0%03o\n",
4786 									i*(1 << SECBLKSZ)+j,securdata[i][j].filecount,
4787 									securdata[i][j].mode);
4788 								count++;
4789 							}
4790 				printf("%d security keys\n",count);
4791 			} else
4792 				err = singleshow(root);
4793 			close_volume(volume);
4794 		} else {
4795 			err = TRUE;
4796 		}
4797 		close_security_api();
4798 	} else {
4799 		if (getuid())
4800 			fprintf(stderr,"This is only possible as root\n");
4801 		else
4802 			fprintf(stderr,"Could not open security API\n");
4803 		err = TRUE;
4804 	}
4805 	return (err);
4806 }
4807 
4808 #ifdef HAVE_WINDOWS_H
4809 
mapproposal(const char * volume,const char * name)4810 static BOOL mapproposal(const char *volume, const char *name)
4811 {
4812 	BOOL err;
4813 	u32 attrsz;
4814 	int securindex;
4815 	char attr[256]; /* header (20) and a couple of SIDs (max 68 each) */
4816 
4817 	err = FALSE;
4818 	if (!getuid() && open_security_api()) {
4819 		if (open_volume(volume,NTFS_MNT_RDONLY)) {
4820 
4821 			attrsz = 0;
4822 			securindex = ntfs_get_file_security(ntfs_context,name,
4823 					OWNER_SECURITY_INFORMATION
4824 					    | GROUP_SECURITY_INFORMATION,
4825 					(char*)attr,MAXATTRSZ,&attrsz);
4826 			if (securindex)
4827 				err = proposal(name,attr);
4828 			else {
4829 				fprintf(stderr,"*** Could not get the ACL of ");
4830 				printname(stderr, name);
4831 				fprintf(stderr,"\n");
4832 				printerror(stderr);
4833 				errors++;
4834 			}
4835 			close_volume(volume);
4836 		} else {
4837 			fprintf(stderr,"Could not open volume %s\n",volume);
4838 			printerror(stdout);
4839 			err = TRUE;
4840 		}
4841 		close_security_api();
4842 	} else {
4843 		if (getuid())
4844 			fprintf(stderr,"This is only possible as root\n");
4845 		else
4846 			fprintf(stderr,"Could not open security API\n");
4847 		err = TRUE;
4848 	}
4849 	return (err);
4850 }
4851 
4852 #endif
4853 
4854 /*
4855  *		Check whether a SDS entry is valid
4856  */
4857 
valid_sds(const char * attr,unsigned int offset,unsigned int entrysz,unsigned int size,u32 prevkey,BOOL second)4858 static BOOL valid_sds(const char *attr, unsigned int offset,
4859 		unsigned int entrysz, unsigned int size, u32 prevkey,
4860 		BOOL second)
4861 {
4862 	BOOL unsane;
4863 	u32 comphash;
4864 	u32 key;
4865 
4866 	unsane = FALSE;
4867 	if (!get4l(attr,0) && !get4l(attr,4)) {
4868 		printf("Entry at 0x%lx was deleted\n",(long)offset);
4869 	} else {
4870 		if ((ntfs_attr_size(&attr[20]) + 20) > entrysz) {
4871 			printf("** Entry is truncated (expected size %ld)\n",
4872 				(long)ntfs_attr_size(&attr[20] + 20));
4873 			unsane = TRUE;
4874 			errors++;
4875 		}
4876 		if ((ntfs_attr_size(&attr[20]) + 20) < entrysz) {
4877 			printf("** Extra data appended to entry (expected size %ld)\n",
4878 				(long)ntfs_attr_size(&attr[20]) + 20);
4879 			warnings++;
4880 		}
4881 		if (!unsane && !ntfs_valid_descr((const char*)&attr[20],size)) {
4882 			printf("** General sanity check has failed\n");
4883 			unsane = TRUE;
4884 			errors++;
4885 		}
4886 		if (!unsane) {
4887 			comphash = hash((const le32*)&attr[20],entrysz-20);
4888 			if ((u32)get4l(attr,0) == comphash) {
4889 				if (opt_v >= 2)
4890 					printf("Hash	 0x%08lx (correct)\n",
4891 						(unsigned long)comphash);
4892 			} else {
4893 				printf("** hash  0x%08lx (computed : 0x%08lx)\n",
4894 					(unsigned long)get4l(attr,0),
4895 					(unsigned long)comphash);
4896 				unsane = TRUE;
4897 				errors++;
4898 			}
4899 		}
4900 		if (!unsane) {
4901 			if ((second ? get8l(attr,8) + 0x40000 : get8l(attr,8)) == offset) {
4902 				if (opt_v >= 2)
4903 					printf("Offset	 0x%lx (correct)\n",(long)offset);
4904 			} else {
4905 				printf("** offset  0x%llx (expected : 0x%llx)\n",
4906 					(long long)get8l(attr,8),
4907 					(long long)(second ? get8l(attr,8) - 0x40000 : get8l(attr,8)));
4908 //				unsane = TRUE;
4909 				errors++;
4910 			}
4911 		}
4912 		if (!unsane) {
4913 			key = get4l(attr,4);
4914 			if (opt_v >= 2)
4915 				printf("Key	 0x%x\n",(int)key);
4916 			if (key) {
4917 				if (key <= prevkey) {
4918 					printf("** Unordered key 0x%lx after 0x%lx\n",
4919 						(long)key,(long)prevkey);
4920 					unsane = TRUE;
4921 					errors++;
4922 				}
4923 			}
4924 		}
4925 	}
4926 	return (!unsane);
4927 }
4928 
4929 /*
4930  *		Check whether a SDS entry is consistent with other known data
4931  *	and store current data for subsequent checks
4932  */
4933 
consist_sds(const char * attr,unsigned int offset,unsigned int entrysz,BOOL second)4934 static int consist_sds(const char *attr, unsigned int offset,
4935 		unsigned int entrysz, BOOL second)
4936 {
4937 	int errcnt;
4938 	u32 key;
4939 	u32 comphash;
4940 	struct SECURITY_DATA *psecurdata;
4941 
4942 	errcnt = 0;
4943 	key = get4l(attr,4);
4944 	if ((key > 0) && (key < MAXSECURID)) {
4945 		printf("Valid entry at 0x%lx for key 0x%lx\n",
4946 			(long)offset,(long)key);
4947 		if (!securdata[key >> SECBLKSZ])
4948 			newblock(key);
4949 		if (securdata[key >> SECBLKSZ]) {
4950 			psecurdata = &securdata[key >> SECBLKSZ][key & ((1 << SECBLKSZ) - 1)];
4951 			comphash = hash((const le32*)&attr[20],entrysz-20);
4952 			if (psecurdata->flags & INSDS1) {
4953 				if (psecurdata->hash != comphash) {
4954 					printf("** Different hash values : $SDS-1 0x%08lx $SDS-2 0x%08lx\n",
4955 						(unsigned long)psecurdata->hash,
4956 						(unsigned long)comphash);
4957 					errcnt++;
4958 					errors++;
4959 				}
4960 				if (psecurdata->offset != get8l(attr,8)) {
4961 					printf("** Different offsets : $SDS-1 0x%llx $SDS-2 0x%llx\n",
4962 						(long long)psecurdata->offset,(long long)get8l(attr,8));
4963 					errcnt++;
4964 					errors++;
4965 				}
4966 				if (psecurdata->length != get4l(attr,16)) {
4967 					printf("** Different lengths : $SDS-1 0x%lx $SDS-2 0x%lx\n",
4968 						(long)psecurdata->length,(long)get4l(attr,16));
4969 					errcnt++;
4970 					errors++;
4971 				}
4972 			} else {
4973 				if (second) {
4974 					printf("** Entry was not present in $SDS-1\n");
4975 					errcnt++;
4976 					errors++;
4977 				}
4978 				psecurdata->hash = comphash;
4979 				psecurdata->offset = get8l(attr,8);
4980 				psecurdata->length = get4l(attr,16);
4981 			}
4982 			psecurdata->flags |= (second ? INSDS2 : INSDS1);
4983 		}
4984 	} else
4985 		if (key || get4l(attr,0)) {
4986 			printf("** Security_id 0x%x out of bounds\n",key);
4987 			warnings++;
4988 		}
4989 	return (errcnt);
4990 }
4991 
4992 
4993 /*
4994  *		       Auditing of $SDS
4995  */
4996 
audit_sds(BOOL second)4997 static int audit_sds(BOOL second)
4998 {
4999 	static char attr[MAXATTRSZ + 20];
5000 	BOOL isdir;
5001 	BOOL done;
5002 	BOOL unsane;
5003 	u32 prevkey;
5004 	int errcnt;
5005 	int size;
5006 	unsigned int entrysz;
5007 	unsigned int entryalsz;
5008 	unsigned int offset;
5009 	int count;
5010 	int deleted;
5011 	int mode;
5012 
5013 	if (second)
5014 		printf("\nAuditing $SDS-2\n");
5015 	else
5016 		printf("\nAuditing $SDS-1\n");
5017 	errcnt = 0;
5018 	offset = (second ? 0x40000 : 0);
5019 	count = 0;
5020 	deleted = 0;
5021 	done = FALSE;
5022 	prevkey = 0;
5023 
5024 	  /* get size of first record */
5025 
5026 	size = ntfs_read_sds(ntfs_context,(char*)attr,20,offset);
5027 	if (size != 20) {
5028 		if ((size < 0) && (errno == ENOTSUP))
5029 			printf("** There is no $SDS-%d in this volume\n",
5030 							(second ? 2 : 1));
5031 		else {
5032 			printf("** Could not open $SDS-%d, size %d\n",
5033 							(second ? 2 : 1),size);
5034 			errors++;
5035 			errcnt++;
5036 		}
5037 	} else
5038 		do {
5039 			entrysz = get4l(attr,16);
5040 			entryalsz = ((entrysz - 1) | 15) + 1;
5041 			if (entryalsz <= (MAXATTRSZ + 20)) {
5042 				/* read next header in anticipation, to get its size */
5043 				size = ntfs_read_sds(ntfs_context,
5044 					(char*)&attr[20],entryalsz,offset + 20);
5045 				if (opt_v)
5046 					printf("\nAt offset 0x%lx got %lu bytes\n",(long)offset,(long)size);
5047 			} else {
5048 				printf("** Security attribute is too long (%ld bytes) - stopping\n",
5049 					(long)entryalsz);
5050 				errcnt++;
5051 			}
5052 			if ((entryalsz > (MAXATTRSZ + 20)) || (size < (int)(entrysz - 20)))
5053 				done = TRUE;
5054 			else {
5055 				if (opt_v) {
5056 					printf("Entry size %d bytes\n",entrysz);
5057 					hexdump(&attr[20],size,8);
5058 				}
5059 
5060 				unsane = !valid_sds(attr,offset,entrysz,
5061 					size,prevkey,second);
5062 				if (!unsane) {
5063 					if (!get4l(attr,0) && !get4l(attr,4))
5064 						deleted++;
5065 					else
5066 						count++;
5067 					errcnt += consist_sds(attr,offset,
5068 						entrysz, second);
5069 					if (opt_v >= 2) {
5070 						isdir = guess_dir(&attr[20]);
5071 						printf("Assuming %s descriptor\n",(isdir ? "directory" : "file"));
5072 						showheader(&attr[20],0);
5073 						showusid(&attr[20],0);
5074 						showgsid(&attr[20],0);
5075 						showdacl(&attr[20],isdir,0);
5076 						showsacl(&attr[20],isdir,0);
5077 						showownership(&attr[20]);
5078 						mode = linux_permissions(
5079 						    &attr[20],isdir);
5080 						printf("Interpreted Unix mode 0%03o\n",mode);
5081 					}
5082 					prevkey = get4l(attr,4);
5083 				}
5084 				if (!unsane) {
5085 					memcpy(attr,&attr[entryalsz],20);
5086 					offset += entryalsz;
5087 					if (!get4l(attr,16)
5088 					   || ((((offset - 1) | 0x3ffff) - offset + 1) < 20)) {
5089 						if (second)
5090 							offset = ((offset - 1) | 0x7ffff) + 0x40001;
5091 						else
5092 							offset = ((offset - 1) | 0x7ffff) + 1;
5093 						if (opt_v)
5094 							printf("Trying next SDS-%d block at offset 0x%lx\n",
5095 								(second ? 2 : 1), (long)offset);
5096 						size = ntfs_read_sds(ntfs_context,
5097 							(char*)attr,20,offset);
5098 						if (size != 20) {
5099 							if (opt_v)
5100 								printf("Assuming end of $SDS, got %d bytes\n",size);
5101 							done = TRUE;
5102 						}
5103 					}
5104 				} else {
5105 					printf("** Sanity check failed - stopping there\n");
5106 					errcnt++;
5107 					errors++;
5108 					done = TRUE;
5109 				}
5110 			}
5111 		} while (!done);
5112 	if (count || deleted || errcnt) {
5113 		printf("%d valid and %d deleted entries in $SDS-%d\n",
5114 				count,deleted,(second ? 2 : 1));
5115 		printf("%d errors in $SDS-%c\n",errcnt,(second ? '2' : '1'));
5116 	}
5117 	return (errcnt);
5118 }
5119 
5120 /*
5121  *		Check whether a SII entry is sane
5122  */
5123 
valid_sii(const char * entry,u32 prevkey)5124 static BOOL valid_sii(const char *entry, u32 prevkey)
5125 {
5126 	BOOL valid;
5127 	u32 key;
5128 
5129 	valid = TRUE;
5130 	key = get4l(entry,16);
5131 	if (key <= prevkey) {
5132 		printf("** Unordered key 0x%lx after 0x%lx\n",
5133 			(long)key,(long)prevkey);
5134 		valid = FALSE;
5135 		errors++;
5136 	}
5137 	prevkey = key;
5138 	if (get2l(entry,0) != 20) {
5139 		printf("** offset %d (instead of 20)\n",(int)get2l(entry,0));
5140 		valid = FALSE;
5141 		errors++;
5142 	}
5143 	if (get2l(entry,2) != 20) {
5144 		printf("** size %d (instead of 20)\n",(int)get2l(entry,2));
5145 		valid = FALSE;
5146 		errors++;
5147 	}
5148 	if (get4l(entry,4) != 0) {
5149 		printf("** fill1 %d (instead of 0)\n",(int)get4l(entry,4));
5150 		valid = FALSE;
5151 		errors++;
5152 	}
5153 	if (get2l(entry,12) & 1) {
5154 		if (get2l(entry,8) != 48) {
5155 			printf("** index size %d (instead of 48)\n",(int)get2l(entry,8));
5156 			valid = FALSE;
5157 			errors++;
5158 		}
5159 	} else
5160 		if (get2l(entry,8) != 40) {
5161 			printf("** index size %d (instead of 40)\n",(int)get2l(entry,8));
5162 			valid = FALSE;
5163 			errors++;
5164 		}
5165 	if (get2l(entry,10) != 4) {
5166 		printf("** index key size %d (instead of 4)\n",(int)get2l(entry,10));
5167 		valid = FALSE;
5168 		errors++;
5169 	}
5170 	if ((get2l(entry,12) & ~3) != 0) {
5171 		printf("** flags 0x%x (instead of < 4)\n",(int)get2l(entry,12));
5172 		valid = FALSE;
5173 		errors++;
5174 	}
5175 	if (get2l(entry,14) != 0) {
5176 		printf("** fill2 %d (instead of 0)\n",(int)get2l(entry,14));
5177 		valid = FALSE;
5178 		errors++;
5179 	}
5180 	if (get4l(entry,24) != key) {
5181 		printf("** key 0x%x (instead of 0x%x)\n",
5182 						(int)get4l(entry,24),(int)key);
5183 		valid = FALSE;
5184 		errors++;
5185 	}
5186 	return (valid);
5187 }
5188 
5189 /*
5190  *		Check whether a SII entry is consistent with other known data
5191  */
5192 
consist_sii(const char * entry)5193 static int consist_sii(const char *entry)
5194 {
5195 	int errcnt;
5196 	u32 key;
5197 	struct SECURITY_DATA *psecurdata;
5198 
5199 	errcnt = 0;
5200 	key = get4l(entry,16);
5201 	if ((key > 0) && (key < MAXSECURID)) {
5202 		printf("Valid entry for key 0x%lx\n",(long)key);
5203 		if (!securdata[key >> SECBLKSZ])
5204 			newblock(key);
5205 		if (securdata[key >> SECBLKSZ]) {
5206 			psecurdata = &securdata[key >> SECBLKSZ][key & ((1 << SECBLKSZ) - 1)];
5207 			psecurdata->flags |= INSII;
5208 			if (psecurdata->flags & (INSDS1 | INSDS2)) {
5209 				if ((u32)get4l(entry,20) != psecurdata->hash) {
5210 					printf("** hash 0x%x (instead of 0x%x)\n",
5211 						(unsigned int)get4l(entry,20),
5212 						(unsigned int)psecurdata->hash);
5213 					errors++;
5214 				}
5215 				if (get8l(entry,28) != psecurdata->offset) {
5216 					printf("** offset 0x%llx (instead of 0x%llx)\n",
5217 						(long long)get8l(entry,28),
5218 						(long long)psecurdata->offset);
5219 					errors++;
5220 				}
5221 				if (get4l(entry,36) != psecurdata->length) {
5222 					printf("** length 0x%lx (instead of %ld)\n",
5223 						(long)get4l(entry,36),
5224 						(long)psecurdata->length);
5225 					errors++;
5226 				}
5227 			} else {
5228 				printf("** Entry was not present in $SDS\n");
5229 				errors++;
5230 				psecurdata->hash = get4l(entry,20);
5231 				psecurdata->offset = get8l(entry,28);
5232 				psecurdata->length = get4l(entry,36);
5233 				if (opt_v) {
5234 					printf("   hash 0x%x\n",(unsigned int)psecurdata->hash);
5235 					printf("   offset 0x%llx\n",(long long)psecurdata->offset);
5236 					printf("   length %ld\n",(long)psecurdata->length);
5237 				}
5238 				errcnt++;
5239 			}
5240 		}
5241 	} else {
5242 		printf("** Security_id 0x%x out of bounds\n",key);
5243 		warnings++;
5244 	}
5245 	return (errcnt);
5246 }
5247 
5248 
5249 /*
5250  *		       Auditing of $SII
5251  */
5252 
audit_sii(void)5253 static int audit_sii(void)
5254 {
5255 	char *entry;
5256 	int errcnt;
5257 	u32 prevkey;
5258 	BOOL valid;
5259 	BOOL done;
5260 	int count;
5261 
5262 	printf("\nAuditing $SII\n");
5263 	errcnt = 0;
5264 	count = 0;
5265 	entry = (char*)NULL;
5266 	prevkey = 0;
5267 	done = FALSE;
5268 	do {
5269 		entry = (char*)ntfs_read_sii(ntfs_context,(INDEX_ENTRY*)entry);
5270 		if (entry) {
5271 			valid = valid_sii(entry,prevkey);
5272 			if (valid) {
5273 				count++;
5274 				errcnt += consist_sii(entry);
5275 				prevkey = get4l(entry,16);
5276 			} else
5277 				errcnt++;
5278 		} else
5279 			if ((errno == ENOTSUP) && !prevkey)
5280 				printf("** There is no $SII in this volume\n");
5281 	} while (entry && !done);
5282 	if (count || errcnt) {
5283 		printf("%d valid entries in $SII\n",count);
5284 		printf("%d errors in $SII\n",errcnt);
5285 	}
5286 	return (errcnt);
5287 }
5288 
5289 /*
5290  *		Check whether a SII entry is sane
5291  */
5292 
valid_sdh(const char * entry,u32 prevkey,u32 prevhash)5293 static BOOL valid_sdh(const char *entry, u32 prevkey, u32 prevhash)
5294 {
5295 	BOOL valid;
5296 	u32 key;
5297 	u32 currhash;
5298 
5299 	valid = TRUE;
5300 	currhash = get4l(entry,16);
5301 	key = get4l(entry,20);
5302 	if ((currhash < prevhash)
5303 		|| ((currhash == prevhash) && (key <= prevkey))) {
5304 		printf("** Unordered hash and key 0x%x 0x%x after 0x%x 0x%x\n",
5305 			(unsigned int)currhash,(unsigned int)key,
5306 			(unsigned int)prevhash,(unsigned int)prevkey);
5307 		valid = FALSE;
5308 		errors++;
5309 	}
5310 	if ((opt_v >= 2) && (currhash == prevhash))
5311 		printf("Hash collision (not an error)\n");
5312 
5313 	if (get2l(entry,0) != 24) {
5314 		printf("** offset %d (instead of 24)\n",(int)get2l(entry,0));
5315 		valid = FALSE;
5316 		errors++;
5317 	}
5318 	if (get2l(entry,2) != 20) {
5319 		printf("** size %d (instead of 20)\n",(int)get2l(entry,2));
5320 		valid = FALSE;
5321 		errors++;
5322 	}
5323 	if (get4l(entry,4) != 0) {
5324 		printf("** fill1 %d (instead of 0)\n",(int)get4l(entry,4));
5325 		valid = FALSE;
5326 		errors++;
5327 	}
5328 	if (get2l(entry,12) & 1) {
5329 		if (get2l(entry,8) != 56) {
5330 			printf("** index size %d (instead of 56)\n",(int)get2l(entry,8));
5331 			valid = FALSE;
5332 			errors++;
5333 		}
5334 	} else
5335 		if (get2l(entry,8) != 48) {
5336 			printf("** index size %d (instead of 48)\n",(int)get2l(entry,8));
5337 			valid = FALSE;
5338 			errors++;
5339 		}
5340 	if (get2l(entry,10) != 8) {
5341 		printf("** index key size %d (instead of 8)\n",(int)get2l(entry,10));
5342 		valid = FALSE;
5343 		errors++;
5344 	}
5345 	if ((get2l(entry,12) & ~3) != 0) {
5346 		printf("** flags 0x%x (instead of < 4)\n",(int)get2l(entry,12));
5347 		valid = FALSE;
5348 		errors++;
5349 	}
5350 	if (get2l(entry,14) != 0) {
5351 		printf("** fill2 %d (instead of 0)\n",(int)get2l(entry,14));
5352 		valid = FALSE;
5353 		errors++;
5354 	}
5355 	if ((u32)get4l(entry,24) != currhash) {
5356 		printf("** hash 0x%x (instead of 0x%x)\n",
5357 			(unsigned int)get4l(entry,24),(unsigned int)currhash);
5358 		valid = FALSE;
5359 		errors++;
5360 	}
5361 	if (get4l(entry,28) != key) {
5362 		printf("** key 0x%x (instead of 0x%x)\n",
5363 			(int)get4l(entry,28),(int)key);
5364 		valid = FALSE;
5365 		errors++;
5366 	}
5367 	if (get4l(entry,44)
5368 		&& (get4l(entry,44) != 0x490049)) {
5369 		printf("** fill3 0x%lx (instead of 0 or 0x490049)\n",
5370 			(long)get4l(entry,44));
5371 		valid = FALSE;
5372 		errors++;
5373 	}
5374 	return (valid);
5375 }
5376 
5377 /*
5378  *		Check whether a SDH entry is consistent with other known data
5379  */
5380 
consist_sdh(const char * entry)5381 static int consist_sdh(const char *entry)
5382 {
5383 	int errcnt;
5384 	u32 key;
5385 	struct SECURITY_DATA *psecurdata;
5386 
5387 	errcnt = 0;
5388 	key = get4l(entry,20);
5389 	if ((key > 0) && (key < MAXSECURID)) {
5390 		printf("Valid entry for key 0x%lx\n",(long)key);
5391 		if (!securdata[key >> SECBLKSZ])
5392 			newblock(key);
5393 		if (securdata[key >> SECBLKSZ]) {
5394 			psecurdata = &securdata[key >> SECBLKSZ][key & ((1 << SECBLKSZ) - 1)];
5395 			psecurdata->flags |= INSDH;
5396 			if (psecurdata->flags & (INSDS1 | INSDS2 | INSII)) {
5397 				if ((u32)get4l(entry,24) != psecurdata->hash) {
5398 					printf("** hash 0x%x (instead of 0x%x)\n",
5399 						(unsigned int)get4l(entry,24),
5400 						(unsigned int)psecurdata->hash);
5401 					errors++;
5402 				}
5403 				if (get8l(entry,32) != psecurdata->offset) {
5404 					printf("** offset 0x%llx (instead of 0x%llx)\n",
5405 						(long long)get8l(entry,32),
5406 						(long long)psecurdata->offset);
5407 					errors++;
5408 				}
5409 				if (get4l(entry,40) != psecurdata->length) {
5410 					printf("** length %ld (instead of %ld)\n",
5411 						(long)get4l(entry,40),
5412 						(long)psecurdata->length);
5413 					errors++;
5414 				}
5415 			} else {
5416 				printf("** Entry was not present in $SDS nor in $SII\n");
5417 				errors++;
5418 				psecurdata->hash = get4l(entry,24);
5419 				psecurdata->offset = get8l(entry,32);
5420 				psecurdata->length = get4l(entry,40);
5421 				if (opt_v) {
5422 					printf("   offset 0x%llx\n",(long long)psecurdata->offset);
5423 					printf("   length %ld\n",(long)psecurdata->length);
5424 				}
5425 				errcnt++;
5426 			}
5427 		}
5428 	} else {
5429 		printf("** Security_id 0x%x out of bounds\n",key);
5430 		warnings++;
5431 	}
5432 	return (errcnt);
5433 }
5434 
5435 /*
5436  *		       Auditing of $SDH
5437  */
5438 
audit_sdh(void)5439 static int audit_sdh(void)
5440 {
5441 	char *entry;
5442 	int errcnt;
5443 	int count;
5444 	u32 prevkey;
5445 	u32 prevhash;
5446 	BOOL valid;
5447 	BOOL done;
5448 
5449 	printf("\nAuditing $SDH\n");
5450 	count = 0;
5451 	errcnt = 0;
5452 	prevkey = 0;
5453 	prevhash = 0;
5454 	entry = (char*)NULL;
5455 	done = FALSE;
5456 	do {
5457 		entry = (char*)ntfs_read_sdh(ntfs_context,(INDEX_ENTRY*)entry);
5458 		if (entry) {
5459 			valid = valid_sdh(entry,prevkey,prevhash);
5460 			if (valid) {
5461 				count++;
5462 				errcnt += consist_sdh(entry);
5463 				prevhash = get4l(entry,16);
5464 				prevkey = get4l(entry,20);
5465 			} else
5466 				errcnt++;
5467 		} else
5468 			if ((errno == ENOTSUP) && !prevkey)
5469 				printf("** There is no $SDH in this volume\n");
5470 	} while (entry && !done);
5471 	if (count || errcnt) {
5472 		printf("%d valid entries in $SDH\n",count);
5473 		printf("%d errors in $SDH\n",errcnt);
5474 	}
5475 	return (errcnt);
5476 }
5477 
5478 /*
5479  *		Audit summary
5480  */
5481 
audit_summary(void)5482 static void audit_summary(void)
5483 {
5484 	int count;
5485 	int flags;
5486 	int cnt;
5487 	int found;
5488 	int i,j;
5489 
5490 	count = 0;
5491 	found = 0;
5492 	if (opt_r) printf("Summary of security key use :\n");
5493 	for (i=0; i<(MAXSECURID + (1 << SECBLKSZ) - 1)/(1 << SECBLKSZ); i++)
5494 		if (securdata[i])
5495 			for (j=0; j<(1 << SECBLKSZ); j++) {
5496 				flags = securdata[i][j].flags & (INSDS1 + INSDS2 + INSII + INSDH);
5497 				if (flags) found++;
5498 				if (flags
5499 					&& (flags != (INSDS1 + INSDS2 + INSII + INSDH)))
5500 					{
5501 					if (!count && !opt_r)
5502 						printf("\n** Keys not present in all files :\n");
5503 					cnt = securdata[i][j].filecount;
5504 					if (opt_r)
5505 						printf("Key 0x%x used by %d %s, not in",
5506 							i*(1 << SECBLKSZ)+j,cnt,
5507 							(cnt > 1 ? "files" : "file"));
5508 					else
5509 						printf("Key 0x%x not in", i*(1 << SECBLKSZ)+j);
5510 					if (!(flags & INSDS1))
5511 						printf(" SDS-1");
5512 					if (!(flags & INSDS2))
5513 						printf(" SDS-2");
5514 					if (!(flags & INSII))
5515 						printf(" SII");
5516 					if (!(flags & INSDH))
5517 						printf(" SDH");
5518 					printf("\n");
5519 					count++;
5520 				} else {
5521 					cnt = securdata[i][j].filecount;
5522 					if (opt_r && cnt)
5523 						printf("Key 0x%x used by %d %s\n",
5524 							i*(1 << SECBLKSZ)+j,cnt,
5525 							(cnt > 1 ? "files" : "file"));
5526 				}
5527 			}
5528 	if (found) {
5529 		if (count)
5530 			printf("%d keys not present in all lists\n",count);
5531 		else
5532 			printf("All keys are present in all lists\n");
5533 	}
5534 }
5535 
5536 /*
5537  *		       Auditing
5538  */
5539 
audit(const char * volume)5540 static BOOL audit(const char *volume)
5541 {
5542 	BOOL err;
5543 
5544 	err = FALSE;
5545 	if (!getuid() && open_security_api()) {
5546 		if (open_volume(volume,NTFS_MNT_RDONLY)) {
5547 			if (audit_sds(FALSE)) err = TRUE;
5548 			if (audit_sds(TRUE)) err = TRUE;
5549 			if (audit_sii()) err = TRUE;
5550 			if (audit_sdh()) err = TRUE;
5551 			if (opt_r) recurseshow("/");
5552 
5553 			audit_summary();
5554 			close_volume(volume);
5555 		}
5556 		else {
5557 			fprintf(stderr,"Could not open volume %s\n",volume);
5558 			printerror(stdout);
5559 			err = TRUE;
5560 		}
5561 		close_security_api();
5562 	}
5563 	else {
5564 		if (getuid())
5565 			fprintf(stderr,"This is only possible as root\n");
5566 		else fprintf(stderr,"Could not open security API\n");
5567 		err = TRUE;
5568 	}
5569 	return (err);
5570 }
5571 
5572 #if POSIXACLS
5573 
5574 /*
5575  *		Encode a Posix ACL string
5576  *	[d:]{ugmo}:uid[:perms],...
5577  */
5578 
encode_posix_acl(const char * str)5579 static struct POSIX_SECURITY *encode_posix_acl(const char *str)
5580 {
5581 	int acccnt;
5582 	int defcnt;
5583 	int i,k,l;
5584 	int c;
5585 	s32 id;
5586 	u16 perms;
5587 	u16 apermsset;
5588 	u16 dpermsset;
5589 	u16 tag;
5590 	u16 tagsset;
5591 	mode_t mode;
5592 	BOOL defacl;
5593 	BOOL dmask;
5594 	BOOL amask;
5595 	const char *p;
5596 	struct POSIX_ACL *acl;
5597 	struct POSIX_SECURITY *pxdesc;
5598 	enum { PXBEGIN, PXTAG, PXTAG1, PXID, PXID1, PXID2,
5599 		PXPERM, PXPERM1, PXPERM2, PXOCT, PXNEXT, PXEND, PXERR
5600 	} state;
5601 
5602 				/* raw evaluation of ACE count */
5603 	p = str;
5604 	amask = FALSE;
5605 	dmask = FALSE;
5606 	if (*p == 'd') {
5607 		acccnt = 0;
5608 		defcnt = 1;
5609 	} else {
5610 		if ((*p >= '0') && (*p <= '7'))
5611 			acccnt = 0;
5612 		else
5613 			acccnt = 1;
5614 		defcnt = 0;
5615 	}
5616 	while (*p)
5617 		if (*p++ == ',') {
5618 			if (*p == 'd') {
5619 				defcnt++;
5620 				if (p[1] && (p[2] == 'm'))
5621 					dmask = TRUE;
5622 			} else {
5623 				acccnt++;
5624 				if (*p == 'm')
5625 					amask = TRUE;
5626 			}
5627 		}
5628 		/* account for an implicit mask if none defined */
5629 	if (acccnt && !amask)
5630 		acccnt++;
5631 	if (defcnt && !dmask)
5632 		defcnt++;
5633 	pxdesc = (struct POSIX_SECURITY*)malloc(sizeof(struct POSIX_SECURITY)
5634 				+ (acccnt + defcnt)*sizeof(struct POSIX_ACE));
5635 	if (pxdesc) {
5636 		pxdesc->acccnt = acccnt;
5637 		pxdesc->firstdef = acccnt;
5638 		pxdesc->defcnt = defcnt;
5639 		acl = &pxdesc->acl;
5640 		p = str;
5641 		state = PXBEGIN;
5642 		id = 0;
5643 		defacl = FALSE;
5644 		mode = 0;
5645 		apermsset = 0;
5646 		dpermsset = 0;
5647 		tag = 0;
5648 		perms = 0;
5649 		k = l = 0;
5650 		c = *p++;
5651 		while ((state != PXEND) && (state != PXERR)) {
5652 			switch (state) {
5653 			case PXBEGIN :
5654 				if (c == 'd') {
5655 					defacl = TRUE;
5656 					state = PXTAG1;
5657 					break;
5658 				} else
5659 					if ((c >= '0') && (c <= '7')) {
5660 						mode = c - '0';
5661 						state = PXOCT;
5662 						break;
5663 					}
5664 				defacl = FALSE;
5665 				/* fall through */
5666 			case PXTAG :
5667 				switch (c) {
5668 				case 'u' :
5669 					tag = POSIX_ACL_USER;
5670 					state = PXID;
5671 					break;
5672 				case 'g' :
5673 					tag = POSIX_ACL_GROUP;
5674 					state = PXID;
5675 					break;
5676 				case 'o' :
5677 					tag = POSIX_ACL_OTHER;
5678 					state = PXID;
5679 					break;
5680 				case 'm' :
5681 					tag = POSIX_ACL_MASK;
5682 					state = PXID;
5683 					break;
5684 				default :
5685 					state = PXERR;
5686 					break;
5687 				}
5688 				break;
5689 			case PXTAG1 :
5690 				if (c == ':')
5691 					state = PXTAG;
5692 				else
5693 					state = PXERR;
5694 				break;
5695 			case PXID :
5696 				if (c == ':') {
5697 					if ((tag == POSIX_ACL_OTHER)
5698 					   || (tag == POSIX_ACL_MASK))
5699 						state = PXPERM;
5700 					else
5701 						state = PXID1;
5702 				} else
5703 					state = PXERR;
5704 				break;
5705 			case PXID1 :
5706 				if ((c >= '0') && (c <= '9')) {
5707 					id = c - '0';
5708 					state = PXID2;
5709 				} else
5710 					if (c == ':') {
5711 						id = -1;
5712 						if (tag == POSIX_ACL_USER)
5713 							tag = POSIX_ACL_USER_OBJ;
5714 						if (tag == POSIX_ACL_GROUP)
5715 							tag = POSIX_ACL_GROUP_OBJ;
5716 						state = PXPERM1;
5717 					} else
5718 						state = PXERR;
5719 				break;
5720 			case PXID2 :
5721 				if ((c >= '0') && (c <= '9'))
5722 					id = 10*id + c - '0';
5723 				else
5724 					if (c == ':')
5725 						state = PXPERM1;
5726 					else
5727 						state = PXERR;
5728 				break;
5729 			case PXPERM :
5730 				if (c == ':') {
5731 					id = -1;
5732 					state = PXPERM1;
5733 				} else
5734 					state = PXERR;
5735 				break;
5736 			case PXPERM1 :
5737 				if ((c >= '0') && (c <= '7')) {
5738 					perms = c - '0';
5739 					state = PXNEXT;
5740 					break;
5741 				}
5742 				state = PXPERM2;
5743 				perms = 0;
5744 				/* fall through */
5745 			case PXPERM2 :
5746 				switch (c) {
5747 				case 'r' :
5748 					perms |= POSIX_PERM_R;
5749 					break;
5750 				case 'w' :
5751 					perms |= POSIX_PERM_W;
5752 					break;
5753 				case 'x' :
5754 					perms |= POSIX_PERM_X;
5755 					break;
5756 				case ',' :
5757 				case '\0' :
5758 					if (defacl) {
5759 						i = acccnt + l++;
5760 						dpermsset |= perms;
5761 					} else {
5762 						i = k++;
5763 						apermsset |= perms;
5764 					}
5765 					acl->ace[i].tag = tag;
5766 					acl->ace[i].perms = perms;
5767 					acl->ace[i].id = id;
5768 					if (c == '\0')
5769 						state = PXEND;
5770 					else
5771 						state = PXBEGIN;
5772 					break;
5773 				}
5774 				break;
5775 			case PXNEXT :
5776 				if (!c || (c == ',')) {
5777 					if (defacl) {
5778 						i = acccnt + l++;
5779 						dpermsset |= perms;
5780 					} else {
5781 						i = k++;
5782 						apermsset |= perms;
5783 					}
5784 					acl->ace[i].tag = tag;
5785 					acl->ace[i].perms = perms;
5786 					acl->ace[i].id = id;
5787 					if (c == '\0')
5788 						state = PXEND;
5789 					else
5790 						state = PXBEGIN;
5791 				} else
5792 					state = PXERR;
5793 				break;
5794 			case PXOCT :
5795 				if ((c >= '0') && (c <= '7'))
5796 					mode = (mode << 3) + c - '0';
5797 				else
5798 					if (c == '\0')
5799 						state = PXEND;
5800 					else
5801 						state = PXBEGIN;
5802 				break;
5803 			default :
5804 				break;
5805 			}
5806 			c = *p++;
5807 		}
5808 			/* insert default mask if none defined */
5809 		if (acccnt && !amask) {
5810 			i = k++;
5811 			acl->ace[i].tag = POSIX_ACL_MASK;
5812 			acl->ace[i].perms = apermsset;
5813 			acl->ace[i].id = -1;
5814 		}
5815 		if (defcnt && !dmask) {
5816 			i = acccnt + l++;
5817 			acl->ace[i].tag = POSIX_ACL_MASK;
5818 			acl->ace[i].perms = dpermsset;
5819 			acl->ace[i].id = -1;
5820 		}
5821 			/* compute the mode and tagsset */
5822 		tagsset = 0;
5823 		for (i=0; i<acccnt; i++) {
5824 			tagsset |= acl->ace[i].tag;
5825 			switch (acl->ace[i].tag) {
5826 			case POSIX_ACL_USER_OBJ :
5827 				mode |= acl->ace[i].perms << 6;
5828 				break;
5829 			case POSIX_ACL_GROUP_OBJ :
5830 					/* unless mask seen first */
5831 				if (!(tagsset & POSIX_ACL_MASK))
5832 					mode |= acl->ace[i].perms << 3;
5833 				break;
5834 			case POSIX_ACL_OTHER :
5835 				mode |= acl->ace[i].perms;
5836 				break;
5837 			case POSIX_ACL_MASK :
5838 					/* overrides group */
5839 				mode = (mode & 07707)
5840 						| (acl->ace[i].perms << 3);
5841 				break;
5842 			default :
5843 				break;
5844 			}
5845 		}
5846 		pxdesc->mode = mode;
5847 		pxdesc->tagsset = tagsset;
5848 		pxdesc->acl.version = POSIX_VERSION;
5849 		pxdesc->acl.flags = 0;
5850 		pxdesc->acl.filler = 0;
5851 		if (state != PXERR)
5852 			ntfs_sort_posix(pxdesc);
5853 showposix(pxdesc);
5854 		if ((state == PXERR)
5855 		   || (k != acccnt)
5856 		   || (l != defcnt)
5857                    || !ntfs_valid_posix(pxdesc)) {
5858 			if (~pxdesc->tagsset
5859 			    & (POSIX_ACL_USER_OBJ | POSIX_ACL_GROUP_OBJ | POSIX_ACL_OTHER))
5860 				fprintf(stderr,"User, group or other permissions missing\n");
5861 			else
5862 				fprintf(stderr,"Bad ACL description\n");
5863 			free(pxdesc);
5864 			pxdesc = (struct POSIX_SECURITY*)NULL;
5865 		} else
5866 			if (opt_v >= 2) {
5867 				printf("Interpreted input description :\n");
5868 				showposix(pxdesc);
5869 			}
5870 	} else
5871 		errno = ENOMEM;
5872 	return (pxdesc);
5873 }
5874 
5875 #endif /* POSIXACLS */
5876 
setperms(const char * volume,const char * perms,const char * base)5877 static BOOL setperms(const char *volume, const char *perms, const char *base)
5878 {
5879 	const char *p;
5880 	BOOL cmderr;
5881 	int i;
5882 #if POSIXACLS
5883 	struct POSIX_SECURITY *pxdesc;
5884 #else /* POSIXACLS */
5885 	int mode;
5886 #endif /* POSIXACLS */
5887 
5888 	cmderr = FALSE;
5889 	p = perms;
5890 #if POSIXACLS
5891 	pxdesc = encode_posix_acl(p);
5892 	if (pxdesc) {
5893 		if (!getuid() && open_security_api()) {
5894 			if (open_volume(volume,NTFS_MNT_NONE)) {
5895 				if (opt_r) {
5896 					for (i=0; i<(MAXSECURID + (1 << SECBLKSZ) - 1)/(1 << SECBLKSZ); i++)
5897 						securdata[i] = (struct SECURITY_DATA*)NULL;
5898 					recurseset_posix(base,pxdesc);
5899 				} else
5900 					singleset_posix(base,pxdesc);
5901 				close_volume(volume);
5902 			} else {
5903 				fprintf(stderr,"Could not open volume %s\n",volume);
5904 				printerror(stderr);
5905 				cmderr = TRUE;
5906 			}
5907 			close_security_api();
5908 		} else {
5909 			if (getuid())
5910 				fprintf(stderr,"This is only possible as root\n");
5911 			else
5912 				fprintf(stderr,"Could not open security API\n");
5913 			cmderr = TRUE;
5914 		}
5915 		free(pxdesc);
5916 	} else
5917 		cmderr = TRUE;
5918 #else /* POSIXACLS */
5919 	mode = 0;
5920 	while ((*p >= '0') && (*p <= '7'))
5921 		mode = (mode << 3) + (*p++) - '0';
5922 	if (*p) {
5923 		fprintf(stderr,"New mode should be given in octal\n");
5924 		cmderr = TRUE;
5925 	} else {
5926 		if (!getuid() && open_security_api()) {
5927 			if (open_volume(volume,NTFS_MNT_NONE)) {
5928 				if (opt_r) {
5929 					for (i=0; i<(MAXSECURID + (1 << SECBLKSZ) - 1)/(1 << SECBLKSZ); i++)
5930 						securdata[i] = (struct SECURITY_DATA*)NULL;
5931 					recurseset(base,mode);
5932 				} else
5933 					singleset(base,mode);
5934 				close_volume(volume);
5935 			} else {
5936 				fprintf(stderr,"Could not open volume %s\n",volume);
5937 				printerror(stderr);
5938 				cmderr = TRUE;
5939 			}
5940 			close_security_api();
5941 		} else {
5942 			if (getuid())
5943 				fprintf(stderr,"This is only possible as root\n");
5944 			else
5945 				fprintf(stderr,"Could not open security API\n");
5946 			cmderr = TRUE;
5947 		}
5948 	}
5949 #endif /* POSIXACLS */
5950 	return (cmderr);
5951 }
5952 
usage(void)5953 static void usage(void)
5954 {
5955 #ifdef HAVE_WINDOWS_H
5956 	fprintf(stderr,"Usage:\n");
5957 #ifdef SELFTESTS
5958 	fprintf(stderr,"   ntfssecaudit -t\n");
5959 	fprintf(stderr,"	run self-tests\n");
5960 #endif /* SELFTESTS */
5961 	fprintf(stderr,"   ntfssecaudit -h [file]\n");
5962 	fprintf(stderr,"	display security descriptors within file\n");
5963 	fprintf(stderr,"   ntfssecaudit -a[rv] volume\n");
5964 	fprintf(stderr,"	audit the volume\n");
5965 	fprintf(stderr,"   ntfssecaudit [-v] file\n");
5966 	fprintf(stderr,"	display the security parameters of file\n");
5967 	fprintf(stderr,"   ntfssecaudit -r[v] directory\n");
5968 	fprintf(stderr,"	display the security parameters of files in directory\n");
5969 	fprintf(stderr,"   ntfssecaudit -b[v] directory\n");
5970 	fprintf(stderr,"        backup the security parameters of files in directory\n");
5971 	fprintf(stderr,"   ntfssecaudit -s[ev] volume [backupfile]\n");
5972 	fprintf(stderr,"        set the security parameters as indicated in backup file\n");
5973 	fprintf(stderr,"        with -e also set extra parameters (Windows attrib)\n");
5974 	fprintf(stderr,"   ntfssecaudit perms file\n");
5975 	fprintf(stderr,"	set the security parameters of file to perms\n");
5976 	fprintf(stderr,"   ntfssecaudit -r[v] perms directory\n");
5977 	fprintf(stderr,"	set the security parameters of files in directory to perms\n");
5978 	fprintf(stderr,"   ntfssecaudit -u file\n");
5979 	fprintf(stderr,"	get a user mapping proposal applicable to file\n");
5980 #if POSIXACLS
5981 	fprintf(stderr,"   Notes: perms can be an octal mode or a Posix ACL description\n");
5982 #else /* POSIXACLS */
5983 	fprintf(stderr,"   Notes: perms is an octal mode\n");
5984 #endif /* POSIXACLS */
5985 	fprintf(stderr,"          volume is a drive letter and colon (eg. D:)\n");
5986 	fprintf(stderr,"          -v is for verbose, -vv for very verbose\n");
5987 #else /* HAVE_WINDOWS_H */
5988 	fprintf(stderr,"Usage:\n");
5989 #ifdef SELFTESTS
5990 	fprintf(stderr,"   ntfssecaudit -t\n");
5991 	fprintf(stderr,"	run self-tests\n");
5992 #endif /* SELFTESTS */
5993 	fprintf(stderr,"   ntfssecaudit -h [file]\n");
5994 	fprintf(stderr,"	display security descriptors within file\n");
5995 	fprintf(stderr,"   ntfssecaudit -a[rv] volume\n");
5996 	fprintf(stderr,"	audit the volume\n");
5997 	fprintf(stderr,"   ntfssecaudit [-v] volume file\n");
5998 	fprintf(stderr,"	display the security parameters of file\n");
5999 	fprintf(stderr,"   ntfssecaudit -r[v] volume directory\n");
6000 	fprintf(stderr,"	display the security parameters of files in directory\n");
6001 	fprintf(stderr,"   ntfssecaudit -b[v] volume directory\n");
6002 	fprintf(stderr,"        backup the security parameters of files in directory\n");
6003 	fprintf(stderr,"   ntfssecaudit -s[ev] volume [backupfile]\n");
6004 	fprintf(stderr,"        set the security parameters as indicated in backup file\n");
6005 	fprintf(stderr,"        with -e also set extra parameters (Windows attrib)\n");
6006 	fprintf(stderr,"   ntfssecaudit volume perms file\n");
6007 	fprintf(stderr,"	set the security parameters of file to perms\n");
6008 	fprintf(stderr,"   ntfssecaudit -r[v] volume perms directory\n");
6009 	fprintf(stderr,"	set the security parameters of files in directory to perms\n");
6010 #ifdef HAVE_SETXATTR
6011 	fprintf(stderr," special cases, do not require being root :\n");
6012 	fprintf(stderr,"   ntfssecaudit -u mounted-file\n");
6013 	fprintf(stderr,"	get a user mapping proposal applicable to mounted file\n");
6014 	fprintf(stderr,"   ntfssecaudit [-v] mounted-file\n");
6015 	fprintf(stderr,"	display the security parameters of a mounted file\n");
6016 #endif /* HAVE_SETXATTR */
6017 #if POSIXACLS
6018 	fprintf(stderr,"   Notes: perms can be an octal mode or a Posix ACL description\n");
6019 #else /* POSIXACLS */
6020 	fprintf(stderr,"   Notes: perms is an octal mode\n");
6021 #endif /* POSIXACLS */
6022 #if defined(__sun) && defined (__SVR4)
6023 	fprintf(stderr,"          volume is a partition designator (eg. /dev/dsk/c5t0d0p1)\n");
6024 #else /* defined(__sun) && defined (__SVR4) */
6025 	fprintf(stderr,"          volume is a partition designator (eg. /dev/sdb2)\n");
6026 #endif /* defined(__sun) && defined (__SVR4) */
6027 	fprintf(stderr,"          -v is for verbose, -vv for very verbose\n");
6028 #endif /* HAVE_WINDOWS_H */
6029 }
6030 
version(void)6031 static void version(void)
6032 {
6033 	static const char *EXEC_NAME = "ntfssecaudit";
6034 
6035 // confusing (see banner)
6036 	printf("\n%s v%s (libntfs-3g) - Audit security data on a NTFS "
6037 			"Volume.\n\n", EXEC_NAME, VERSION);
6038 	printf("    Copyright (c) 2007-2016 Jean-Pierre Andre\n");
6039 	printf("\n%s\n%s%s\n", ntfs_gpl, ntfs_bugs, ntfs_home);
6040 }
6041 
6042 #ifdef HAVE_WINDOWS_H
6043 
6044 /*
6045  *		Split a Windows file designator into volume and fullpath
6046  */
6047 
splitarg(char ** split,const char * arg)6048 static BOOL splitarg(char **split, const char *arg)
6049 {
6050 	char curdir[MAXFILENAME];
6051 	BOOL err;
6052 	BOOL withvol;
6053 	BOOL withfullpath;
6054 	int lthd;
6055 	char *volume;
6056 	char *filename;
6057 
6058 	err = TRUE;
6059 	withvol = arg[0] && (arg[1] == ':');
6060 	if (withvol)
6061 		withfullpath = (arg[2] == '/') || (arg[2] == '\\');
6062 	else
6063 		withfullpath = (arg[0] == '/') || (arg[0] == '\\');
6064 	lthd = 0;
6065 	if (!withvol || !withfullpath) {
6066 		if (getcwd(curdir, sizeof(curdir)))
6067 			lthd = strlen(curdir);
6068 	}
6069 	if (withvol && !withfullpath && arg[2]
6070 	    && ((arg[0] ^ curdir[0]) & 0x3f)) {
6071 		fprintf(stderr,"%c: is not the current drive,\n",arg[0]);
6072 		fprintf(stderr,"please use the full path\n");
6073 	} else {
6074 		if (withvol && !withfullpath && !arg[2]
6075 		    && ((arg[0] ^ curdir[0]) & 0x3f)) {
6076 			curdir[2] = '\\';
6077 			curdir[3] = 0;
6078 			lthd = 3;
6079 		}
6080 		volume = (char*)malloc(4);
6081 		if (volume) {
6082 			if (withvol)
6083 				volume[0] = arg[0];
6084 			else
6085 				volume[0] = curdir[0];
6086 			volume[1] = ':';
6087 			volume[2] = 0;
6088 			filename = (char*)malloc(strlen(arg) + lthd + 2);
6089 			if (filename) {
6090 				if (withfullpath) {
6091 					if (withvol)
6092 						strcpy(filename, &arg[2]);
6093 					else
6094 						strcpy(filename, arg);
6095 				} else {
6096 					strcpy(filename, &curdir[2]);
6097 					if (curdir[lthd - 1] != '\\')
6098 						strcat(filename, "\\");
6099 					if (withvol)
6100 						strcat(filename, &arg[2]);
6101 					else
6102 						strcat(filename, arg);
6103 				}
6104 				if (!cleanpath(filename)) {
6105 					split[0] = volume;
6106 					split[1] = filename;
6107 					err = FALSE;
6108 				} else {
6109 					fprintf(stderr,"Bad path %s\n", arg);
6110 				}
6111 			} else
6112 				free(volume);
6113 		}
6114 	}
6115 	return (err);
6116 }
6117 
6118 #endif /* HAVE_WINDOWS_H */
6119 
6120 /*
6121  *		Parse the command-line options
6122  */
6123 
parse_options(int argc,char * argv[])6124 static int parse_options(int argc, char *argv[])
6125 {
6126 	static const char *sopt = "-abehHrstuvV";
6127 	static const struct option lopt[] = {
6128 		{ "audit",	 no_argument,		NULL, 'a' },
6129 		{ "backup",	 no_argument,		NULL, 'b' },
6130 		{ "extra",	 no_argument,		NULL, 'e' },
6131 		{ "help",	 no_argument,		NULL, 'H' },
6132 		{ "hexdecode",	 no_argument,		NULL, 'h' },
6133 		{ "recurse",	 no_argument,		NULL, 'r' },
6134 		{ "set",	 no_argument,		NULL, 's' },
6135 		{ "test",	 no_argument,		NULL, 't' },
6136 		{ "user-mapping",no_argument,		NULL, 'u' },
6137 		{ "verbose",	 no_argument,		NULL, 'v' },
6138 		{ "version",	 no_argument,		NULL, 'V' },
6139 		{ NULL,		 0,			NULL,  0  }
6140 	};
6141 
6142 	int c = -1;
6143 	int err = 0;
6144 	int ver = 0;
6145 	int help = 0;
6146 	int xarg = 0;
6147 	CMDS prevcmd;
6148 
6149 	opterr = 0; /* We'll handle the errors, thank you. */
6150 
6151 	opt_e = FALSE;
6152 	opt_r = FALSE;
6153 	opt_v = 0;
6154 	cmd = CMD_NONE;
6155 	prevcmd = CMD_NONE;
6156 
6157 	while ((c = getopt_long(argc, argv, sopt, lopt, NULL)) != -1) {
6158 		switch (c) {
6159 		case 1:
6160 			if (!xarg)
6161 				xarg = optind - 1;
6162 			break;
6163 		case 'a':
6164 			prevcmd = cmd;
6165 			cmd = CMD_AUDIT;
6166 			break;
6167 		case 'b':
6168 			prevcmd = cmd;
6169 			cmd = CMD_BACKUP;
6170 			break;
6171 		case 'e':
6172 			opt_e = TRUE;
6173 			break;
6174 		case 'h':
6175 			prevcmd = cmd;
6176 			cmd = CMD_HEX;
6177 			break;
6178 		case 'H':
6179 			help++;
6180 			break;
6181 		case 'r':
6182 			opt_r = TRUE;
6183 			break;
6184 		case 's':
6185 			prevcmd = cmd;
6186 			cmd = CMD_SET;
6187 			break;
6188 #ifdef SELFTESTS
6189 		case 't':
6190 			prevcmd = cmd;
6191 			cmd = CMD_TEST;
6192 			break;
6193 #endif
6194 		case 'u':
6195 			prevcmd = cmd;
6196 			cmd = CMD_USERMAP;
6197 			break;
6198 		case 'v':
6199 			opt_v++;
6200 			break;
6201 		case 'V':
6202 			ver++;
6203 			break;
6204 		default:
6205 			if ((c < 'a') || (c > 'z'))
6206 				fprintf(stderr,"Unhandled option case: %d.\n", c);
6207 			else
6208 				fprintf(stderr,"Invalid option -%c\n",c);
6209 			err++;
6210 			break;
6211 		}
6212 		if ((cmd != CMD_NONE)
6213 		    && (prevcmd != CMD_NONE)
6214 		    && (prevcmd != cmd)) {
6215 			fprintf(stderr,"Incompatible commands\n");
6216 			err++;
6217 		}
6218 	}
6219 
6220 	if (!xarg)
6221 		xarg = argc;
6222 
6223 	if (help || err)
6224 		cmd = CMD_HELP;
6225 	else
6226 		if (ver)
6227 			cmd = CMD_VERSION;
6228 
6229 	return (err ? 0 : xarg);
6230 }
6231 
main(int argc,char * argv[])6232 int main(int argc, char *argv[])
6233 {
6234 	char *split[2];
6235 	char *uname;
6236 	FILE *fd;
6237 	int xarg;
6238 	BOOL cmderr;
6239 	BOOL fail;
6240 	int i;
6241 
6242 	printf("%s\n",BANNER);
6243 	cmderr = FALSE;
6244 	fail = FALSE;
6245 	errors = 0;
6246 	warnings = 0;
6247 	split[0] = split[1] = (char*)NULL;
6248 	uname = (char*)NULL;
6249 	xarg = parse_options(argc,argv);
6250 	if (xarg) {
6251 		for (i=0; i<(MAXSECURID + (1 << SECBLKSZ) - 1)/(1 << SECBLKSZ); i++)
6252 			securdata[i] = (struct SECURITY_DATA*)NULL;
6253 #if POSIXACLS
6254 		context.mapping[MAPUSERS] = (struct MAPPING*)NULL;
6255 		context.mapping[MAPGROUPS] = (struct MAPPING*)NULL;
6256 #endif /* POSIXACLS */
6257 		mappingtype = MAPNONE;
6258 
6259 		switch (cmd) {
6260 		case CMD_AUDIT :
6261 			if (xarg == (argc - 1))
6262 				fail = audit(argv[xarg]);
6263 			else
6264 				cmderr = TRUE;
6265 			break;
6266 		case CMD_BACKUP :
6267 			switch (argc - xarg) {
6268 			case 1 :
6269 #ifdef HAVE_WINDOWS_H
6270 				if (!splitarg(split, argv[xarg]))
6271 					fail = backup(split[0], split[1]);
6272 				else
6273 					cmderr = TRUE;
6274 #else
6275 				fail = backup(argv[xarg],"/");
6276 #endif
6277 				break;
6278 			case 2 :
6279 				cmderr = backup(argv[xarg],argv[xarg+1]);
6280 				break;
6281 			default :
6282 				cmderr = TRUE;
6283 				break;
6284 			}
6285 			break;
6286 		case CMD_HEX :
6287 			switch (argc - xarg) {
6288 			case 0 :
6289 				showhex(stdin);
6290 				break;
6291 			case 1 :
6292 				fd = fopen(argv[xarg],"rb");
6293 				if (fd) {
6294 					showhex(fd);
6295 					fclose(fd);
6296 				} else {
6297 					fprintf(stderr,"Could not open %s\n",
6298 							argv[xarg]);
6299 					cmderr = TRUE;
6300 				}
6301 				break;
6302 			default :
6303 				cmderr = TRUE;
6304 				break;
6305 			}
6306 			break;
6307 		case CMD_NONE :
6308 			switch (argc - xarg) {
6309 			case 1 :
6310 #ifdef HAVE_WINDOWS_H
6311 				if (!splitarg(split, argv[xarg]))
6312 					fail = listfiles(split[0], split[1]);
6313 				else
6314 					cmderr = TRUE;
6315 #else
6316 				if (opt_r)
6317 					cmderr = listfiles(argv[xarg],"/");
6318 				else
6319 					cmderr = processmounted(argv[xarg]);
6320 #endif
6321 				break;
6322 			case 2 :
6323 #ifdef HAVE_WINDOWS_H
6324 				if (!splitarg(split, argv[xarg + 1]))
6325 					fail = setperms(split[0],
6326 							argv[xarg], split[1]);
6327 				else
6328 					cmderr = TRUE;
6329 #else /* HAVE_WINDOWS_H */
6330 				fail = listfiles(argv[xarg],argv[xarg+1]);
6331 #endif /* HAVE_WINDOWS_H */
6332 				break;
6333 			case 3 :
6334 #ifdef HAVE_WINDOWS_H
6335 				uname = unixname(argv[xarg+2]);
6336 				if (uname)
6337 					fail = setperms(argv[xarg],
6338 							argv[xarg+1], uname);
6339 				else
6340 					cmderr = TRUE;
6341 #else /* HAVE_WINDOWS_H */
6342 				cmderr = setperms(argv[xarg], argv[xarg+1],
6343 							argv[xarg+2]);
6344 #endif
6345 				break;
6346 			default :
6347 				cmderr = TRUE;
6348 				break;
6349 			}
6350 			break;
6351 		case CMD_SET :
6352 			switch (argc - xarg) {
6353 			case 1 :
6354 				cmderr = dorestore(argv[xarg],stdin);
6355 				break;
6356 			case 2 :
6357 				fd = fopen(argv[xarg+1],"rb");
6358 				if (fd) {
6359 					if (dorestore(argv[xarg],fd))
6360 						cmderr = TRUE;
6361 					fclose(fd);
6362 				} else {
6363 					fprintf(stderr,"Could not open %s\n",
6364 								argv[xarg]);
6365 					cmderr = TRUE;
6366 				}
6367 				break;
6368 			default :
6369 				cmderr = TRUE;
6370 			}
6371 			break;
6372 #ifdef SELFTESTS
6373 		case CMD_TEST :
6374 			if (xarg != argc)
6375 				cmderr = TRUE;
6376 			else
6377 				selftests();
6378 			break;
6379 #endif
6380 		case CMD_USERMAP :
6381 			switch (argc - xarg) {
6382 			case 1 :
6383 #ifdef HAVE_WINDOWS_H
6384 				if (!splitarg(split, argv[xarg]))
6385 					fail = mapproposal(split[0],
6386 								split[1]);
6387 				else
6388 					cmderr = TRUE;
6389 #else /* HAVE_WINDOWS_H */
6390 				processmounted(argv[xarg]);
6391 #endif /* HAVE_WINDOWS_H */
6392 				break;
6393 			case 2 :
6394 #ifdef HAVE_WINDOWS_H
6395 				uname = unixname(argv[xarg+1]);
6396 				if (uname)
6397 					cmderr = mapproposal(argv[xarg],
6398 								uname);
6399 				else
6400 					cmderr = TRUE;
6401 #else /* HAVE_WINDOWS_H */
6402 				cmderr = TRUE;
6403 #endif /* HAVE_WINDOWS_H */
6404 				break;
6405 			default :
6406 				cmderr = TRUE;
6407 			}
6408 			break;
6409 		case CMD_HELP :
6410 		default :
6411 			usage();
6412 			break;
6413 		case CMD_VERSION :
6414 			version();
6415 			break;
6416 		}
6417 
6418 		if (warnings)
6419 			printf("** %u %s signalled\n",warnings,
6420 				(warnings > 1 ? "warnings were"
6421 						: "warning was"));
6422 		if (errors)
6423 			printf("** %u %s found\n",errors,
6424 				(errors > 1 ? "errors were" : "error was"));
6425 		else
6426 			if (fail)
6427 				printf("Command failed\n");
6428 			else
6429 				if (cmderr)
6430 					usage();
6431 				else
6432 					printf("No errors were found\n");
6433 		if (!isatty(1)) {
6434 			fflush(stdout);
6435 			if (warnings)
6436 				fprintf(stderr,"** %u %s signalled\n",warnings,
6437 					(warnings > 1 ? "warnings were"
6438 							: "warning was"));
6439 			if (errors)
6440 				fprintf(stderr,"** %u %s found\n",errors,
6441 					(errors > 1 ? "errors were"
6442 							: "error was"));
6443 			else
6444 				fprintf(stderr,"No errors were found\n");
6445 		}
6446 		if (split[0])
6447 			free(split[0]);
6448 		if (split[1])
6449 			free(split[1]);
6450 		if (uname)
6451 			free(uname);
6452 		freeblocks();
6453 	} else
6454 		usage();
6455 	if (cmderr || errors)
6456 		exit(1);
6457 	return (0);
6458 }
6459