ntfs-3g/ntfsprogs/ntfssecaudit.c
Jean-Pierre André 94677ae961 Accepted displaying bad security descriptors on conditions
Display security descriptors in ntfssecaudit when they appear as
correctly formed, even when they contain unsupported items.
2017-02-11 10:37:47 +01:00

6459 lines
154 KiB
C

/*
* Display and audit security attributes in an NTFS volume
*
* Copyright (c) 2007-2016 Jean-Pierre Andre
*
* Options :
* -a auditing security data
* -b backing up NTFS ACLs
* -e set extra backed-up parameters (in conjunction with -s)
* -h displaying hexadecimal security descriptors within a file
* -r recursing in a directory
* -s setting backed-up NTFS ACLs
* -u getting a user mapping proposal
* -v verbose (very verbose if set twice)
* also, if compile-time option is set
* -t run internal tests (with no access to storage)
*
* On Linux (being root, with volume not mounted) :
* ntfssecaudit -h [file]
* display the security descriptors found in file
* ntfssecaudit -a[rv] volume
* audit the volume
* ntfssecaudit [-v] volume file
* display the security parameters of file
* ntfssecaudit -r[v] volume directory
* display the security parameters of files in directory
* ntfssecaudit -b[v] volume [directory]
* backup the security parameters of files in directory
* ntfssecaudit -s[ve] volume [backupfile]
* set the security parameters as indicated in backup
* with -e set extra parameters (Windows attrib)
* ntfssecaudit volume perms file
* set the security parameters of file to perms (mode or acl)
* ntfssecaudit -r[v] volume perms directory
* set the security parameters of files in directory to perms
* special cases, do not require being root :
* ntfssecaudit [-v] mounted-file
* display the security parameters of mounted file
* ntfssecaudit -u[v] mounted-file
* display a user mapping proposal
*
*
* On Windows (the volume being part of file name)
* ntfssecaudit -h [file]
* display the security descriptors found in file
* ntfssecaudit -a[rv] volume
* audit the volume
* ntfssecaudit [-v] file
* display the security parameters of file
* ntfssecaudit -r[v] directory
* display the security parameters of files in directory
* ntfssecaudit -b[v] directory
* backup the security parameters of files in directory
* ntfssecaudit -s[v] volume [backupfile]
* set the security parameters as indicated in backup
* with -e set extra parameters (Windows attrib)
* ntfssecaudit perms file
* set the security parameters of file to perms (mode or acl)
* ntfssecaudit -r[v] perms directory
* set the security parameters of files in directory to perms
*/
/* History
*
* Nov 2007
* - first version, by concatenating miscellaneous utilities
*
* Jan 2008, version 1.0.1
* - fixed mode displaying
* - added a global severe errors count
*
* Feb 2008, version 1.0.2
* - implemented conversions for big-endian machines
*
* Mar 2008, version 1.0.3
* - avoided consistency checks on $Secure when there is no such file
*
* Mar 2008, version 1.0.4
* - changed ordering of ACE's
* - changed representation for special flags
* - defaulted to stdin for option -h
* - added self tests (compile time option)
* - fixed errors specific to big-endian computers
*
* Apr 2008, version 1.1.0
* - developped Posix ACLs to NTFS ACLs conversions
* - developped NTFS ACLs backup and restore
*
* Apr 2008, version 1.1.1
* - fixed an error specific to big-endian computers
* - checked hash value and fixed error report in restore
* - improved display in showhex() and restore()
*
* Apr 2008, version 1.1.2
* - improved and fixed Posix ACLs to NTFS ACLs conversions
*
* Apr 2008, version 1.1.3
* - reenabled recursion for setting a new mode or ACL
* - processed Unicode file names and displayed them as UTF-8
* - allocated dynamically memory for file names when recursing
*
* May 2008, version 1.1.4
* - all Unicode/UTF-8 strings checked and processed
*
* Jul 2008, version 1.1.5
* - made Windows owner consistent with Linux owner when changing mode
* - allowed owner change on Windows when it does not match Linux owner
* - skipped currently unused code
*
* Aug 2008, version 1.2.0
* - processed \.NTFS-3G\UserMapping on Windows
* - made use of user mappings through the security API or direct access
* - fixed a bug in restore
* - fixed UTF-8 conversions
*
* Sep 2008, version 1.3.0
* - split the code to have part of it shared with ntfs-3g library
* - fixed testing for end of SDS block
* - added samples checking in selftest for easier debugging
*
* Oct 2008, version 1.3.1
* - fixed outputting long long data when testing on a Palm organizer
*
* Dec 2008, version 1.3.2
* - fixed restoring ACLs
* - added optional logging of ACL hashes to facilitate restore checks
* - fixed collecting SACLs
* - fixed setting special control flags
* - fixed clearing existing SACLs (Linux only) and DACLs
* - changed the sequencing of items when quering a security descriptor
* - avoided recursing on junctions and symlinks on Windows
*
* Jan 2009, version 1.3.3
* - save/restore Windows attributes (code from Faisal)
*
* Mar 2009, version 1.3.4
* - enabled displaying attributes of a mounted file over Linux
*
* Apr 2009, version 1.3.5
* - fixed initialisation of stand-alone user mapping
* - fixed POSIXACL redefinition when included in the ntfs-3g package
* - fixed displaying of options
* - fixed a dependency on the shared library version used
*
* May 2009, version 1.3.6
* - added new samples for self testing
*
* Jun 2009, version 1.3.7
* - fixed displaying owner and group of a mounted file over Linux
*
* Jul 2009, version 1.3.8
* - fixed again displaying owner and group of a mounted file over Linux
* - cleaned some code to avoid warnings
*
* Nov 2009, version 1.3.9
* - allowed security descriptors up to 64K
*
* Nov 2009, version 1.3.10
* - applied patches for MacOSX from Erik Larsson
*
* Nov 2009, version 1.3.11
* - replace <attr/xattr.h> by <sys/xattr.h> (provided by glibc)
*
* Dec 2009, version 1.3.12
* - worked around "const" possibly redefined in config.h
*
* Dec 2009, version 1.3.13
* - fixed the return code of dorestore()
*
* Dec 2009, version 1.3.14
* - adapted to opensolaris
*
* Jan 2010, version 1.3.15
* - more adaptations to opensolaris
* - removed the fix for return code of dorestore()
*
* Jan 2010, version 1.3.16
* - repeated the fix for return code of dorestore()
*
* Mar 2010, version 1.3.17
* - adapted to new default user mapping
* - fixed #ifdef'd code for selftest
*
* May 2010, version 1.3.18
* - redefined early error logging
*
* Mar 2011, version 1.3.19
* - fixed interface to ntfs_initialize_file_security()
*
* Apr 2011, version 1.3.20
* - fixed false memory leak detection
*
* Jun 2011, version 1.3.21
* - cleaned a few unneeded variables
*
* Nov 2011, version 1.3.22
* - added a distinctive prefix to owner and group SID
* - fixed a false memory leak detection
*
* Jun 2012, version 1.3.23
* - added support for SACL (nickgarvey)
*
* Jul 2012, version 1.3.24
* - added self-tests for authenticated users
* - added display of ace-inherited flag
* - made runnable on OpenIndiana
*
* Aug 2012, version 1.4.0
* - added an option for user mapping proposal
*
* Sep 2013, version 1.4.1
* - silenced an aliasing warning by gcc >= 4.8
*
* May 2014, version 1.4.2
* - decoded GENERIC_ALL permissions
* - decoded more "well-known" and generic SIDs
* - showed Windows ownership in verbose situations
* - fixed apparent const violations
*
* Dec 2014, version 1.4.3
* - fixed displaying "UserMapping" as a file name
*
* Mar 2015, version 1.4.5
* - adapted to new NTFS ACLs when owner is same as group
*
* May 2015, version 1.4.6
* - made to load shared library based on generic name
*
* Mar 2016, Version 1.5.0
* - reorganized to rely on libntfs-3g even on Windows
*/
/*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program (in the main directory of the NTFS-3G
* distribution in the file COPYING); if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
/*
* General parameters which may have to be adapted to needs
*/
#define AUDT_VERSION "1.5.0"
#define SELFTESTS 0
#define NOREVBOM 0 /* still unclear what this should be */
/*
* External declarations
*/
#include "config.h"
#ifdef HAVE_STDIO_H
#include <stdio.h>
#endif /* HAVE_STDIO_H */
#ifdef HAVE_STDLIB_H
#include <stdlib.h>
#endif /* HAVE_STDLIB_H */
#ifdef HAVE_STRING_H
#include <string.h>
#endif /* HAVE_STRING_H */
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif /* HAVE_UNISTD_H */
#ifdef HAVE_TIME_H
#include <time.h>
#endif /* HAVE_TIME_H */
#ifdef HAVE_GETOPT_H
#include <getopt.h>
#endif /* HAVE_GETOPT_H */
#ifdef HAVE_ERRNO_H
#include <errno.h>
#endif /* HAVE_ERRNO_H */
#ifdef HAVE_FCNTL_H
#include <fcntl.h>
#endif /* HAVE_FCNTL_H */
#ifdef HAVE_SYS_TYPES_H
#include <sys/types.h>
#endif /* HAVE_SYS_TYPES_H */
#ifdef HAVE_SYS_STAT_H
#include <sys/stat.h>
#endif /* HAVE_SYS_STAT_H */
#ifdef HAVE_SETXATTR
#include <sys/xattr.h>
#else /* HAVE_SETXATTR */
#warning "The extended attribute package is not available"
#endif /* HAVE_SETXATTR */
#include "types.h"
#include "endians.h"
#include "support.h"
#include "layout.h"
#include "param.h"
#include "ntfstime.h"
#include "device_io.h"
#include "device.h"
#include "logging.h"
#include "runlist.h"
#include "mft.h"
#include "inode.h"
#include "attrib.h"
#include "bitmap.h"
#include "index.h"
#include "volume.h"
#include "unistr.h"
#include "mst.h"
#include "security.h"
#include "acls.h"
#include "realpath.h"
#include "utils.h"
#include "misc.h"
struct CALLBACK;
typedef int (*dircallback)(void *context, const ntfschar *ntfsname,
const int length, const int type, const s64 pos,
const MFT_REF mft_ref, const unsigned int dt_type);
#if POSIXACLS
static BOOL same_posix(struct POSIX_SECURITY *pxdesc1,
struct POSIX_SECURITY *pxdesc2);
#endif /* POSIXACLS */
#ifndef HAVE_SYSLOG_H
void ntfs_log_early_error(const char *format, ...)
__attribute__((format(printf, 1, 2)));
#endif /* HAVE_SYSLOG_H */
#define ACCOUNTSIZE 256 /* maximum size of an account name */
#define MAXFILENAME 4096
#define MAXATTRSZ 65536 /* Max sec attr size (16448 met for WinXP) */
#define MAXLINE 80 /* maximum processed size of a line */
#define BUFSZ 1024 /* buffer size to read mapping file */
#define LINESZ 120 /* maximum useful size of a mapping line */
typedef enum { RECSHOW, RECSET, RECSETPOSIX } RECURSE;
typedef enum { MAPNONE, MAPEXTERN, MAPLOCAL, MAPDUMMY } MAPTYPE;
typedef enum { CMD_AUDIT, CMD_BACKUP, CMD_HEX, CMD_HELP, CMD_SET,
CMD_TEST, CMD_USERMAP, CMD_VERSION, CMD_NONE } CMDS;
#define MAXSECURID 262144
#define SECBLKSZ 8
#define MAPDIR ".NTFS-3G"
#define MAPFILE "UserMapping"
#ifdef HAVE_WINDOWS_H
#define DIRSEP "\\"
#else
#define DIRSEP "/"
#endif
/* standard owner (and administrator) rights */
#define OWNER_RIGHTS (DELETE | READ_CONTROL | WRITE_DAC | WRITE_OWNER \
| SYNCHRONIZE \
| FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES \
| FILE_READ_EA | FILE_WRITE_EA)
/* standard world rights */
#define WORLD_RIGHTS (READ_CONTROL | FILE_READ_ATTRIBUTES | FILE_READ_EA \
| SYNCHRONIZE)
/* inheritance flags for files and directories */
#define FILE_INHERITANCE NO_PROPAGATE_INHERIT_ACE
#define DIR_INHERITANCE (OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE)
/*
* To identify NTFS ACL meaning Posix ACL granted to root
* we use rights always granted to anybody, so they have no impact
* either on Windows or on Linux.
*/
#define ROOT_OWNER_UNMARK SYNCHRONIZE /* ACL granted to root as owner */
#define ROOT_GROUP_UNMARK FILE_READ_EA /* ACL granted to root as group */
#define INSDS1 1
#define INSDS2 2
#define INSII 4
#define INSDH 8
struct SII { /* this is an image of an $SII index entry */
le16 offs;
le16 size;
le32 fill1;
le16 indexsz;
le16 indexksz;
le16 flags;
le16 fill2;
le32 keysecurid;
/* did not find official description for the following */
le32 hash;
le32 securid;
le32 dataoffsl; /* documented as badly aligned */
le32 dataoffsh;
le32 datasize;
} ;
struct SDH { /* this is an image of an $SDH index entry */
le16 offs;
le16 size;
le32 fill1;
le16 indexsz;
le16 indexksz;
le16 flags;
le16 fill2;
le32 keyhash;
le32 keysecurid;
/* did not find official description for the following */
le32 hash;
le32 securid;
le32 dataoffsl;
le32 dataoffsh;
le32 datasize;
le32 fill3;
} ;
#ifdef HAVE_WINDOWS_H
/*
* Including <windows.h> leads to numerous conflicts with layout.h
* so define a few needed Windows calls unrelated to ntfs-3g
*/
BOOL WINAPI LookupAccountSidA(const char*, void*, char*, u32*,
char*, u32*, s32*);
u32 WINAPI GetFileAttributesA(const char*);
#endif /* HAVE_WINDOWS_H */
#define INVALID_FILE_ATTRIBUTES (-1)/* error from ntfs_get_file_attributes() */
/*
* Structures for collecting directory contents
*/
struct LINK {
struct LINK *next;
char name[1];
} ;
struct CALLBACK {
struct LINK *head;
const char *dir;
} ;
static int callback(void *context, const ntfschar *ntfsname,
const int length, const int type, const s64 pos,
const MFT_REF mft_ref, const unsigned int dt_type);
struct SECURITY_DATA {
u64 offset;
char *attr;
u32 hash;
u32 length;
unsigned int filecount:16;
unsigned int mode:12;
unsigned int flags:4;
} ;
/*
* Global constants
*/
#define BANNER "ntfssecaudit " AUDT_VERSION " : NTFS security data auditing"
#ifdef SELFTESTS
/*
* Dummy mapping file (self tests only)
*/
#define DUMMYAUTH "S-1-5-21-3141592653-589793238-462843383-"
char dummymapping[] = "500::" DUMMYAUTH "1000\n"
"501::" DUMMYAUTH "1001\n"
"502::" DUMMYAUTH "1002\n"
"503::" DUMMYAUTH "1003\n"
"516::" DUMMYAUTH "1016\n"
":500:" DUMMYAUTH "513\r\n"
":511:S-1-5-21-1607551490-981732888-1819828000-513\n"
":516:" DUMMYAUTH "1012\r\n"
"::" DUMMYAUTH "10000\n";
/*
* SID for authenticated user (S-1-5-11)
*/
static const char authsidbytes[] = {
1, /* revision */
1, /* auth count */
0, 0, 0, 0, 0, 5, /* base */
11, 0, 0, 0 /* 1st level */
};
static const SID *authsid = (const SID*)authsidbytes;
/*
* SID for local users (S-1-5-32-545)
*/
static const char localsidbytes[] = {
1, /* revision */
2, /* auth count */
0, 0, 0, 0, 0, 5, /* base */
32, 0, 0, 0, /* 1st level */
33, 2, 0, 0 /* 2nd level */
};
static const SID *localsid = (const SID*)localsidbytes;
/*
* SID for system (S-1-5-18)
*/
static const char systemsidbytes[] = {
1, /* revision */
1, /* auth count */
0, 0, 0, 0, 0, 5, /* base */
18, 0, 0, 0 /* 1st level */
};
static const SID *systemsid = (const SID*)systemsidbytes;
#endif /* SELFTESTS */
/*
* Global variables
*/
BOOL opt_e; /* restore extra (currently windows attribs) */
BOOL opt_r; /* recursively apply to subdirectories */
BOOL opt_u; /* user mapping proposal */
int opt_v; /* verbose or very verbose*/
CMDS cmd; /* command to process */
struct SECURITY_DATA *securdata[(MAXSECURID + (1 << SECBLKSZ) - 1)/(1 << SECBLKSZ)];
unsigned int errors; /* number of severe errors */
unsigned int warnings; /* number of non-severe errors */
struct SECURITY_CONTEXT context;
MAPTYPE mappingtype;
struct SECURITY_API *ntfs_context = (struct SECURITY_API*)NULL;
/*
* Open and close the security API (obsolete)
*/
static BOOL open_security_api(void)
{
return (TRUE);
}
static BOOL close_security_api(void)
{
return (0);
}
/*
* Open and close a volume
* Assumes a single volume is opened
*/
static BOOL open_volume(const char *volume, unsigned long flags)
{
BOOL ok;
ok = FALSE;
if (!ntfs_context) {
ntfs_context = ntfs_initialize_file_security(volume, flags);
if (ntfs_context) {
if (*(u32*)ntfs_context != MAGIC_API) {
fprintf(stderr,"Versions of ntfs-3g and ntfssecaudit"
" are not compatible\n");
} else {
fprintf(stderr,"\"%s\" opened %s\n",volume,
(flags & NTFS_MNT_RDONLY
? "read-only" : "read-write"));
mappingtype = MAPEXTERN;
ok = TRUE;
}
} else {
fprintf(stderr,"Could not open \"%s\"\n",volume);
#ifdef HAVE_WINDOWS_H
switch (errno) {
case EACCES :
fprintf(stderr,"You need Administrator rights to open \"%s\"\n",
volume);
break;
case EBUSY :
fprintf(stderr,"Looks like \"%s\" is mounted,\n",volume);
fprintf(stderr,"close all applications using it\n");
break;
default :
fprintf(stderr,"Close all applications using %s\n", volume);
fprintf(stderr,"to make sure it is not mounted\n");
}
#else
fprintf(stderr,"Make sure \"%s\" is not mounted\n",volume);
#endif
}
} else
fprintf(stderr,"A volume is already open\n");
return (ok);
}
static BOOL close_volume(const char *volume)
{
BOOL r;
r = ntfs_leave_file_security(ntfs_context);
if (r)
fprintf(stderr,"\"%s\" closed\n",volume);
else
fprintf(stderr,"Could not close \"%s\"\n",volume);
ntfs_context = (struct SECURITY_API*)NULL;
return (r);
}
#ifdef HAVE_WINDOWS_H
/*
* Make a path suitable for feeding to libntfs-3g
*
* Use '/' as a directory separator and remove /../ and /./
*/
static int cleanpath(char *path)
{
int err;
char *p;
char *s, *d;
err = 0;
for (p=path; *p; p++)
if (*p == '\\')
*p = '/';
do {
s = (char*)NULL;
p = strstr(path, "/./");
if (p) {
s = p + 3;
d = p + 1;
} else {
p = strstr(path, "/../");
if (p) {
d = p;
while ((d != path) && (*--d != '/'))
d--;
if ((d != p) && (*d == '/')) {
s = p + 3;
} else
err = 1;
}
}
if (s) {
while (*s)
*d++ = *s++;
*d = 0;
}
} while (s && !err);
return (err);
}
/*
* Build a path with Unix-type separators
*
* The path from the ntfs root is required for libntfs-3g calls
*/
static char *unixname(const char *name)
{
char *uname;
uname = (char*)malloc(strlen(name) + 1);
if (uname) {
strcpy(uname, name);
if (cleanpath(uname)) {
fprintf(stderr,"Bad path %s\n",name);
free(uname);
uname = (char*)NULL;
}
}
return (uname);
}
#endif /* HAVE_WINDOWS_H */
/*
* Extract small or big endian data from an array of bytes
*/
static unsigned int get2l(const char *attr, int p)
{
int i;
unsigned int v;
v = 0;
for (i=0; i<2; i++)
v += (attr[p+i] & 255) << (8*i);
return (v);
}
static unsigned long get4l(const char *attr, int p)
{
int i;
unsigned long v;
v = 0;
for (i=0; i<4; i++)
v += ((long)(attr[p+i] & 255)) << (8*i);
return (v);
}
static u64 get6h(const char *attr, int p)
{
int i;
u64 v;
v = 0;
for (i=0; i<6; i++)
v = (v << 8) + (attr[p+i] & 255);
return (v);
}
static u64 get8l(const char *attr, int p)
{
int i;
u64 v;
v = 0;
for (i=0; i<8; i++)
v += ((long long)(attr[p+i] & 255)) << (8*i);
return (v);
}
/*
* Set small or big endian data into an array of bytes
*/
static void set2l(char *p, unsigned int v)
{
int i;
for (i=0; i<2; i++)
p[i] = ((v >> 8*i) & 255);
}
static void set4l(char *p, unsigned long v)
{
int i;
for (i=0; i<4; i++)
p[i] = ((v >> 8*i) & 255);
}
/*
* hexadecimal dump of an array of bytes
*/
static void hexdump(const char *attr, int size, int level)
{
int i,j;
for (i=0; i<size; i+=16) {
if (level)
printf("%*c",level,' ');
printf("%06x ",i);
for (j=i; (j<(i+16)) && (j<size); j++)
printf((j & 3 ? "%02x" : " %02x"),attr[j] & 255);
printf("\n");
}
}
static u32 hash(const le32 *buf, int size /* bytes */)
{
u32 h;
int i;
h = 0;
for (i=0; 4*i<size; i++)
h = le32_to_cpu(buf[i]) + (h << 3) + ((h >> 29) & 7);
return (h);
}
/*
* Evaluate the size of UTS-8 conversion of a UTF-16LE text
* trailing '\0' not accounted for
* Returns 0 for invalid input
*/
static unsigned int utf8size(const ntfschar *utf16, int length)
{
int i;
int count = 0;
BOOL surrog;
BOOL fail;
surrog = FALSE;
fail = FALSE;
for (i = 0; i < length && utf16[i] && !fail; i++) {
unsigned short c = le16_to_cpu(utf16[i]);
if (surrog) {
if ((c >= 0xdc00) && (c < 0xe000)) {
surrog = FALSE;
count += 4;
} else
fail = TRUE;
} else
if (c < 0x80)
count++;
else if (c < 0x800)
count += 2;
else if (c < 0xd800)
count += 3;
else if (c < 0xdc00)
surrog = TRUE;
#if NOREVBOM
else if ((c >= 0xe000) && (c < 0xfffe))
#else
else if (c >= 0xe000)
#endif
count += 3;
else
fail = TRUE;
}
if (surrog)
fail = TRUE;
return (fail ? 0 : count);
}
/*
* Convert a UTF-16LE text to UTF-8
* Note : wcstombs() not used because on Linux it fails for characters
* not present in current locale
* Returns size or zero for invalid input
*/
static unsigned int makeutf8(char *utf8, const ntfschar *utf16, int length)
{
int size;
size = ntfs_ucstombs(utf16, length, &utf8, MAXFILENAME);
return (size < 0 ? 0 : size);
}
/*
* Print a file name
* on Windows it prints UTF-16LE names as UTF-8
*/
static void printname(FILE *file, const char *name)
{
#ifdef HAVE_WINDOWS_H
char *wname;
char *p;
wname = (char*)malloc(strlen(name) + 1);
if (wname) {
strcpy(wname, name);
for (p=wname; *p; p++)
if (*p == '/')
*p = '\\';
fprintf(file,"%s", wname);
free(wname);
}
#else /* HAVE_WINDOWS_H */
fprintf(file,"%s",name);
#endif /* HAVE_WINDOWS_H */
}
/*
* Print the last error code
*/
static void printerror(FILE *file)
{
if (errno)
fprintf(file,"Error code %d : %s\n",errno,strerror(errno));
switch (errno) {
case EACCES :
fprintf(file,"You probably need Administrator rights\n");
break;
case EBUSY :
fprintf(file,"You probably try to write to a mounted device\n");
break;
default :
break;
}
}
#ifndef HAVE_SYSLOG_H
/*
* Redefine early error messages in stand-alone situations
*/
static void ntfs_log_early_error(const char *format, ...)
{
va_list args;
va_start(args, format);
vfprintf(stderr,format,args);
va_end(args);
}
#endif /* HAVE_SYSLOG_H */
/*
* Guess whether a security attribute is intended for a directory
* based on the presence of inheritable ACE
* (not 100% reliable)
*/
static BOOL guess_dir(const char *attr)
{
int off;
int isdir;
int cnt;
int i;
int x;
isdir = 0;
off = get4l(attr,16);
if (off) {
cnt = get2l(attr,off+4);
x = 8;
for (i=0; i<cnt; i++) {
if (attr[off + x + 1] & 3)
isdir = 1;
x += get2l(attr,off + x + 2);
}
}
return (isdir);
}
/*
* Display a SID
* See http://msdn2.microsoft.com/en-us/library/aa379649.aspx
*/
static void showsid(const char *attr, int off, const char *prefix, int level)
{
int cnt;
int i;
BOOL known;
u64 auth;
unsigned long first;
unsigned long second;
unsigned long last;
char marker;
if (cmd == CMD_BACKUP)
marker = '#';
else
marker = ' ';
cnt = attr[off+1] & 255;
auth = get6h(attr,off+2);
known = FALSE;
/* SID names taken from https://support.microsoft.com/en-us/kb/243330 */
if ((attr[off] == 1) /* revision */
&& cnt
&& (auth < 100)) {
first = get4l(attr,off+8);
switch (cnt) {
case 0 : /* no level (error) */
break;
case 1 : /* single level */
switch (auth) {
case 0 :
if (first == 0) {
known = TRUE;
printf("%*cNull SID\n",-level,marker);
}
break;
case 1 :
if (first == 0) {
known = TRUE;
printf("%*cWorld SID\n",-level,marker);
}
break;
case 3 :
switch (first) {
case 0 :
known = TRUE;
printf("%*cCreator owner SID\n",-level,marker);
break;
case 1 :
known = TRUE;
printf("%*cCreator group SID\n",-level,marker);
break;
}
break;
case 5 :
switch (first) {
case 1 :
known = TRUE;
printf("%*cDialup SID\n",-level,marker);
break;
case 2 :
known = TRUE;
printf("%*cNetwork SID\n",-level,marker);
break;
case 3 :
known = TRUE;
printf("%*cBatch SID\n",-level,marker);
break;
case 4 :
known = TRUE;
printf("%*cInteractive SID\n",-level,marker);
break;
case 6 :
known = TRUE;
printf("%*cService SID\n",-level,marker);
break;
case 7 :
known = TRUE;
printf("%*cAnonymous SID\n",-level,marker);
break;
case 11 :
known = TRUE;
printf("%*cAuthenticated Users SID\n",-level,marker);
break;
case 13 :
known = TRUE;
printf("%*cTerminal Server Users SID\n",-level,marker);
break;
case 14 :
known = TRUE;
printf("%*cRemote Interactive Logon SID\n",-level,marker);
break;
case 18 :
known = TRUE;
printf("%*cLocal System SID\n",-level,marker);
break;
}
break;
}
break;
case 2 : /* double level */
second = get4l(attr,off+12);
switch (auth) {
case 5 :
if (first == 32) {
known = TRUE;
switch (second) {
case 544 :
printf("%*cAdministrators SID\n",-level,marker);
break;
case 545 :
printf("%*cUsers SID\n",-level,marker);
break;
case 546 :
printf("%*cGuests SID\n",-level,marker);
break;
default :
printf("%*cSome domain SID\n",-level,marker);
break;
}
}
break;
}
break;
default : /* three levels or more */
second = get4l(attr,off+12);
last = get4l(attr,off+4+4*cnt);
switch (auth) {
case 5 :
if (first == 21) {
known = TRUE;
switch (last) {
case 500 :
printf("%*cAdministrator SID\n",-level,marker);
break;
case 501 :
printf("%*cGuest SID\n",-level,marker);
break;
case 512 :
printf("%*cDomain Admins SID\n",-level,marker);
break;
case 513 :
printf("%*cDomain Users SID\n",-level,marker);
break;
case 514 :
printf("%*cDomain Guests SID\n",-level,marker);
break;
default :
printf("%*cLocal user-%lu SID\n",-level,marker,last);
break;
}
}
break;
}
}
}
if (!known)
printf("%*cUnknown SID\n",-level,marker);
printf("%*c%shex S-%x-",-level,marker,prefix,attr[off] & 255);
printf("%llx",(long long)auth);
for (i=0; i<cnt; i++)
printf("-%lx",get4l(attr,off+8+4*i));
printf("\n");
printf("%*c%sdec S-%u-",-level,marker,prefix,attr[off] & 255);
printf("%llu",(long long)auth);
for (i=0; i<cnt; i++)
printf("-%lu",get4l(attr,off+8+4*i));
printf("\n");
}
static void showusid(const char *attr, int level)
{
int off;
char marker;
if (cmd == CMD_BACKUP)
marker = '#';
else
marker = ' ';
if (level)
printf("%*c",-level,marker);
printf("Owner SID\n");
off = get4l(attr,4);
showsid(attr,off,"O:",level+4);
}
static void showgsid(const char *attr, int level)
{
int off;
char marker;
if (cmd == CMD_BACKUP)
marker = '#';
else
marker = ' ';
if (level)
printf("%*c",-level,marker);
printf("Group SID\n");
off = get4l(attr,8);
showsid(attr,off,"G:",level+4);
}
static void showownership(const char *attr)
{
#ifdef HAVE_WINDOWS_H
char account[ACCOUNTSIZE];
BIGSID sidcopy;
s32 use;
u32 accountsz;
u32 domainsz;
#endif /* HAVE_WINDOWS_H */
enum { SHOWOWN, SHOWGRP, SHOWINT, SHOWDONE } shown;
const char *sid;
const char *prefix;
u64 auth;
int cnt;
int off;
int i;
for (shown=SHOWOWN; shown<SHOWDONE; ) {
switch (shown) {
case SHOWOWN :
off = get4l(attr,4);
sid = &attr[off];
prefix = "Windows owner";
shown = SHOWGRP;
break;
case SHOWGRP :
off = get4l(attr,8);
sid = &attr[off];
prefix = "Windows group";
shown = SHOWINT;
break;
#if OWNERFROMACL
case SHOWINT :
off = get4l(attr,4);
prefix = "Interpreted owner";
sid = (const char*)ntfs_acl_owner((const char*)attr);
if (ntfs_same_sid((const SID*)sid,
(const SID*)&attr[off]))
sid = (const char*)NULL;
shown = SHOWDONE;
break;
#endif /* OWNERFROMACL */
default :
sid = (const char*)NULL;
prefix = (const char*)NULL;
shown = SHOWDONE;
break;
}
if (sid) {
cnt = sid[1] & 255;
auth = get6h(sid,2);
if (cmd == CMD_BACKUP)
printf("# %s S-%d-",prefix,sid[0] & 255);
else
printf("%s S-%d-",prefix,sid[0] & 255);
printf("%llu",(long long)auth);
for (i=0; i<cnt; i++)
printf("-%lu",get4l(sid,8+4*i));
#ifdef HAVE_WINDOWS_H
memcpy(sidcopy,sid,ntfs_sid_size((const SID*)sid));
accountsz = ACCOUNTSIZE;
domainsz = ACCOUNTSIZE;
if (LookupAccountSidA((const char*)NULL, sidcopy,
account, &accountsz,
(char*)NULL, &domainsz, &use))
printf(" (%s)", account);
#endif /* HAVE_WINDOWS_H */
printf("\n");
}
}
}
static void showheader(const char *attr, int level)
{
int flags;
char marker;
if (cmd == CMD_BACKUP)
marker = '#';
else
marker = ' ';
if (level)
printf("%*c",-level,marker);
printf("Global header\n");
printf("%*crevision %d\n",-level-4,marker,attr[0]);
flags = get2l(attr,2);
printf("%*cflags 0x%x\n",-level-4,marker,flags);
if (flags & 1)
printf("%*c owner is defaulted\n",-level-4,marker);
if (flags & 2)
printf("%*c group is defaulted\n",-level-4,marker);
if (flags & 4)
printf("%*c DACL present\n",-level-4,marker);
if (flags & 8)
printf("%*c DACL is defaulted\n",-level-4,marker);
if (flags & 0x10)
printf("%*c SACL present\n",-level-4,marker);
if (flags & 0x20)
printf("%*c SACL is defaulted\n",-level-4,marker);
if (flags & 0x100)
printf("%*c DACL inheritance is requested\n",-level-4,marker);
if (flags & 0x200)
printf("%*c SACL inheritance is requested\n",-level-4,marker);
if (flags & 0x400)
printf("%*c DACL was inherited automatically\n",-level-4,marker);
if (flags & 0x800)
printf("%*c SACL was inherited automatically\n",-level-4,marker);
if (flags & 0x1000)
printf("%*c DACL cannot be modified by inheritable ACEs\n",-level-4,marker);
if (flags & 0x2000)
printf("%*c SACL cannot be modified by inheritable ACEs\n",-level-4,marker);
if (flags & 0x8000)
printf("%*c self relative descriptor\n",-level-4,marker);
if (flags & 0x43eb)
printf("%*c unknown flags 0x%x present\n",-level-4,marker,
flags & 0x43eb);
printf("%*cOff USID 0x%x\n",-level-4,marker,(int)get4l(attr,4));
printf("%*cOff GSID 0x%x\n",-level-4,marker,(int)get4l(attr,8));
printf("%*cOff SACL 0x%x\n",-level-4,marker,(int)get4l(attr,12));
printf("%*cOff DACL 0x%x\n",-level-4,marker,(int)get4l(attr,16));
}
static void showace(const char *attr, int off, int isdir, int level)
{
int flags;
u32 rights;
char marker;
if (cmd == CMD_BACKUP)
marker = '#';
else
marker = ' ';
printf("%*ctype %d\n",-level,marker,attr[off]);
switch (attr[off]) {
case 0 :
printf("%*cAccess allowed\n",-level-4,marker);
break;
case 1 :
printf("%*cAccess denied\n",-level-4,marker);
break;
case 2 :
printf("%*cSystem audit\n",-level-4,marker);
break;
default :
printf("%*cunknown\n",-level-4,marker);
break;
}
flags = attr[off+1] & 255;
printf("%*cflags 0x%x\n",-level,marker,flags);
if (flags & 1)
printf("%*cObject inherits ACE\n",-level-4,marker);
if (flags & 2)
printf("%*cContainer inherits ACE\n",-level-4,marker);
if (flags & 4)
printf("%*cDon\'t propagate inherits ACE\n",-level-4,marker);
if (flags & 8)
printf("%*cInherit only ACE\n",-level-4,marker);
if (flags & 0x10)
printf("%*cACE was inherited\n",-level-4,marker);
if (flags & 0x40)
printf("%*cAudit on success\n",-level-4,marker);
if (flags & 0x80)
printf("%*cAudit on failure\n",-level-4,marker);
printf("%*cSize 0x%x\n",-level,marker,get2l(attr,off+2));
rights = get4l(attr,off+4);
printf("%*cAcc rgts 0x%lx\n",-level,marker,(long)rights);
printf("%*cObj specific acc rgts 0x%lx\n",-level-4,marker,(long)rights & 65535);
if (isdir) /* a directory */ {
if (rights & 0x01)
printf("%*cList directory\n",-level-8,marker);
if (rights & 0x02)
printf("%*cAdd file\n",-level-8,marker);
if (rights & 0x04)
printf("%*cAdd subdirectory\n",-level-8,marker);
if (rights & 0x08)
printf("%*cRead EA\n",-level-8,marker);
if (rights & 0x10)
printf("%*cWrite EA\n",-level-8,marker);
if (rights & 0x20)
printf("%*cTraverse\n",-level-8,marker);
if (rights & 0x40)
printf("%*cDelete child\n",-level-8,marker);
if (rights & 0x80)
printf("%*cRead attributes\n",-level-8,marker);
if (rights & 0x100)
printf("%*cWrite attributes\n",-level-8,marker);
}
else {
/* see FILE_READ_DATA etc in winnt.h */
if (rights & 0x01)
printf("%*cRead data\n",-level-8,marker);
if (rights & 0x02)
printf("%*cWrite data\n",-level-8,marker);
if (rights & 0x04)
printf("%*cAppend data\n",-level-8,marker);
if (rights & 0x08)
printf("%*cRead EA\n",-level-8,marker);
if (rights & 0x10)
printf("%*cWrite EA\n",-level-8,marker);
if (rights & 0x20)
printf("%*cExecute\n",-level-8,marker);
if (rights & 0x80)
printf("%*cRead attributes\n",-level-8,marker);
if (rights & 0x100)
printf("%*cWrite attributes\n",-level-8,marker);
}
printf("%*cstandard acc rgts 0x%lx\n",-level-4,marker,(long)(rights >> 16) & 127);
if (rights & 0x10000)
printf("%*cDelete\n",-level-8,marker);
if (rights & 0x20000)
printf("%*cRead control\n",-level-8,marker);
if (rights & 0x40000)
printf("%*cWrite DAC\n",-level-8,marker);
if (rights & 0x80000)
printf("%*cWrite owner\n",-level-8,marker);
if (rights & 0x100000)
printf("%*cSynchronize\n",-level-8,marker);
if (rights & 0x800000)
printf("%*cCan access security ACL\n",-level-4,marker);
if (rights & 0x10000000)
printf("%*cGeneric all\n",-level-4,marker);
if (rights & 0x20000000)
printf("%*cGeneric execute\n",-level-4,marker);
if (rights & 0x40000000)
printf("%*cGeneric write\n",-level-4,marker);
if (rights & 0x80000000)
printf("%*cGeneric read\n",-level-4,marker);
printf("%*cSID at 0x%x\n",-level,marker,off+8);
showsid(attr,off+8,"",level+4);
printf("%*cSummary :",-level,marker);
if (attr[off] == 0)
printf(" grant");
if (attr[off] == 1)
printf(" deny");
if (rights & le32_to_cpu(FILE_GREAD | FILE_GWRITE | FILE_GEXEC)) {
printf(" ");
if (rights & le32_to_cpu(FILE_GREAD))
printf("r");
if (rights & le32_to_cpu(FILE_GWRITE))
printf("w");
if (rights & le32_to_cpu(FILE_GEXEC))
printf("x");
} else
printf(" none");
if (flags & 11)
printf(" inherited");
if (!(flags & 8)) {
int sz;
printf(" applied");
sz = attr[off+9]*4 + 8;
if (!memcmp(&attr[off+8],&attr[get4l(attr,4)],sz))
printf(" to owner");
if (!memcmp(&attr[off+8],&attr[get4l(attr,8)],sz))
printf(" to group");
}
printf("\n");
}
static void showacl(const char *attr, int off, int isdir, int level)
{
int i;
int cnt;
int size;
int x;
char marker;
if (cmd == CMD_BACKUP)
marker = '#';
else
marker = ' ';
size = get2l(attr,off+2);
printf("%*crevision %d\n",-level,marker,attr[off]);
printf("%*cACL size %d\n",-level,marker,size);
cnt = get2l(attr,off+4);
printf("%*cACE cnt %d\n",-level,marker,cnt);
x = 8;
for (i=0; (i<cnt) && (x < size); i++) {
printf("%*cACE %d at 0x%x\n",-level,marker,i + 1,off+x);
showace(attr,off + x,isdir,level+4);
x += get2l(attr,off + x + 2);
}
}
static void showdacl(const char *attr, int isdir, int level)
{
int off;
char marker;
if (cmd == CMD_BACKUP)
marker = '#';
else
marker = ' ';
off = get4l(attr,16);
if (off) {
if (level)
printf("%*c",-level,marker);
printf("DACL\n");
showacl(attr,off,isdir,level+4);
} else {
if (level)
printf("%*c",-level,marker);
printf("No DACL\n");
}
}
static void showsacl(const char *attr, int isdir, int level)
{
int off;
char marker;
if (cmd == CMD_BACKUP)
marker = '#';
else
marker = ' ';
off = get4l(attr,12);
if (off) {
if (level)
printf("%*c",-level,marker);
printf("SACL\n");
showacl(attr,off,isdir,level+4);
}
else {
if (level)
printf("%*c",-level,marker);
printf("No SACL\n");
}
}
static void showall(const char *attr, int level)
{
BOOL isdir;
isdir = guess_dir(attr);
showheader(attr,level);
showusid(attr,level);
showgsid(attr,level);
showdacl(attr,isdir,level);
showsacl(attr,isdir,level);
}
#if POSIXACLS
/*
* Display a Posix descriptor
*/
static void showposix(const struct POSIX_SECURITY *pxdesc)
{
char txperm[4];
const char *txtag;
const char *txtype;
const struct POSIX_ACL *acl;
const struct POSIX_ACE *pxace;
int acccnt;
int defcnt;
int firstdef;
int perms;
u16 tag;
s32 id;
int k,l;
if (pxdesc) {
acccnt = pxdesc->acccnt;
defcnt = pxdesc->defcnt;
firstdef = pxdesc->firstdef;
acl = &pxdesc->acl;
printf("Posix descriptor :\n");
printf(" acccnt %d\n",acccnt);
printf(" defcnt %d\n",defcnt);
printf(" firstdef %d\n",firstdef);
printf(" mode : 0%03o\n",(int)pxdesc->mode);
printf(" tagsset : 0x%02x\n",(int)pxdesc->tagsset);
printf("Posix ACL :\n");
printf(" version %d\n",(int)acl->version);
printf(" flags 0x%02x\n",(int)acl->flags);
for (k=0; k<(acccnt + defcnt); k++) {
if (k < acccnt)
l = k;
else
l = firstdef + k - acccnt;
pxace = &acl->ace[l];
tag = pxace->tag;
perms = pxace->perms;
if (tag == POSIX_ACL_SPECIAL) {
txperm[0] = (perms & S_ISVTX ? 's' : '-');
txperm[1] = (perms & S_ISUID ? 'u' : '-');
txperm[2] = (perms & S_ISGID ? 'g' : '-');
} else {
txperm[0] = (perms & 4 ? 'r' : '-');
txperm[1] = (perms & 2 ? 'w' : '-');
txperm[2] = (perms & 1 ? 'x' : '-');
}
txperm[3] = 0;
if (k >= acccnt)
txtype = "default";
else
txtype = "access ";
switch (tag) {
case POSIX_ACL_USER :
txtag = "USER ";
break;
case POSIX_ACL_USER_OBJ :
txtag = "USR-O";
break;
case POSIX_ACL_GROUP :
txtag = "GROUP";
break;
case POSIX_ACL_GROUP_OBJ :
txtag = "GRP-O";
break;
case POSIX_ACL_MASK :
txtag = "MASK ";
break;
case POSIX_ACL_OTHER :
txtag = "OTHER";
break;
case POSIX_ACL_SPECIAL :
txtag = "SPECL";
break;
default :
txtag = "UNKWN";
break;
}
id = pxace->id;
printf("ace %d : %s %s %4ld perms 0%03o %s\n",
l,txtype,txtag,(long)id,
perms,txperm);
}
} else
printf("** NULL ACL\n");
}
#endif /* POSIXACLS */
/*
* Relay to get uid as defined during mounting
*/
static uid_t relay_find_user(const struct MAPPING *mapping __attribute__((unused)),
const SID *usid)
{
int uid;
uid = ntfs_get_user(ntfs_context, usid);
return (uid < 0 ? 0 : uid);
}
/*
* Relay to get gid as defined during mounting
*/
static gid_t relay_find_group(const struct MAPPING *mapping __attribute__((unused)),
const SID *gsid)
{
int gid;
gid = ntfs_get_group(ntfs_context, gsid);
return (gid < 0 ? 0 : gid);
}
#if POSIXACLS
static struct POSIX_SECURITY *linux_permissions_posix(const char *attr, BOOL isdir)
{
const SECURITY_DESCRIPTOR_RELATIVE *phead;
#if OWNERFROMACL
const SID *osid;
#endif /* OWNERFROMACL */
const SID *usid;
const SID *gsid;
struct POSIX_SECURITY *posix_desc;
phead = (const SECURITY_DESCRIPTOR_RELATIVE*)attr;
gsid = (const SID*)&attr[le32_to_cpu(phead->group)];
#if OWNERFROMACL
osid = (const SID*)&attr[le32_to_cpu(phead->owner)];
usid = ntfs_acl_owner((const char*)attr);
#ifdef SELFTESTS
if ((cmd != CMD_TEST) && !ntfs_same_sid(usid,osid))
printf("== Linux owner is different from Windows owner\n");
#else /* SELFTESTS */
if (!ntfs_same_sid(usid,osid))
printf("== Linux owner is different from Windows owner\n");
#endif /* SELFTESTS */
#else /* OWNERFROMACL */
usid = (const SID*)&attr[le32_to_cpu(phead->owner)];
#endif /* OWNERFROMACL */
if (mappingtype == MAPEXTERN)
posix_desc = ntfs_build_permissions_posix(
ntfs_context->security.mapping,
(const char*)attr, usid, gsid, isdir);
else
posix_desc = ntfs_build_permissions_posix(context.mapping,
(const char*)attr, usid, gsid, isdir);
if (!posix_desc) {
printf("** Failed to build a Posix descriptor\n");
errors++;
}
return (posix_desc);
}
#endif /* POSIXACLS */
static int linux_permissions(const char *attr, BOOL isdir)
{
const SECURITY_DESCRIPTOR_RELATIVE *phead;
#if OWNERFROMACL
const SID *osid;
#endif /* OWNERFROMACL */
const SID *usid;
const SID *gsid;
int perm;
phead = (const SECURITY_DESCRIPTOR_RELATIVE*)attr;
gsid = (const SID*)&attr[le32_to_cpu(phead->group)];
#if OWNERFROMACL
osid = (const SID*)&attr[le32_to_cpu(phead->owner)];
usid = ntfs_acl_owner((const char*)attr);
#ifdef SELFTESTS
if ((cmd != CMD_TEST) && !ntfs_same_sid(usid,osid))
printf("== Linux owner is different from Windows owner\n");
#else /* SELFTESTS */
if (!ntfs_same_sid(usid,osid))
printf("== Linux owner is different from Windows owner\n");
#endif /* SELFTESTS */
#else /* OWNERFROMACL */
usid = (const SID*)&attr[le32_to_cpu(phead->owner)];
#endif /* OWNERFROMACL */
perm = ntfs_build_permissions((const char*)attr, usid, gsid, isdir);
if (perm < 0) {
printf("** Failed to build permissions\n");
errors++;
}
return (perm);
}
static uid_t linux_owner(const char *attr)
{
const SID *usid;
uid_t uid;
#if OWNERFROMACL
usid = ntfs_acl_owner((const char*)attr);
#else /* OWNERFROMACL */
const SECURITY_DESCRIPTOR_RELATIVE *phead;
phead = (const SECURITY_DESCRIPTOR_RELATIVE*)attr;
usid = (const SID*)&attr[le32_to_cpu(phead->owner)];
#endif /* OWNERFROMACL */
#ifdef HAVE_WINDOWS_H
uid = ntfs_find_user(context.mapping[MAPUSERS],usid);
#else /* defined(HAVE_WINDOWS_H) */
if (mappingtype == MAPEXTERN)
uid = relay_find_user(context.mapping[MAPUSERS],usid);
else
uid = ntfs_find_user(context.mapping[MAPUSERS],usid);
#endif /* defined(HAVE_WINDOWS_H) */
return (uid);
}
static gid_t linux_group(const char *attr)
{
const SECURITY_DESCRIPTOR_RELATIVE *phead;
const SID *gsid;
gid_t gid;
phead = (const SECURITY_DESCRIPTOR_RELATIVE*)attr;
gsid = (const SID*)&attr[le32_to_cpu(phead->group)];
#ifdef HAVE_WINDOWS_H
gid = ntfs_find_group(context.mapping[MAPGROUPS],gsid);
#else /* defined(HAVE_WINDOWS_H) */
if (mappingtype == MAPEXTERN)
gid = relay_find_group(context.mapping[MAPGROUPS],gsid);
else
gid = ntfs_find_group(context.mapping[MAPGROUPS],gsid);
#endif /* defined(HAVE_WINDOWS_H) */
return (gid);
}
static void newblock(s32 key)
{
struct SECURITY_DATA *psecurdata;
int i;
if ((key > 0) && (key < MAXSECURID) && !securdata[key >> SECBLKSZ]) {
securdata[key >> SECBLKSZ] =
(struct SECURITY_DATA*)malloc((1 << SECBLKSZ)*sizeof(struct SECURITY_DATA));
if (securdata[key >> SECBLKSZ])
for (i=0; i<(1 << SECBLKSZ); i++) {
psecurdata = &securdata[key >> SECBLKSZ][i];
psecurdata->filecount = 0;
psecurdata->mode = 0;
psecurdata->flags = 0;
psecurdata->attr = (char*)NULL;
}
}
}
static void freeblocks(void)
{
int i,j;
struct SECURITY_DATA *psecurdata;
for (i=0; i<(MAXSECURID + (1 << SECBLKSZ) - 1)/(1 << SECBLKSZ); i++)
if (securdata[i]) {
for (j=0; j<(1 << SECBLKSZ); j++) {
psecurdata = &securdata[i][j];
if (psecurdata->attr)
free(psecurdata->attr);
}
free(securdata[i]);
}
}
/*
* Basic read from a user mapping file (Win32)
*/
static int basicread(void *fileid, char *buf, size_t size,
off_t pos __attribute__((unused)))
{
return (read(*(int*)fileid, buf, size));
}
#ifdef SELFTESTS
/*
* Read a dummy mapping file for tests
*/
static int dummyread(void *fileid __attribute__((unused)),
char *buf, size_t size, off_t pos)
{
size_t sz;
if (pos >= (off_t)(sizeof(dummymapping) - 1))
sz = 0;
else
if ((size + pos) >= (sizeof(dummymapping) - 1))
sz = sizeof(dummymapping) - 1 - pos;
else
sz = size;
if (sz > 0)
memcpy(buf,&dummymapping[pos],sz);
return (sz);
}
#endif /* SELFTESTS */
/*
* Apply default single user mapping
* returns zero if successful
*/
static int do_default_mapping(struct MAPPING *mapping[],
const SID *usid)
{
struct MAPPING *usermapping;
struct MAPPING *groupmapping;
SID *sid;
int sidsz;
int res;
res = -1;
sidsz = ntfs_sid_size(usid);
sid = (SID*)ntfs_malloc(sidsz);
if (sid) {
memcpy(sid,usid,sidsz);
usermapping = (struct MAPPING*)ntfs_malloc(sizeof(struct MAPPING));
if (usermapping) {
groupmapping = (struct MAPPING*)ntfs_malloc(sizeof(struct MAPPING));
if (groupmapping) {
usermapping->sid = sid;
usermapping->xid = 0;
usermapping->next = (struct MAPPING*)NULL;
groupmapping->sid = sid;
groupmapping->xid = 0;
groupmapping->next = (struct MAPPING*)NULL;
mapping[MAPUSERS] = usermapping;
mapping[MAPGROUPS] = groupmapping;
res = 0;
}
}
}
return (res);
}
/*
* Build the user mapping
* - according to a mapping file if defined (or default present),
* - or try default single user mapping if possible
*
* The mapping is specific to a mounted device
* No locking done, mounting assumed non multithreaded
*
* returns zero if mapping is successful
* (failure should not be interpreted as an error)
*/
static int local_build_mapping(struct MAPPING *mapping[], const char *usermap_path)
{
#ifdef HAVE_WINDOWS_H
char mapfile[sizeof(MAPDIR) + sizeof(MAPFILE) + 6];
char currpath[MAXFILENAME];
#else /* HAVE_WINDOWS_H */
char *mapfile;
char *p;
#endif /* HAVE_WINDOWS_H */
int fd;
struct MAPLIST *item;
struct MAPLIST *firstitem = (struct MAPLIST*)NULL;
struct MAPPING *usermapping;
struct MAPPING *groupmapping;
static struct {
u8 revision;
u8 levels;
be16 highbase;
be32 lowbase;
le32 level1;
le32 level2;
le32 level3;
le32 level4;
le32 level5;
} defmap = {
1, 5, const_cpu_to_be16(0), const_cpu_to_be32(5),
const_cpu_to_le32(21),
const_cpu_to_le32(DEFSECAUTH1), const_cpu_to_le32(DEFSECAUTH2),
const_cpu_to_le32(DEFSECAUTH3), const_cpu_to_le32(DEFSECBASE)
} ;
/* be sure not to map anything until done */
mapping[MAPUSERS] = (struct MAPPING*)NULL;
mapping[MAPGROUPS] = (struct MAPPING*)NULL;
if (usermap_path) {
#ifdef HAVE_WINDOWS_H
/* TODO : check whether the device can store acls */
strcpy(mapfile,"x:\\" MAPDIR "\\" MAPFILE);
if (((const le16*)usermap_path)[1] == ':')
mapfile[0] = usermap_path[0];
else {
getcwd(currpath,MAXFILENAME);
mapfile[0] = currpath[0];
}
fd = open(mapfile,O_RDONLY);
#else /* HAVE_WINDOWS_H */
fd = 0;
mapfile = (char*)malloc(MAXFILENAME);
if (mapfile) {
/* build a full path to locate the mapping file */
/*
if ((usermap_path[0] != '/')
&& getcwd(mapfile,MAXFILENAME)) {
strcat(mapfile,"/");
strcat(mapfile,usermap_path);
} else
strcpy(mapfile,usermap_path);
*/
p = ntfs_realpath(usermap_path, mapfile);
if (p)
p = strrchr(mapfile,'/');
if (p)
do {
strcpy(p,"/" MAPDIR "/" MAPFILE);
fd = open(mapfile,O_RDONLY);
if (fd <= 0) {
*p = 0;
p = strrchr(mapfile,'/');
if (p == mapfile)
p = (char*)NULL;
}
} while ((fd <= 0) && p);
free(mapfile);
if (!p) {
printf("** Could not find the user mapping file\n");
if (usermap_path[0] != '/')
printf(" Retry with full path of file\n");
errors++;
}
}
#endif /* HAVE_WINDOWS_H */
if (fd > 0) {
firstitem = ntfs_read_mapping(basicread, (void*)&fd);
close(fd);
}
} else {
#ifdef SELFTESTS
firstitem = ntfs_read_mapping(dummyread, (void*)NULL);
#endif /* SELFTESTS */
}
if (firstitem) {
usermapping = ntfs_do_user_mapping(firstitem);
groupmapping = ntfs_do_group_mapping(firstitem);
if (usermapping && groupmapping) {
mapping[MAPUSERS] = usermapping;
mapping[MAPGROUPS] = groupmapping;
} else
ntfs_log_error("There were no valid user or no valid group\n");
/* now we can free the memory copy of input text */
/* and rely on internal representation */
while (firstitem) {
item = firstitem->next;
free(firstitem);
firstitem = item;
}
} else {
do_default_mapping(mapping,(const SID*)&defmap);
}
if (mapping[MAPUSERS])
mappingtype = MAPLOCAL;
return (!mapping[MAPUSERS]);
}
/*
* Get an hexadecimal number (source with MSB first)
*/
static u32 getmsbhex(const char *text)
{
u32 v;
int b;
BOOL ok;
v = 0;
ok = TRUE;
do {
b = *text++;
if ((b >= '0') && (b <= '9'))
v = (v << 4) + b - '0';
else
if ((b >= 'a') && (b <= 'f'))
v = (v << 4) + b - 'a' + 10;
else
if ((b >= 'A') && (b <= 'F'))
v = (v << 4) + b - 'A' + 10;
else ok = FALSE;
} while (ok);
return (v);
}
/*
* Get an hexadecimal number (source with LSB first)
* An odd number of digits might yield a strange result
*/
static u32 getlsbhex(const char *text)
{
u32 v;
int b;
BOOL ok;
int pos;
v = 0;
ok = TRUE;
pos = 0;
do {
b = *text++;
if ((b >= '0') && (b <= '9'))
v |= (u32)(b - '0') << (pos ^ 4);
else
if ((b >= 'a') && (b <= 'f'))
v |= (u32)(b - 'a' + 10) << (pos ^ 4);
else
if ((b >= 'A') && (b <= 'F'))
v |= (u32)(b - 'A' + 10) << (pos ^ 4);
else ok = FALSE;
pos += 4;
} while (ok);
return (v);
}
/*
* Check whether a line looks like an hex dump
*/
static BOOL ishexdump(const char *line, int first, int lth)
{
BOOL ok;
int i;
int b;
ok = (first >= 0) && (lth >= (first + 16));
for (i=0; ((first+i)<lth) && ok; i++) {
b = line[first + i];
if ((i == 6)
|| (i == 7)
|| (i == 16)
|| (i == 25)
|| (i == 34)
|| (i == 43))
ok = (b == ' ') || (b == '\n');
else
ok = ((b >= '0') && (b <= '9'))
|| ((b >= 'a') && (b <= 'f'))
|| ((b >= 'A') && (b <= 'F'));
}
return (ok);
}
/*
* Display security descriptors from a file
* This is typically to convert a verbose output to a very verbose one
*/
static void showhex(FILE *fd)
{
static char attr[MAXATTRSZ];
char line[MAXLINE+1];
#if POSIXACLS
struct POSIX_SECURITY *pxdesc;
#endif /* POSIXACLS */
int lth;
int first;
unsigned int pos;
u32 v;
int c;
int isdir;
int mode;
unsigned int off;
int i;
le32 *pattr;
BOOL acceptable;
BOOL isdump;
BOOL done;
pos = 0;
off = 0;
done = FALSE;
do {
/* input a (partial) line without displaying */
lth = 0;
first = -1;
do {
c = getc(fd);
if ((c != ' ') && (first < 0))
first = lth;
if (c == EOF)
done = TRUE;
else
if (c != '\r')
line[lth++] = c;
} while (!done && (c != '\n') && (lth < MAXLINE));
/* check whether this looks like an hexadecimal dump */
isdump = ishexdump(line, first, lth);
if (isdump) off = getmsbhex(&line[first]);
/* line is not an hexadecimal dump */
/* display what we have in store if acceptable */
acceptable = ((!isdump || !off)
&& (pos >= 20))
&& (pos > get4l(attr,4))
&& (pos > get4l(attr,8))
&& (pos > get4l(attr,12))
&& (pos > get4l(attr,16))
&& (pos >= ntfs_attr_size(attr));
if (acceptable) {
printf(" Computed hash : 0x%08lx\n",
(unsigned long)hash((le32*)attr,
ntfs_attr_size(attr)));
isdir = guess_dir(attr);
printf(" Estimated type : %s\n",
(isdir ? "directory" : "file"));
if (!ntfs_valid_descr((char*)attr,pos)) {
printf("** Bad descriptor,"
" trying to display anyway\n");
errors++;
}
showheader(attr,4);
showusid(attr,4);
showgsid(attr,4);
showdacl(attr,isdir,4);
showsacl(attr,isdir,4);
showownership(attr);
mode = linux_permissions(attr,isdir);
printf("Interpreted Unix mode 0%03o\n",mode);
#if POSIXACLS
/*
* Posix display not possible when user
* mapping is not available (option -h)
*/
if (mappingtype != MAPNONE) {
pxdesc = linux_permissions_posix(attr,isdir);
if (pxdesc) {
showposix(pxdesc);
free(pxdesc);
}
}
#endif /* POSIXACLS */
pos = 0;
}
if (isdump && !off)
pos = off;
/* line looks like an hexadecimal dump */
/* decode it into attribute */
if (isdump && (off == pos)) {
for (i=first+8; i<lth; i+=9) {
pattr = (le32*)&attr[pos];
v = getlsbhex(&line[i]);
*pattr = cpu_to_le32(v);
pos += 4;
}
}
/* display (full) current line */
if (lth) printf("! ");
for (i=0; i<lth; i++) {
c = line[i];
putchar(c);
}
while (!done && (c != '\n')) {
c = getc(fd);
if (c == EOF)
done = TRUE;
else
putchar(c);
}
} while (!done);
}
static BOOL applyattr(const char *fullname, const char *attr,
BOOL withattr, int attrib, s32 key)
{
struct SECURITY_DATA *psecurdata;
const char *curattr;
char *newattr;
int selection;
BOOL bad;
BOOL badattrib;
BOOL err;
err = FALSE;
psecurdata = (struct SECURITY_DATA*)NULL;
curattr = (const char*)NULL;
newattr = (char*)NULL;
if ((key > 0) && (key < MAXSECURID)) {
if (!securdata[key >> SECBLKSZ])
newblock(key);
if (securdata[key >> SECBLKSZ]) {
psecurdata = &securdata[key >> SECBLKSZ]
[key & ((1 << SECBLKSZ) - 1)];
}
}
/* If we have a usable attrib value. Try applying */
badattrib = FALSE;
if (opt_e && (attrib != INVALID_FILE_ATTRIBUTES)) {
badattrib = !ntfs_set_file_attributes(ntfs_context, fullname, attrib);
if (badattrib) {
printf("** Could not set Windows attrib of ");
printname(stdout,fullname);
printf(" to 0x%x\n", attrib);
printerror(stdout);
warnings++;
}
}
if (withattr) {
if (psecurdata) {
newattr = (char*)malloc(ntfs_attr_size(attr));
if (newattr) {
memcpy(newattr,attr,ntfs_attr_size(attr));
psecurdata->attr = newattr;
}
}
curattr = attr;
} else
/*
* No explicit attr in backup, use attr defined
* previously for the same id
*/
if (psecurdata)
curattr = psecurdata->attr;
if (curattr) {
selection = OWNER_SECURITY_INFORMATION
| GROUP_SECURITY_INFORMATION
| DACL_SECURITY_INFORMATION
| SACL_SECURITY_INFORMATION;
bad = !ntfs_set_file_security(ntfs_context,fullname,
selection, (const char*)curattr);
if (bad) {
printf("** Could not set the ACL of ");
printname(stdout,fullname);
printf("\n");
printerror(stdout);
err = TRUE;
} else
if (opt_v) {
if (opt_e && !badattrib)
printf("ACL and attrib have been applied to ");
else
printf("ACL has been applied to ");
printname(stdout,fullname);
printf("\n");
}
} else {
printf("** There was no valid ACL for ");
printname(stdout,fullname);
printf("\n");
err = TRUE;
}
return (!err);
}
/*
* Restore security descriptors from a file
*/
static BOOL restore(FILE *fd)
{
static char attr[MAXATTRSZ];
char line[MAXFILENAME+25];
char fullname[MAXFILENAME+25];
SECURITY_DESCRIPTOR_RELATIVE *phead;
int lth;
int first;
unsigned int pos;
int c;
int isdir;
int mode;
s32 key;
BOOL isdump;
unsigned int off;
u32 v;
u32 oldhash;
int i;
int count;
int attrib;
le32 *pattr;
BOOL withattr;
BOOL done;
pos = 0;
off = 0;
done = FALSE;
withattr = FALSE;
oldhash = 0;
key = 0;
errors = 0;
count = 0;
fullname[0] = 0;
attrib = INVALID_FILE_ATTRIBUTES;
do {
/* input a (partial) line without processing */
lth = 0;
first = -1;
do {
c = getc(fd);
if ((c != ' ') && (first < 0))
first = lth;
if (c == EOF)
done = TRUE;
else
if (c != '\r')
line[lth++] = c;
} while (!done && (c != '\n') && (lth < (MAXFILENAME + 24)));
/* check whether this looks like an hexadecimal dump */
isdump = ishexdump(line, first, lth);
if (isdump) off = getmsbhex(&line[first]);
/* line is not an hexadecimal dump */
/* apply what we have in store, only if valid */
if ((!isdump || !off) && pos && ntfs_valid_descr((char*)attr,pos)) {
withattr = TRUE;
if (opt_v >= 2) {
printf(" Computed hash : 0x%08lx\n",
(unsigned long)hash((le32*)attr,
ntfs_attr_size(attr)));
isdir = guess_dir(attr);
printf(" Estimated type : %s\n",(isdir ? "directory" : "file"));
showheader(attr,4);
showusid(attr,4);
showgsid(attr,4);
showdacl(attr,isdir,4);
showsacl(attr,isdir,4);
mode = linux_permissions(attr,isdir);
showownership(attr);
printf("Interpreted Unix mode 0%03o\n",mode);
}
pos = 0;
}
if (isdump && !off)
pos = off;
/* line looks like an hexadecimal dump */
/* decode it into attribute */
if (isdump && (off == pos)) {
for (i=first+8; i<lth; i+=9) {
pattr = (le32*)&attr[pos];
v = getlsbhex(&line[i]);
*pattr = cpu_to_le32(v);
pos += 4;
}
}
/* display (full) current line unless dump or verbose */
if (!isdump || opt_v) {
if(lth) printf("! ");
for (i=0; i<lth; i++) {
c = line[i];
putchar(c);
}
}
while (!done && (c != '\n')) {
c = getc(fd);
if (c == EOF)
done = TRUE;
else
if (!isdump || opt_v)
putchar(c);
}
line[lth] = 0;
while ((lth > 0)
&& ((line[lth-1] == '\n') || (line[lth-1] == '\r')))
line[--lth] = 0;
if (!strncmp(line,"Computed hash : 0x",18))
oldhash = getmsbhex(&line[18]);
if (!strncmp(line,"Security key : 0x",17))
key = getmsbhex(&line[17]);
if (!strncmp(line,"Windows attrib : 0x",19))
attrib = getmsbhex(&line[19]);
if (done
|| !strncmp(line,"File ",5)
|| !strncmp(line,"Directory ",10)) {
/*
* New file or directory (or end of file) :
* apply attribute just collected
* or apply attribute defined from current key
*/
if (withattr
&& oldhash
&& (hash((const le32*)attr,ntfs_attr_size(attr)) != oldhash)) {
printf("** ACL rejected, its hash is not as expected\n");
errors++;
} else
if (fullname[0]) {
phead = (SECURITY_DESCRIPTOR_RELATIVE*)attr;
/* set the request for auto-inheritance */
if (phead->control & SE_DACL_AUTO_INHERITED)
phead->control |= SE_DACL_AUTO_INHERIT_REQ;
if (!applyattr(fullname,attr,withattr,
attrib,key))
errors++;
else
count++;
}
/* save current file or directory name */
withattr = FALSE;
key = 0;
oldhash = 0;
attrib = INVALID_FILE_ATTRIBUTES;
if (!done) {
if (!strncmp(line,"File ",5))
strcpy(fullname,&line[5]);
else
strcpy(fullname,&line[10]);
#ifdef HAVE_WINDOWS_H
cleanpath(fullname);
#endif /* HAVE_WINDOWS_H */
}
}
} while (!done);
printf("%d ACLs have been applied\n",count);
return (FALSE);
}
static BOOL dorestore(const char *volume, FILE *fd)
{
BOOL err;
err = FALSE;
if (!getuid()) {
if (open_security_api()) {
if (open_volume(volume,NTFS_MNT_NONE)) {
if (restore(fd)) err = TRUE;
close_volume(volume);
} else {
fprintf(stderr,"Could not open volume %s\n",volume);
printerror(stderr);
err = TRUE;
}
close_security_api();
} else {
fprintf(stderr,"Could not open security API\n");
printerror(stderr);
err = TRUE;
}
} else {
fprintf(stderr,"Restore is only possible as root\n");
err = TRUE;
}
return (err);
}
#if POSIXACLS
/*
* Merge Posix ACL rights into an u32 (self test only)
*
* Result format : -----rwxrwxrwxrwxrwx---rwxrwxrwx
* U1 U2 G1 G2 M o g w
*
* Only two users (U1, U2) and two groups (G1, G2) taken into account
*/
static u32 merge_rights(const struct POSIX_SECURITY *pxdesc, BOOL def)
{
const struct POSIX_ACE *pxace;
int i;
int users;
int groups;
int first;
int last;
u32 rights;
rights = 0;
users = 0;
groups = 0;
if (def) {
first = pxdesc->firstdef;
last = pxdesc->firstdef + pxdesc->defcnt - 1;
} else {
first = 0;
last = pxdesc->acccnt - 1;
}
pxace = pxdesc->acl.ace;
for (i=first; i<=last; i++) {
switch (pxace[i].tag) {
case POSIX_ACL_USER_OBJ :
rights |= (pxace[i].perms & 7) << 6;
break;
case POSIX_ACL_USER :
if (users < 2)
rights |= ((u32)pxace[i].perms & 7) << (24 - 3*users);
users++;
break;
case POSIX_ACL_GROUP_OBJ :
rights |= (pxace[i].perms & 7) << 3;
break;
case POSIX_ACL_GROUP :
if (groups < 2)
rights |= ((u32)pxace[i].perms & 7) << (18 - 3*groups);
groups++;
break;
case POSIX_ACL_MASK :
rights |= ((u32)pxace[i].perms & 7) << 12;
break;
case POSIX_ACL_OTHER :
rights |= (pxace[i].perms & 7);
break;
default :
break;
}
}
return (rights);
}
static BOOL same_posix(struct POSIX_SECURITY *pxdesc1,
struct POSIX_SECURITY *pxdesc2)
{
BOOL same;
int i;
same = pxdesc1
&& pxdesc2
&& (pxdesc1->mode == pxdesc2->mode)
&& (pxdesc1->acccnt == pxdesc2->acccnt)
&& (pxdesc1->defcnt == pxdesc2->defcnt)
&& (pxdesc1->firstdef == pxdesc2->firstdef)
&& (pxdesc1->tagsset == pxdesc2->tagsset)
&& (pxdesc1->acl.version == pxdesc2->acl.version)
&& (pxdesc1->acl.flags == pxdesc2->acl.flags);
i = 0;
while (same && (i < pxdesc1->acccnt)) {
same = (pxdesc1->acl.ace[i].tag == pxdesc2->acl.ace[i].tag)
&& (pxdesc1->acl.ace[i].perms == pxdesc2->acl.ace[i].perms)
&& (pxdesc1->acl.ace[i].id == pxdesc2->acl.ace[i].id);
i++;
}
i = pxdesc1->firstdef;
while (same && (i < pxdesc1->firstdef + pxdesc1->defcnt)) {
same = (pxdesc1->acl.ace[i].tag == pxdesc2->acl.ace[i].tag)
&& (pxdesc1->acl.ace[i].perms == pxdesc2->acl.ace[i].perms)
&& (pxdesc1->acl.ace[i].id == pxdesc2->acl.ace[i].id);
i++;
}
return (same);
}
#endif /* POSIXACLS */
#if POSIXACLS & SELFTESTS
static void tryposix(struct POSIX_SECURITY *pxdesc)
{
le32 owner_sid[] = /* S-1-5-21-3141592653-589793238-462843383-1016 */
{
cpu_to_le32(0x501), cpu_to_le32(0x05000000), cpu_to_le32(21),
cpu_to_le32(DEFSECAUTH1), cpu_to_le32(DEFSECAUTH2),
cpu_to_le32(DEFSECAUTH3), cpu_to_le32(1016)
} ;
le32 group_sid[] = /* S-1-5-21-3141592653-589793238-462843383-513 */
{
cpu_to_le32(0x501), cpu_to_le32(0x05000000), cpu_to_le32(21),
cpu_to_le32(DEFSECAUTH1), cpu_to_le32(DEFSECAUTH2),
cpu_to_le32(DEFSECAUTH3), cpu_to_le32(513)
} ;
char *attr;
BOOL isdir;
mode_t mode;
struct POSIX_SECURITY *newpxdesc;
struct POSIX_SECURITY *oldpxdesc;
static char *oldattr = (char*)NULL;
isdir = FALSE;
if (oldattr) {
oldpxdesc = linux_permissions_posix(oldattr, isdir);
newpxdesc = ntfs_merge_descr_posix(pxdesc, oldpxdesc);
if (!newpxdesc)
newpxdesc = pxdesc;
free(oldpxdesc);
if (opt_v) {
printf("merged descriptors :\n");
showposix(newpxdesc);
}
} else
newpxdesc = pxdesc;
attr = ntfs_build_descr_posix(context.mapping,newpxdesc,
isdir,(SID*)owner_sid,(SID*)group_sid);
if (attr && ntfs_valid_descr(attr, ntfs_attr_size(attr))) {
if (opt_v)
hexdump(attr,ntfs_attr_size(attr),8);
if (opt_v >= 2) {
showheader(attr,4);
showusid(attr,4);
showgsid(attr,4);
showdacl(attr,isdir,4);
showsacl(attr,isdir,4);
mode = linux_permissions(attr,isdir);
printf("Interpreted Unix mode 0%03o\n",mode);
printf("Interpreted back Posix descriptor :\n");
newpxdesc = linux_permissions_posix(attr,isdir);
showposix(newpxdesc);
free(newpxdesc);
}
if (oldattr) free(oldattr);
oldattr = attr;
}
}
#endif /* POSIXACLS & SELFTESTS */
#ifdef SELFTESTS
/*
* Build a dummy security descriptor
* returns descriptor in allocated memory, must free() after use
*/
static char *build_dummy_descr(BOOL isdir __attribute__((unused)),
const SID *usid, const SID *gsid,
int cnt,
/* seq of int allow, SID *sid, int flags, u32 mask */
...)
{
char *attr;
int attrsz;
SECURITY_DESCRIPTOR_RELATIVE *pnhead;
ACL *pacl;
ACCESS_ALLOWED_ACE *pace;
va_list ap;
const SID *sid;
u32 umask;
le32 mask;
int flags;
BOOL allow;
int pos;
int usidsz;
int gsidsz;
int sidsz;
int aclsz;
int i;
if (usid)
usidsz = ntfs_sid_size(usid);
else
usidsz = 0;
if (gsid)
gsidsz = ntfs_sid_size(gsid);
else
gsidsz = 0;
/* allocate enough space for the new security attribute */
attrsz = sizeof(SECURITY_DESCRIPTOR_RELATIVE) /* header */
+ usidsz + gsidsz /* usid and gsid */
+ sizeof(ACL) /* acl header */
+ cnt*40;
attr = (char*)ntfs_malloc(attrsz);
if (attr) {
/* build the main header part */
pnhead = (SECURITY_DESCRIPTOR_RELATIVE*) attr;
pnhead->revision = SECURITY_DESCRIPTOR_REVISION;
pnhead->alignment = 0;
/*
* The flag SE_DACL_PROTECTED prevents the ACL
* to be changed in an inheritance after creation
*/
pnhead->control = SE_DACL_PRESENT | SE_DACL_PROTECTED
| SE_SELF_RELATIVE;
/*
* Windows prefers ACL first, do the same to
* get the same hash value and avoid duplication
*/
/* build the ACL header */
pos = sizeof(SECURITY_DESCRIPTOR_RELATIVE);
pacl = (ACL*)&attr[pos];
pacl->revision = ACL_REVISION;
pacl->alignment1 = 0;
pacl->size = cpu_to_le16(0); /* fixed later */
pacl->ace_count = cpu_to_le16(cnt);
pacl->alignment2 = cpu_to_le16(0);
/* enter the ACEs */
pos += sizeof(ACL);
aclsz = sizeof(ACL);
va_start(ap,cnt);
for (i=0; i<cnt; i++) {
pace = (ACCESS_ALLOWED_ACE*)&attr[pos];
allow = va_arg(ap,int);
sid = va_arg(ap,SID*);
flags = va_arg(ap,int);
umask = va_arg(ap,u32);
mask = cpu_to_le32(umask);
sidsz = ntfs_sid_size(sid);
pace->type = (allow ? ACCESS_ALLOWED_ACE_TYPE : ACCESS_DENIED_ACE_TYPE);
pace->flags = flags;
pace->size = cpu_to_le16(sidsz + 8);
pace->mask = mask;
memcpy(&pace->sid,sid,sidsz);
aclsz += sidsz + 8;
pos += sidsz + 8;
}
va_end(ap);
/* append usid and gsid if defined */
/* positions of ACL, USID and GSID into header */
pnhead->owner = cpu_to_le32(0);
pnhead->group = cpu_to_le32(0);
if (usid) {
memcpy(&attr[pos], usid, usidsz);
pnhead->owner = cpu_to_le32(pos);
}
if (gsid) {
memcpy(&attr[pos + usidsz], gsid, gsidsz);
pnhead->group = cpu_to_le32(pos + usidsz);
}
/* positions of DACL and SACL into header */
pnhead->sacl = cpu_to_le32(0);
if (cnt) {
pacl->size = cpu_to_le16(aclsz);
pnhead->dacl =
cpu_to_le32(sizeof(SECURITY_DESCRIPTOR_RELATIVE));
} else
pnhead->dacl = cpu_to_le32(0);
if (!ntfs_valid_descr(attr,pos+usidsz+gsidsz)) {
printf("** Bad sample descriptor\n");
free(attr);
attr = (char*)NULL;
errors++;
}
} else
errno = ENOMEM;
return (attr);
}
/*
* Check a few samples with special conditions
*/
static void check_samples(void)
{
char *descr = (char*)NULL;
BOOL isdir = FALSE;
mode_t perms;
mode_t expect = 0;
int cnt;
u32 expectacc;
u32 expectdef;
#if POSIXACLS
u32 accrights;
u32 defrights;
mode_t mixmode;
struct POSIX_SECURITY *pxdesc;
struct POSIX_SECURITY *pxsample;
const char *txsample;
#endif /* POSIXACLS */
le32 owner1[] = /* S-1-5-21-1833069642-4243175381-1340018762-1003 */
{
cpu_to_le32(0x501), cpu_to_le32(0x05000000), cpu_to_le32(21),
cpu_to_le32(1833069642), cpu_to_le32(4243175381U),
cpu_to_le32(1340018762), cpu_to_le32(1003)
} ;
le32 group1[] = /* S-1-5-21-1833069642-4243175381-1340018762-513 */
{
cpu_to_le32(0x501), cpu_to_le32(0x05000000), cpu_to_le32(21),
cpu_to_le32(1833069642), cpu_to_le32(4243175381U),
cpu_to_le32(1340018762), cpu_to_le32(513)
} ;
le32 group2[] = /* S-1-5-21-1607551490-981732888-1819828000-513 */
{
cpu_to_le32(0x501), cpu_to_le32(0x05000000), cpu_to_le32(21),
cpu_to_le32(1607551490), cpu_to_le32(981732888),
cpu_to_le32(1819828000), cpu_to_le32(513)
} ;
le32 owner3[] = /* S-1-5-21-3141592653-589793238-462843383-1016 */
{
cpu_to_le32(0x501), cpu_to_le32(0x05000000), cpu_to_le32(21),
cpu_to_le32(DEFSECAUTH1), cpu_to_le32(DEFSECAUTH2),
cpu_to_le32(DEFSECAUTH3), cpu_to_le32(1016)
} ;
le32 group3[] = /* S-1-5-21-3141592653-589793238-462843383-513 */
{
cpu_to_le32(0x501), cpu_to_le32(0x05000000), cpu_to_le32(21),
cpu_to_le32(DEFSECAUTH1), cpu_to_le32(DEFSECAUTH2),
cpu_to_le32(DEFSECAUTH3), cpu_to_le32(513)
} ;
#if POSIXACLS
struct {
struct POSIX_SECURITY head;
struct POSIX_ACE ace[4];
} sampletry1 =
{
{ 0645, 4, 0, 4, 0x35, 0,
{ POSIX_VERSION, 0, 0 }
},
{
{ 1, 6, -1 },
{ 4, 5, -1 },
{ 16, 4, -1 },
{ 32, 5, -1 }
}
} ;
struct {
struct POSIX_SECURITY head;
struct POSIX_ACE ace[6];
} sampletry3 =
{
{ 0100, 6, 0, 6, 0x3f, 0,
{ POSIX_VERSION, 0, 0 }
},
{
{ 1, 1, -1 },
{ 2, 3, 1000 },
{ 4, 1, -1 },
{ 8, 3, 1002 },
{ 16, 0, -1 },
{ 32, 0, -1 }
}
} ;
struct {
struct POSIX_SECURITY head;
struct POSIX_ACE ace[8];
} sampletry4 =
{
{ 0140, 8, 0, 8, 0x3f, 0,
{ POSIX_VERSION, 0, 0 }
},
{
{ 1, 1, -1 },
{ 2, 3, 516 },
{ 2, 6, 1000 },
{ 4, 1, -1 },
{ 8, 6, 500 },
{ 8, 3, 1002 },
{ 16, 4, -1 },
{ 32, 0, -1 }
}
} ;
struct {
struct POSIX_SECURITY head;
struct POSIX_ACE ace[6];
} sampletry5 =
{
{ 0454, 6, 0, 6, 0x3f, 0,
{ POSIX_VERSION, 0, 0 }
},
{
{ 1, 4, -1 },
{ 2, 5, 516 },
{ 4, 4, -1 },
{ 8, 6, 500 },
{ 16, 5, -1 },
{ 32, 4, -1 }
}
} ;
struct {
struct POSIX_SECURITY head;
struct POSIX_ACE ace[8];
} sampletry6 =
{
{ 0332, 8, 0, 8, 0x3f, 0,
{ POSIX_VERSION, 0, 0 }
},
{
{ 1, 3, -1 },
{ 2, 1, 0 },
{ 2, 2, 1000 },
{ 4, 6, -1 },
{ 8, 4, 0 },
{ 8, 5, 1002 },
{ 16, 3, -1 },
{ 32, 2, -1 }
}
} ;
struct {
struct POSIX_SECURITY head;
struct POSIX_ACE ace[4];
} sampletry8 =
{
{ 0677, 4, 0, 4, 0x35, 0,
{ POSIX_VERSION, 0, 0 }
},
{
{ 1, 6, -1 },
{ 4, 7, -1 },
{ 16, 7, -1 },
{ 32, 7, -1 }
}
} ;
#endif /* POSIXACLS */
#if POSIXACLS
for (cnt=1; cnt<=8; cnt++) {
switch (cnt) {
case 1 :
pxsample = &sampletry1.head;
txsample = "sampletry1-a";
isdir = FALSE;
descr = ntfs_build_descr_posix(context.mapping,&sampletry1.head,
isdir, (const SID*)owner3, (const SID*)group3);
break;
case 2 :
pxsample = &sampletry1.head;
txsample = "sampletry1-b";
isdir = FALSE;
descr = ntfs_build_descr_posix(context.mapping,&sampletry1.head,
isdir, (const SID*)adminsid, (const SID*)group3);
break;
case 3 :
isdir = FALSE;
pxsample = &sampletry3.head;
txsample = "sampletry3";
descr = ntfs_build_descr_posix(context.mapping,pxsample,
isdir, (const SID*)group3, (const SID*)group3);
break;
case 4 :
isdir = FALSE;
pxsample = &sampletry4.head;
txsample = "sampletry4";
descr = ntfs_build_descr_posix(context.mapping,pxsample,
isdir, (const SID*)owner3, (const SID*)group3);
break;
case 5 :
isdir = FALSE;
pxsample = &sampletry5.head;
txsample = "sampletry5";
descr = ntfs_build_descr_posix(context.mapping,pxsample,
isdir, (const SID*)owner3, (const SID*)group3);
break;
case 6 :
isdir = FALSE;
pxsample = &sampletry6.head;
txsample = "sampletry6-a";
descr = ntfs_build_descr_posix(context.mapping,pxsample,
isdir, (const SID*)owner3, (const SID*)group3);
break;
case 7 :
isdir = FALSE;
pxsample = &sampletry6.head;
txsample = "sampletry6-b";
descr = ntfs_build_descr_posix(context.mapping,pxsample,
isdir, (const SID*)adminsid, (const SID*)adminsid);
break;
case 8 :
pxsample = &sampletry8.head;
txsample = "sampletry8";
isdir = FALSE;
descr = ntfs_build_descr_posix(context.mapping,&sampletry8.head,
isdir, (const SID*)owner3, (const SID*)group3);
break;
default :
pxsample = (struct POSIX_SECURITY*)NULL;
txsample = (const char*)NULL;
}
/* check we get original back */
if (descr)
pxdesc = linux_permissions_posix(descr, isdir);
else
pxdesc = (struct POSIX_SECURITY*)NULL;
if (!descr || !pxdesc || !same_posix(pxsample,pxdesc)) {
printf("** Error in %s (errno %d)\n",txsample,errno);
showposix(pxsample);
if (descr)
showall(descr,0);
if (pxdesc)
showposix(pxdesc);
errors++;
}
free(descr);
free(pxdesc);
}
#endif /* POSIXACLS */
/*
* Check a few samples built by Windows,
* which cannot be generated by Linux
*/
for (cnt=1; cnt<=10; cnt++) {
switch(cnt) {
case 1 : /* hp/tmp */
isdir = TRUE;
descr = build_dummy_descr(isdir,
(const SID*)owner1, (const SID*)group1,
1,
(int)TRUE, worldsid, (int)0x3, (u32)0x1f01ff);
expect = expectacc = 0777;
expectdef = 0;
break;
case 2 : /* swsetup */
isdir = TRUE;
descr = build_dummy_descr(isdir, adminsid, (const SID*)group2,
2,
(int)TRUE, worldsid, (int)0x0, (u32)0x1f01ff,
(int)TRUE, worldsid, (int)0xb, (u32)0x1f01ff);
expectacc = expect = 0777;
expectdef = 0777;
break;
case 3 : /* Dr Watson */
isdir = TRUE;
descr = build_dummy_descr(isdir, (const SID*)owner3, (const SID*)group3,
0);
expectacc = expect = 0700;
expectdef = 0;
break;
case 4 :
isdir = FALSE;
descr = build_dummy_descr(isdir,
(const SID*)owner3, (const SID*)group3,
4,
(int)TRUE, (const SID*)owner3, 0,
le32_to_cpu(FILE_READ_DATA | OWNER_RIGHTS),
(int)TRUE, (const SID*)group3, 0,
le32_to_cpu(FILE_WRITE_DATA),
(int)TRUE, (const SID*)group2, 0,
le32_to_cpu(FILE_WRITE_DATA | FILE_READ_DATA),
(int)TRUE, (const SID*)worldsid, 0,
le32_to_cpu(FILE_EXECUTE));
expect = 0731;
expectacc = 07070731;
expectdef = 0;
break;
case 5 : /* Vista/JP */
isdir = TRUE;
descr = build_dummy_descr(isdir, systemsid, systemsid,
6,
(int)TRUE, owner1, (int)0x0, (u32)0x1f01ff,
(int)TRUE, systemsid, (int)0x0, (u32)0x1f01ff,
(int)TRUE, adminsid, (int)0x0, (u32)0x1f01ff,
(int)TRUE, owner1, (int)0xb, (u32)0x10000000,
(int)TRUE, systemsid, (int)0xb, (u32)0x10000000,
(int)TRUE, adminsid, (int)0xb, (u32)0x10000000);
expectacc = expect = 0700;
expectdef = 0700;
break;
case 6 : /* Vista/JP2 */
isdir = TRUE;
descr = build_dummy_descr(isdir, systemsid, systemsid,
7,
(int)TRUE, owner1, (int)0x0, (u32)0x1f01ff,
(int)TRUE, systemsid, (int)0x0, (u32)0x1f01ff,
(int)TRUE, adminsid, (int)0x0, (u32)0x1f01ff,
(int)TRUE, owner1, (int)0xb, (u32)0x1f01ff,
(int)TRUE, systemsid, (int)0xb, (u32)0x1f01ff,
(int)TRUE, adminsid, (int)0xb, (u32)0x1f01ff,
(int)TRUE, owner3, (int)0x3, (u32)0x1200a9);
expectacc = 0500070700;
expectdef = expect = 0700;
break;
case 7 : /* WinXP/JP */
isdir = TRUE;
descr = build_dummy_descr(isdir, adminsid, systemsid,
6,
(int)TRUE, owner1, (int)0x0, (u32)0x1f01ff,
(int)TRUE, systemsid, (int)0x0, (u32)0x1f01ff,
(int)TRUE, adminsid, (int)0x0, (u32)0x1f01ff,
(int)TRUE, owner1, (int)0xb, (u32)0x10000000,
(int)TRUE, systemsid, (int)0xb, (u32)0x10000000,
(int)TRUE, adminsid, (int)0xb, (u32)0x10000000);
expectacc = expect = 0700;
expectdef = 0700;
break;
case 8 : /* WinXP/JP2 */
isdir = TRUE;
descr = build_dummy_descr(isdir, adminsid, systemsid,
6,
(int)TRUE, owner1, (int)0x0, (u32)0x1f01ff,
(int)TRUE, systemsid, (int)0x0, (u32)0x1f01ff,
(int)TRUE, adminsid, (int)0x0, (u32)0x1f01ff,
(int)TRUE, owner1, (int)0xb, (u32)0x10000000,
(int)TRUE, systemsid, (int)0xb, (u32)0x10000000,
(int)TRUE, adminsid, (int)0xb, (u32)0x10000000);
expectacc = expect = 0700;
expectdef = 0700;
break;
case 9 : /* Win8/bin */
isdir = TRUE;
descr = build_dummy_descr(isdir,
(const SID*)owner3, (const SID*)owner3,
6,
(int)TRUE, authsid, (int)0x3, (u32)0x1f01ff,
(int)TRUE, adminsid, (int)0x13, (u32)0x1f01ff,
(int)TRUE, systemsid, (int)0x13, (u32)0x1f01ff,
(int)TRUE, localsid, (int)0x13, (u32)0x1200a9,
(int)TRUE, authsid, (int)0x10, (u32)0x1301bf,
(int)TRUE, authsid, (int)0x1b, (u32)0xe0010000);
expectacc = expect = 0777;
expectdef = 0777;
break;
case 10 : /* Win8/bin/linem.exe */
isdir = FALSE;
descr = build_dummy_descr(isdir,
(const SID*)owner3, (const SID*)owner3,
4,
(int)TRUE, authsid, (int)0x10, (u32)0x1f01ff,
(int)TRUE, adminsid, (int)0x10, (u32)0x1f01ff,
(int)TRUE, systemsid, (int)0x10, (u32)0x1ff,
(int)TRUE, localsid, (int)0x10, (u32)0x1200a9);
expectacc = expect = 0777;
expectdef = 0;
break;
default :
expectacc = expectdef = 0;
break;
}
if (descr) {
perms = linux_permissions(descr, isdir);
if (perms != expect) {
printf("** Error in sample %d, perms 0%03o expected 0%03o\n",
cnt,perms,expect);
showall(descr,0);
errors++;
} else {
#if POSIXACLS
pxdesc = linux_permissions_posix(descr, isdir);
if (pxdesc) {
accrights = merge_rights(pxdesc,FALSE);
defrights = merge_rights(pxdesc,TRUE);
if (!(pxdesc->tagsset & ~(POSIX_ACL_USER_OBJ | POSIX_ACL_GROUP_OBJ | POSIX_ACL_OTHER)))
mixmode = expect;
else
mixmode = (expect & 07707) | ((accrights >> 9) & 070);
if ((pxdesc->mode != mixmode)
|| (accrights != expectacc)
|| (defrights != expectdef)) {
printf("** Error in sample %d : mode %03o expected 0%03o\n",
cnt,pxdesc->mode,mixmode);
printf(" Posix access rights 0%03lo expected 0%03lo\n",
(long)accrights,(long)expectacc);
printf(" default rights 0%03lo expected 0%03lo\n",
(long)defrights,(long)expectdef);
showall(descr,0);
showposix(pxdesc);
}
free(pxdesc);
}
#endif /* POSIXACLS */
}
free(descr);
}
}
}
/*
* Check whether any basic permission setting is interpreted
* back exactly as set
*/
static void basictest(int kind, BOOL isdir, const SID *owner, const SID *group)
{
char *attr;
mode_t perm;
mode_t gotback;
u32 count;
u32 acecount;
u32 globhash;
const SECURITY_DESCRIPTOR_RELATIVE *phead;
const ACL *pacl;
enum { ERRNO,
ERRMA, ERRPA, /* error converting mode or Posix ACL to NTFS */
ERRAM, ERRAP, /* error converting NTFS to mode or Posix ACL */
} err;
u32 expectcnt[] = {
27800, 31896,
24064, 28160,
24064, 28160,
24064, 28160,
24904, 29000
} ;
u32 expecthash[] = {
0x8f80865b, 0x7bc7960,
0x8fd9ecfe, 0xddd4db0,
0xa8b07400, 0xa189c20,
0xc5689a00, 0xb6c09000,
0xb040e509, 0x4f4db7f7
} ;
#if POSIXACLS
struct POSIX_SECURITY *pxdesc;
char *pxattr;
u32 pxcount;
u32 pxacecount;
u32 pxglobhash;
#endif /* POSIXACLS */
count = 0;
acecount = 0;
globhash = 0;
#if POSIXACLS
pxcount = 0;
pxacecount = 0;
pxglobhash = 0;
#endif /* POSIXACLS */
for (perm=0; (perm<=07777) && (errors < 10); perm++) {
err = ERRNO;
/* file owned by plain user and group */
attr = ntfs_build_descr(perm,isdir,owner,(const SID*)group);
if (attr && ntfs_valid_descr(attr, ntfs_attr_size(attr))) {
phead = (const SECURITY_DESCRIPTOR_RELATIVE*)attr;
pacl = (const ACL*)&attr[le32_to_cpu(phead->dacl)];
acecount += le16_to_cpu(pacl->ace_count);
globhash += hash((const le32*)attr,ntfs_attr_size(attr));
count++;
#if POSIXACLS
/*
* Build a NTFS ACL from a mode, and
* decode to a Posix ACL, expecting to
* get the original mode back.
*/
pxdesc = linux_permissions_posix(attr, isdir);
if (!pxdesc || (pxdesc->mode != perm)) {
err = ERRAP;
if (pxdesc)
gotback = pxdesc->mode;
else
gotback = 0;
} else {
/*
* Build a NTFS ACL from the Posix ACL, expecting to
* get exactly the same NTFS ACL, then decode to a
* mode, expecting to get the original mode back.
*/
pxattr = ntfs_build_descr_posix(context.mapping,
pxdesc,isdir,owner,
(const SID*)group);
if (pxattr && !memcmp(pxattr,attr,
ntfs_attr_size(attr))) {
phead = (const SECURITY_DESCRIPTOR_RELATIVE*)attr;
pacl = (const ACL*)&attr[le32_to_cpu(phead->dacl)];
pxacecount += le16_to_cpu(pacl->ace_count);
pxglobhash += hash((const le32*)attr,ntfs_attr_size(attr));
pxcount++;
gotback = linux_permissions(pxattr, isdir);
if (gotback != perm)
err = ERRAM;
else
free(pxattr);
} else
err = ERRPA;
free(attr);
}
free(pxdesc);
#else /* POSIXACLS */
gotback = linux_permissions(attr, isdir);
if (gotback != perm)
err = ERRAM;
else
free(attr);
#endif /* POSIXACLS */
} else
err = ERRMA;
switch (err) {
case ERRMA :
printf("** no or wrong permission settings "
"for kind %d perm %03o\n",kind,perm);
if (attr && opt_v)
hexdump(attr,ntfs_attr_size(attr),8);
if (attr && (opt_v >= 2)) {
showheader(attr,4);
showusid(attr,4);
showgsid(attr,4);
showdacl(attr,isdir,4);
showsacl(attr,isdir,4);
}
errors++;
break;
case ERRPA :
printf("** no or wrong permission settings from PX "
"for kind %d perm %03o\n",kind,perm);
errors++;
break;
#if POSIXACLS
case ERRAM :
printf("** wrong permission settings, "
"kind %d perm 0%03o, gotback %03o\n",
kind, perm, gotback);
if (opt_v)
hexdump(pxattr,ntfs_attr_size(pxattr),8);
if (opt_v >= 2) {
showheader(pxattr,4);
showusid(pxattr,4);
showgsid(pxattr,4);
showdacl(pxattr,isdir,4);
showsacl(pxattr,isdir,4);
}
errors++;
break;
case ERRAP :
/* continued */
#else /* POSIXACLS */
case ERRAM :
case ERRAP :
#endif /* POSIXACLS */
printf("** wrong permission settings, "
"kind %d perm 0%03o, gotback %03o\n",
kind, perm, gotback);
if (opt_v)
hexdump(attr,ntfs_attr_size(attr),8);
if (opt_v >= 2) {
showheader(attr,4);
showusid(attr,4);
showgsid(attr,4);
showdacl(attr,isdir,4);
showsacl(attr,isdir,4);
}
errors++;
free(attr);
break;
default :
break;
}
}
printf("%lu ACLs built from mode, %lu ACE built, mean count %lu.%02lu\n",
(unsigned long)count,(unsigned long)acecount,
(unsigned long)acecount/count,acecount*100L/count%100L);
if (acecount != expectcnt[kind]) {
printf("** Error : ACE count %lu instead of %lu\n",
(unsigned long)acecount,
(unsigned long)expectcnt[kind]);
errors++;
}
if (globhash != expecthash[kind]) {
printf("** Error : wrong global hash 0x%lx instead of 0x%lx\n",
(unsigned long)globhash, (unsigned long)expecthash[kind]);
errors++;
}
#if POSIXACLS
printf("%lu ACLs built from Posix ACLs, %lu ACE built, mean count %lu.%02lu\n",
(unsigned long)pxcount,(unsigned long)pxacecount,
(unsigned long)pxacecount/pxcount,pxacecount*100L/pxcount%100L);
if (pxacecount != expectcnt[kind]) {
printf("** Error : ACE count %lu instead of %lu\n",
(unsigned long)pxacecount,
(unsigned long)expectcnt[kind]);
errors++;
}
if (pxglobhash != expecthash[kind]) {
printf("** Error : wrong global hash 0x%lx instead of 0x%lx\n",
(unsigned long)pxglobhash, (unsigned long)expecthash[kind]);
errors++;
}
#endif /* POSIXACLS */
}
#if POSIXACLS
/*
* Check whether Posix ACL settings are interpreted
* back exactly as set
*/
static void posixtest(int kind, BOOL isdir,
const SID *owner, const SID *group)
{
struct POSIX_SECURITY *pxdesc;
struct {
struct POSIX_SECURITY pxdesc;
struct POSIX_ACE aces[10];
} desc;
int ownobj;
int grpobj;
int usr;
int grp;
int wrld;
int mask;
int mindes, maxdes;
int minmsk, maxmsk;
char *pxattr;
u32 count;
u32 acecount;
u32 globhash;
const SECURITY_DESCRIPTOR_RELATIVE *phead;
const ACL *pacl;
struct POSIX_SECURITY *gotback;
enum { ERRNO,
ERRMA, ERRPA, /* error converting mode or Posix ACL to NTFS */
ERRAM, ERRAP, /* error converting NTFS to mode or Posix ACL */
} ;
u32 expectcnt[] = {
252720, 273456,
199584, 220320,
199584, 220320,
199584, 220320,
203904, 224640,
0, 0,
0, 0,
0, 0,
196452, 217188,
165888, 186624,
165888, 186624,
165888, 186624,
168480, 189216,
0, 0,
0, 0,
0, 0,
16368, 18672,
0, 0,
13824, 0,
0, 0,
14640, 0
} ;
u32 expecthash[] = {
0x1808a6cd, 0xd82f7c60,
0x5ad29e85, 0x518c7620,
0x188ce270, 0x7e44e590,
0x48a64800, 0x5bdf0030,
0x1c64aec6, 0x8b0168fa,
0, 0,
0, 0,
0, 0,
0x169fb80e, 0x382d9a59,
0xf9c28164, 0x1855d352,
0xf9685700, 0x44d16700,
0x587ebe90, 0xf7c51480,
0x2cb1b518, 0x52408df6,
0, 0,
0, 0,
0, 0,
0x905f2e38, 0xd40c22f0,
0, 0,
0xdd76da00, 0,
0, 0,
0x718e34a0, 0
};
count = 0;
acecount = 0;
globhash = 0;
/* fill headers */
pxdesc = &desc.pxdesc;
pxdesc->mode = 0;
pxdesc->defcnt = 0;
if (kind & 32) {
pxdesc->acccnt = 4;
pxdesc->firstdef = 4;
pxdesc->tagsset = 0x35;
} else {
pxdesc->acccnt = 6;;
pxdesc->firstdef = 6;
pxdesc->tagsset = 0x3f;
}
pxdesc->acl.version = POSIX_VERSION;
pxdesc->acl.flags = 0;
pxdesc->acl.filler = 0;
/* prefill aces */
pxdesc->acl.ace[0].tag = POSIX_ACL_USER_OBJ;
pxdesc->acl.ace[0].id = -1;
if (kind & 32) {
pxdesc->acl.ace[1].tag = POSIX_ACL_GROUP_OBJ;
pxdesc->acl.ace[1].id = -1;
pxdesc->acl.ace[2].tag = POSIX_ACL_MASK;
pxdesc->acl.ace[2].id = -1;
pxdesc->acl.ace[3].tag = POSIX_ACL_OTHER;
pxdesc->acl.ace[3].id = -1;
} else {
pxdesc->acl.ace[1].tag = POSIX_ACL_USER;
pxdesc->acl.ace[1].id = (kind & 16 ? 0 : 1000);
pxdesc->acl.ace[2].tag = POSIX_ACL_GROUP_OBJ;
pxdesc->acl.ace[2].id = -1;
pxdesc->acl.ace[3].tag = POSIX_ACL_GROUP;
pxdesc->acl.ace[3].id = (kind & 16 ? 0 : 1002);
pxdesc->acl.ace[4].tag = POSIX_ACL_MASK;
pxdesc->acl.ace[4].id = -1;
pxdesc->acl.ace[5].tag = POSIX_ACL_OTHER;
pxdesc->acl.ace[5].id = -1;
}
mindes = 3;
maxdes = (kind & 32 ? mindes : 6);
minmsk = 0;
maxmsk = 7;
for (mask=minmsk; mask<=maxmsk; mask++)
for (ownobj=1; ownobj<7; ownobj++)
for (grpobj=1; grpobj<7; grpobj++)
for (wrld=0; wrld<8; wrld++)
for (usr=mindes; usr<=maxdes; usr++)
if (usr != 4)
for (grp=mindes; grp<=maxdes; grp++)
if (grp != 4) {
pxdesc->mode = (ownobj << 6) | (mask << 3) | wrld;
pxdesc->acl.ace[0].perms = ownobj;
if (kind & 32) {
pxdesc->acl.ace[1].perms = grpobj;
pxdesc->acl.ace[2].perms = mask;
pxdesc->acl.ace[3].perms = wrld;
} else {
pxdesc->acl.ace[1].perms = usr;
pxdesc->acl.ace[2].perms = grpobj;
pxdesc->acl.ace[3].perms = grp;
pxdesc->acl.ace[4].perms = mask;
pxdesc->acl.ace[5].perms = wrld;
}
gotback = (struct POSIX_SECURITY*)NULL;
pxattr = ntfs_build_descr_posix(context.mapping,
pxdesc,isdir,owner,group);
if (pxattr && ntfs_valid_descr(pxattr, ntfs_attr_size(pxattr))) {
phead = (const SECURITY_DESCRIPTOR_RELATIVE*)pxattr;
pacl = (const ACL*)&pxattr[le32_to_cpu(phead->dacl)];
acecount += le16_to_cpu(pacl->ace_count);
globhash += hash((const le32*)pxattr,ntfs_attr_size(pxattr));
count++;
gotback = linux_permissions_posix(pxattr, isdir);
if (gotback) {
if (ntfs_valid_posix(gotback)) {
if (!same_posix(pxdesc,gotback)) {
printf("Non matching got back Posix ACL\n");
printf("input ACL\n");
showposix(pxdesc);
printf("NTFS owner\n");
showusid(pxattr,4);
printf("NTFS group\n");
showgsid(pxattr,4);
printf("NTFS DACL\n");
showdacl(pxattr,isdir,4);
printf("gotback ACL\n");
showposix(gotback);
errors++;
}
} else {
printf("Got back an invalid Posix ACL\n");
errors++;
}
free(gotback);
} else {
printf("Could not get Posix ACL back\n");
errors++;
}
} else {
printf("NTFS ACL incorrect or not build\n");
printf("input ACL\n");
showposix(pxdesc);
printf("NTFS DACL\n");
if (pxattr)
showdacl(pxattr,isdir,4);
else
printf(" (none)\n");
if (gotback) {
printf("gotback ACL\n");
showposix(gotback);
} else
printf("no gotback ACL\n");
errors++;
}
if (pxattr)
free(pxattr);
}
printf("%lu ACLs built from Posix ACLs, %lu ACE built, mean count %lu.%02lu\n",
(unsigned long)count,(unsigned long)acecount,
(unsigned long)acecount/count,acecount*100L/count%100L);
if (acecount != expectcnt[kind]) {
printf("** Error ! expected ACE count %lu\n",
(unsigned long)expectcnt[kind]);
errors++;
}
if (globhash != expecthash[kind]) {
printf("** Error : wrong global hash 0x%lx instead of 0x%lx\n",
(unsigned long)globhash, (unsigned long)expecthash[kind]);
errors++;
}
}
#endif /* POSIXACLS */
static void selftests(void)
{
le32 owner_sid[] = /* S-1-5-21-3141592653-589793238-462843383-1016 */
{
cpu_to_le32(0x501), cpu_to_le32(0x05000000), cpu_to_le32(21),
cpu_to_le32(DEFSECAUTH1), cpu_to_le32(DEFSECAUTH2),
cpu_to_le32(DEFSECAUTH3), cpu_to_le32(1016)
} ;
le32 group_sid[] = /* S-1-5-21-3141592653-589793238-462843383-513 */
{
cpu_to_le32(0x501), cpu_to_le32(0x05000000), cpu_to_le32(21),
cpu_to_le32(DEFSECAUTH1), cpu_to_le32(DEFSECAUTH2),
cpu_to_le32(DEFSECAUTH3), cpu_to_le32(513)
} ;
#if POSIXACLS
unsigned char kindlist[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
16, 17, 18, 20, 22, 24, 19, 21, 23, 25,
32, 33, 36, 40 } ;
unsigned int k;
#endif /* POSIXACLS */
int kind;
const SID *owner;
const SID *group;
BOOL isdir;
#if POSIXACLS
local_build_mapping(context.mapping, (const char*)NULL);
#endif /* POSIXACLS */
/* first check samples */
mappingtype = MAPDUMMY;
check_samples();
/*
* kind is oring of :
* 1 : directory
* 2 : owner is root
* 4 : group is root
* 8 : group is owner
* 16 : root is designated user/group
* 32 : mask present with no designated user/group
*/
for (kind=0; (kind<10) && (errors<10); kind++) {
isdir = kind & 1;
if (kind & 8)
owner = (const SID*)group_sid;
else
owner = (kind & 2 ? adminsid : (const SID*)owner_sid);
group = (kind & 4 ? adminsid : (const SID*)group_sid);
basictest(kind, isdir, owner, group);
}
#if POSIXACLS
for (k=0; (k<sizeof(kindlist)) && (errors<10); k++) {
kind = kindlist[k];
isdir = kind & 1;
if (kind & 8)
owner = (const SID*)group_sid;
else
owner = (kind & 2 ? adminsid : (const SID*)owner_sid);
group = (kind & 4 ? adminsid : (const SID*)group_sid);
posixtest(kind, isdir, owner, group);
}
ntfs_free_mapping(context.mapping);
#endif /* POSIXACLS */
if (errors >= 10)
printf("** too many errors, test aborted\n");
}
#endif /* SELFTESTS */
/*
* Get the security descriptor of a file
*/
static unsigned int getfull(char *attr, const char *fullname)
{
static char part[MAXATTRSZ];
BIGSID ownsid;
int xowner;
int ownersz;
u16 ownerfl;
u32 attrsz;
u32 partsz;
BOOL overflow;
attrsz = 0;
partsz = 0;
overflow = FALSE;
if (ntfs_get_file_security(ntfs_context,fullname,
OWNER_SECURITY_INFORMATION,
(char*)part,MAXATTRSZ,&partsz)) {
xowner = get4l(part,4);
if (xowner) {
ownerfl = get2l(part,2);
ownersz = ntfs_sid_size((SID*)&part[xowner]);
if (ownersz <= (int)sizeof(BIGSID))
memcpy(ownsid,&part[xowner],ownersz);
else
overflow = TRUE;
} else {
ownerfl = 0;
ownersz = 0;
}
/*
* SACL : just feed in or clean
*/
if (!ntfs_get_file_security(ntfs_context,fullname,
SACL_SECURITY_INFORMATION,
(char*)attr,MAXATTRSZ,&attrsz)) {
attrsz = 20;
set4l(attr,0);
attr[0] = SECURITY_DESCRIPTOR_REVISION;
set4l(&attr[12],0);
if (opt_v >= 2)
printf(" No SACL\n");
}
/*
* append DACL and merge its flags
*/
partsz = 0;
set4l(&attr[16],0);
if (ntfs_get_file_security(ntfs_context,fullname,
DACL_SECURITY_INFORMATION,
(char*)part,MAXATTRSZ,&partsz)) {
if ((attrsz + partsz - 20) <= MAXATTRSZ) {
memcpy(&attr[attrsz],&part[20],partsz-20);
set4l(&attr[16],(partsz > 20 ? attrsz : 0));
set2l(&attr[2],get2l(attr,2) | (get2l(part,2)
& const_le16_to_cpu(SE_DACL_PROTECTED
| SE_DACL_AUTO_INHERITED
| SE_DACL_PRESENT)));
attrsz += partsz - 20;
} else
overflow = TRUE;
} else
if (partsz > MAXATTRSZ)
overflow = TRUE;
else {
if (cmd == CMD_BACKUP)
printf("# No discretionary access control list\n");
else
printf(" No discretionary access control list\n");
warnings++;
}
/*
* append owner and merge its flag
*/
if (xowner && !overflow) {
memcpy(&attr[attrsz],ownsid,ownersz);
set4l(&attr[4],attrsz);
set2l(&attr[2],get2l(attr,2)
| (ownerfl & const_le16_to_cpu(SE_OWNER_DEFAULTED)));
attrsz += ownersz;
} else
set4l(&attr[4],0);
/*
* append group
*/
partsz = 0;
set4l(&attr[8],0);
if (ntfs_get_file_security(ntfs_context,fullname,
GROUP_SECURITY_INFORMATION,
(char*)part,MAXATTRSZ,&partsz)) {
if ((attrsz + partsz - 20) <= MAXATTRSZ) {
memcpy(&attr[attrsz],&part[20],partsz-20);
set4l(&attr[8],(partsz > 20 ? attrsz : 0));
set2l(&attr[2],get2l(attr,2) | (get2l(part,2)
& const_le16_to_cpu(SE_GROUP_DEFAULTED)));
attrsz += partsz - 20;
} else
overflow = TRUE;
} else
if (partsz > MAXATTRSZ)
overflow = TRUE;
else {
printf("** No group SID\n");
warnings++;
}
if (overflow) {
printf("** Descriptor was too long (> %d)\n",MAXATTRSZ);
warnings++;
attrsz = 0;
} else
if (!ntfs_valid_descr((char*)attr,attrsz)) {
printf("** Descriptor for %s is not valid\n",fullname);
errors++;
attrsz = 0;
}
} else {
printf("** Could not get owner of %s\n",fullname);
warnings++;
attrsz = 0;
}
return (attrsz);
}
/*
* Update a security descriptor
*/
static BOOL updatefull(const char *name, u32 flags, char *attr)
{
BOOL err;
// Why was the error not seen before ?
err = !ntfs_set_file_security(ntfs_context, name, flags, attr);
if (err) {
printf("** Could not change attributes of %s\n",name);
printerror(stdout);
errors++;
}
return (!err);
}
#if POSIXACLS
/*
* Set all the parameters associated to a file
*/
static BOOL setfull_posix(const char *fullname, const struct POSIX_SECURITY *pxdesc,
BOOL isdir)
{
static char attr[MAXATTRSZ];
struct POSIX_SECURITY *oldpxdesc;
struct POSIX_SECURITY *newpxdesc;
const SECURITY_DESCRIPTOR_RELATIVE *phead;
char *newattr;
int err;
unsigned int attrsz;
int newattrsz;
const SID *usid;
const SID *gsid;
#if OWNERFROMACL
const SID *osid;
#endif /* OWNERFROMACL */
printf("%s ",(isdir ? "Directory" : "File"));
printname(stdout,fullname);
if (pxdesc->acccnt)
printf("\n");
else
printf(" mode 0%03o\n",pxdesc->mode);
err = FALSE;
attrsz = getfull(attr, fullname);
if (attrsz) {
oldpxdesc = linux_permissions_posix(attr, isdir);
if (opt_v >= 2) {
printf("Posix equivalent of old ACL :\n");
showposix(oldpxdesc);
}
if (oldpxdesc) {
if (!pxdesc->defcnt
&& !(pxdesc->tagsset &
(POSIX_ACL_USER | POSIX_ACL_GROUP | POSIX_ACL_MASK))) {
if (!ntfs_merge_mode_posix(oldpxdesc,pxdesc->mode))
newpxdesc = oldpxdesc;
else {
newpxdesc = (struct POSIX_SECURITY*)NULL;
free(oldpxdesc);
}
} else {
newpxdesc = ntfs_merge_descr_posix(pxdesc, oldpxdesc);
free(oldpxdesc);
}
if (opt_v) {
printf("New Posix ACL :\n");
showposix(newpxdesc);
}
} else
newpxdesc = (struct POSIX_SECURITY*)NULL;
if (newpxdesc) {
phead = (const SECURITY_DESCRIPTOR_RELATIVE*)attr;
gsid = (const SID*)&attr[le32_to_cpu(phead->group)];
#if OWNERFROMACL
osid = (const SID*)&attr[le32_to_cpu(phead->owner)];
usid = ntfs_acl_owner((const char*)attr);
if (!ntfs_same_sid(usid,osid))
printf("== Windows owner might change\n");
#else /* OWNERFROMACL */
usid = (const SID*)&attr[le32_to_cpu(phead->owner)];
#endif /* OWNERFROMACL */
if (mappingtype == MAPEXTERN)
newattr = ntfs_build_descr_posix(
ntfs_context->security.mapping,
newpxdesc,isdir,usid,gsid);
else
newattr = ntfs_build_descr_posix(
context.mapping,
newpxdesc,isdir,usid,gsid);
free(newpxdesc);
} else
newattr = (char*)NULL;
if (newattr) {
newattrsz = ntfs_attr_size(newattr);
if (opt_v) {
printf("New NTFS security descriptor\n");
hexdump(newattr,newattrsz,4);
}
if (opt_v >= 2) {
printf("Expected hash : 0x%08lx\n",
(unsigned long)hash((le32*)newattr,ntfs_attr_size(newattr)));
showheader(newattr,0);
showusid(newattr,0);
showgsid(newattr,0);
showdacl(newattr,isdir,0);
showsacl(newattr,isdir,0);
}
if (!updatefull(fullname,
DACL_SECURITY_INFORMATION
| GROUP_SECURITY_INFORMATION
| OWNER_SECURITY_INFORMATION,
newattr))
err = TRUE;
/*
{
struct POSIX_SECURITY *interp;
printf("Reinterpreted new Posix :\n");
interp = linux_permissions_posix(newattr,isdir);
showposix(interp);
free(interp);
}
*/
free(newattr);
} else
err = TRUE;
} else
err = TRUE;
return (!err);
}
#else /* POSIXACLS */
static BOOL setfull(const char *fullname, int mode, BOOL isdir)
{
static char attr[MAXATTRSZ];
const SECURITY_DESCRIPTOR_RELATIVE *phead;
char *newattr;
int err;
unsigned int attrsz;
int newattrsz;
const SID *usid;
const SID *gsid;
#if OWNERFROMACL
const SID *osid;
#endif /* OWNERFROMACL */
printf("%s ",(isdir ? "Directory" : "File"));
printname(stdout,fullname);
printf(" mode 0%03o\n",mode);
attrsz = getfull(attr, fullname);
err = FALSE;
if (attrsz) {
phead = (const SECURITY_DESCRIPTOR_RELATIVE*)attr;
gsid = (const SID*)&attr[le32_to_cpu(phead->group)];
#if OWNERFROMACL
osid = (const SID*)&attr[le32_to_cpu(phead->owner)];
usid = ntfs_acl_owner((const char*)attr);
if (!ntfs_same_sid(usid,osid))
printf("== Windows owner might change\n");
#else /* OWNERFROMACL */
usid = (const SID*)&attr[le32_to_cpu(phead->owner)];
#endif /* OWNERFROMACL */
newattr = ntfs_build_descr(mode,isdir,usid,gsid);
if (newattr) {
newattrsz = ntfs_attr_size(newattr);
if (opt_v) {
printf("Security descriptor\n");
hexdump(newattr,newattrsz,4);
}
if (opt_v >= 2) {
printf("Expected hash : 0x%08lx\n",
(unsigned long)hash((le32*)newattr,ntfs_attr_size(newattr)));
showheader(newattr,0);
showusid(newattr,0);
showgsid(newattr,0);
showdacl(newattr,isdir,0);
showsacl(newattr,isdir,0);
}
if (!updatefull(fullname,
DACL_SECURITY_INFORMATION
| GROUP_SECURITY_INFORMATION
| OWNER_SECURITY_INFORMATION,
newattr))
err = TRUE;
free(newattr);
}
} else
err = TRUE;
return (err);
}
#endif /* POSIXACLS */
static BOOL proposal(const char *name, const char *attr)
{
char fullname[MAXFILENAME];
int uoff, goff;
int i;
u64 uauth, gauth;
int ucnt, gcnt;
int uid, gid;
BOOL err;
#ifdef HAVE_WINDOWS_H
char driveletter;
#else /* HAVE_WINDOWS_H */
struct stat st;
char *p,*q;
#endif /* HAVE_WINDOWS_H */
err = FALSE;
#ifdef HAVE_WINDOWS_H
uid = gid = 0;
#else /* HAVE_WINDOWS_H */
uid = getuid();
gid = getgid();
#endif /* HAVE_WINDOWS_H */
uoff = get4l(attr,4);
uauth = get6h(attr,uoff+2);
ucnt = attr[uoff+1] & 255;
goff = get4l(attr,8);
gauth = get6h(attr,goff+2);
gcnt = attr[goff+1] & 255;
if ((ucnt == 5) && (gcnt == 5)
&& (uauth == 5) && (gauth == 5)
&& (get4l(attr,uoff+8) == 21) && (get4l(attr,goff+8) == 21)) {
printf("# User mapping proposal :\n");
printf("# -------------------- cut here -------------------\n");
if (uid)
printf("%d::",uid);
else
printf("user::");
printf("S-%d-%llu",attr[uoff] & 255,(long long)uauth);
for (i=0; i<ucnt; i++)
printf("-%lu",get4l(attr,uoff+8+4*i));
printf("\n");
if (gid)
printf(":%d:",gid);
else
printf(":group:");
printf("S-%d-%llu",attr[goff] & 255,(long long)gauth);
for (i=0; i<gcnt; i++)
printf("-%lu",get4l(attr,goff+8+4*i));
printf("\n");
/* generic rule, based on group */
printf("::S-%d-%llu",attr[goff] & 255,(long long)gauth);
for (i=0; i<gcnt-1; i++)
printf("-%lu",get4l(attr,goff+8+4*i));
printf("-10000\n");
printf("# -------------------- cut here -------------------\n");
if (!uid || !gid) {
printf("# Please replace \"user\" and \"group\" above by the uid\n");
printf("# and gid of the Linux owner and group of ");
printname(stdout,name);
printf(", then\n");
printf("# insert the modified lines into .NTFS-3G/UserMapping, with .NTFS-3G\n");
} else
printf("# Insert the above lines into .NTFS-3G/UserMapping, with .NTFS-3G\n");
#ifdef HAVE_WINDOWS_H
printf("# being a directory of the root of the NTFS file system.\n");
/* Get the drive letter to the file system */
driveletter = 0;
if ((((name[0] >= 'a') && (name[0] <= 'z'))
|| ((name[0] >= 'A') && (name[0] <= 'Z')))
&& (name[1] == ':'))
driveletter = name[0];
else {
if (getcwd(fullname, MAXFILENAME)
&& (fullname[1] == ':'))
driveletter = fullname[0];
}
if (driveletter) {
printf("# Example : %c:\\.NTFS-3G\\UserMapping\n",
driveletter);
}
#else /* HAVE_WINDOWS_H */
printf("# being a hidden subdirectory of the root of the NTFS file system.\n");
/* Get the path to the root of the file system */
/*
if (name[0] != '/') {
p = getcwd(fullname,MAXFILENAME);
if (p) {
strcat(fullname,"/");
strcat(fullname,name);
}
} else {
strcpy(fullname,name);
p = fullname;
}
*/
p = ntfs_realpath(name, fullname);
if (p) {
/* go down the path to inode 5 */
do {
lstat(fullname,&st);
q = strrchr(p,'/');
if (q && (st.st_ino != 5))
*q = 0;
} while (strchr(p,'/') && (st.st_ino != 5));
}
if (p && (st.st_ino == 5)) {
printf("# Example : ");
printname(stdout,p);
printf("/.NTFS-3G/UserMapping\n");
}
#endif /* HAVE_WINDOWS_H */
} else {
printf("** Not possible : ");
printname(stdout,name);
printf(" was not created by a Windows user\n");
err = TRUE;
}
return (err);
}
/*
* Display all the parameters associated to a file
*/
static void showfull(const char *fullname, BOOL isdir)
{
static char attr[MAXATTRSZ];
static char part[MAXATTRSZ];
#if POSIXACLS
struct POSIX_SECURITY *pxdesc;
#endif /* POSIXACLS */
struct SECURITY_DATA *psecurdata;
char *newattr;
int securindex;
int mode;
int level;
int attrib;
u32 attrsz;
u32 partsz;
uid_t uid;
gid_t gid;
if (opt_v || (cmd == CMD_BACKUP)) {
printf("%s ",(isdir ? "Directory" : "File"));
printname(stdout, fullname);
printf("\n");
}
/* get individual parameters, as when trying to get them */
/* all, and one (typically SACL) is missing, we get none */
/* and concatenate them, to be able to compute the checksum */
partsz = 0;
securindex = ntfs_get_file_security(ntfs_context,fullname,
OWNER_SECURITY_INFORMATION,
(char*)part,MAXATTRSZ,&partsz);
attrib = ntfs_get_file_attributes(ntfs_context, fullname);
if (attrib == INVALID_FILE_ATTRIBUTES) {
printf("** Could not get file attrib\n");
errors++;
}
if ((securindex < 0)
|| (securindex >= MAXSECURID)
|| ((securindex > 0)
&& ((!opt_r && (cmd != CMD_BACKUP))
|| !securdata[securindex >> SECBLKSZ]
|| !securdata[securindex >> SECBLKSZ][securindex & ((1 << SECBLKSZ) - 1)].filecount)))
{
if (opt_v || (cmd == CMD_BACKUP)) {
if ((securindex < -1) || (securindex >= MAXSECURID))
printf("Security key : 0x%x out of range\n",securindex);
else
if (securindex == -1)
printf("Security key : none\n");
else
printf("Security key : 0x%x\n",securindex);
} else {
printf("%s ",(isdir ? "Directory" : "File"));
printname(stdout, fullname);
if ((securindex < -1) || (securindex >= MAXSECURID))
printf(" : key 0x%x out of range\n",securindex);
else
if (securindex == -1)
printf(" : no key\n");
else
printf(" : key 0x%x\n",securindex);
}
attrsz = getfull(attr, fullname);
if (attrsz) {
psecurdata = (struct SECURITY_DATA*)NULL;
if ((securindex < MAXSECURID) && (securindex > 0)) {
if (!securdata[securindex >> SECBLKSZ])
newblock(securindex);
if (securdata[securindex >> SECBLKSZ])
psecurdata = &securdata[securindex >> SECBLKSZ]
[securindex & ((1 << SECBLKSZ) - 1)];
}
if (((cmd == CMD_AUDIT) || (cmd == CMD_BACKUP))
&& opt_v && psecurdata) {
newattr = (char*)malloc(attrsz);
printf("# %s ",(isdir ? "Directory" : "File"));
printname(stdout, fullname);
printf(" hash 0x%lx\n",
(unsigned long)hash((le32*)attr,attrsz));
if (newattr) {
memcpy(newattr,attr,attrsz);
psecurdata->attr = newattr;
}
}
if ((opt_v || (cmd == CMD_BACKUP))
&& ((securindex >= MAXSECURID)
|| (securindex <= 0)
|| !psecurdata
|| (!psecurdata->filecount
&& !psecurdata->flags))) {
hexdump(attr,attrsz,8);
printf("Computed hash : 0x%08lx\n",
(unsigned long)hash((le32*)attr,attrsz));
}
if (ntfs_valid_descr((char*)attr,attrsz)) {
#if POSIXACLS
pxdesc = linux_permissions_posix(attr,isdir);
if (pxdesc)
mode = pxdesc->mode;
else
mode = 0;
#else /* POSIXACLS */
mode = linux_permissions(attr,isdir);
#endif /* POSIXACLS */
attrib = ntfs_get_file_attributes(ntfs_context,fullname);
if (opt_v >= 2) {
level = (cmd == CMD_BACKUP ? 4 : 0);
showheader(attr,level);
showusid(attr,level);
showgsid(attr,level);
showdacl(attr,isdir,level);
showsacl(attr,isdir,level);
}
if (attrib != INVALID_FILE_ATTRIBUTES)
printf("Windows attrib : 0x%x\n",attrib);
uid = linux_owner(attr);
gid = linux_group(attr);
if (cmd == CMD_BACKUP) {
showownership(attr);
printf("# Interpreted Unix owner %d, group %d, mode 0%03o\n",
(int)uid,(int)gid,mode);
} else {
showownership(attr);
printf("Interpreted Unix owner %d, group %d, mode 0%03o\n",
(int)uid,(int)gid,mode);
}
#if POSIXACLS
if (pxdesc) {
if ((cmd != CMD_BACKUP)
&& (pxdesc->defcnt
|| (pxdesc->tagsset
& (POSIX_ACL_USER
| POSIX_ACL_GROUP
| POSIX_ACL_MASK))))
showposix(pxdesc);
free(pxdesc);
}
#endif /* POSIXACLS */
if ((opt_r || (cmd == CMD_BACKUP))
&& (securindex < MAXSECURID)
&& (securindex > 0) && psecurdata) {
psecurdata->filecount++;
psecurdata->mode = mode;
}
} else {
printf("** Descriptor fails sanity check\n");
errors++;
}
}
} else
if (securindex > 0) {
if (securdata[securindex >> SECBLKSZ]) {
psecurdata = &securdata[securindex >> SECBLKSZ]
[securindex & ((1 << SECBLKSZ) - 1)];
psecurdata->filecount++;
if ((cmd == CMD_BACKUP) || opt_r) {
if ((cmd != CMD_BACKUP) && !opt_v) {
printf("%s ",(isdir ? "Directory" : "File"));
printname(stdout,fullname);
printf("\n");
}
printf("Security key : 0x%x mode %03o (already displayed)\n",
securindex,psecurdata->mode);
if (attrib != INVALID_FILE_ATTRIBUTES)
printf("Windows attrib : 0x%x\n",attrib);
} else {
printf("%s ",(isdir ? "Directory" : "File"));
printname(stdout,fullname);
printf(" : key 0x%x\n",securindex);
}
if (((cmd == CMD_AUDIT) || (cmd == CMD_BACKUP))
&& opt_v
&& psecurdata
&& psecurdata->attr) {
printf("# %s ",(isdir ? "Directory" : "File"));
printname(stdout,fullname);
printf(" hash 0x%lx\n",
(unsigned long)hash((le32*)psecurdata->attr,
ntfs_attr_size(psecurdata->attr)));
}
}
} else {
if (!opt_v && (cmd != CMD_BACKUP)) {
printf("%s ",(isdir ? "Directory" : "File"));
printname(stdout, fullname);
}
printf(" (Failed)\n");
printf("** Could not get security data of ");
printname(stdout, fullname);
printf(", partsz %d\n", partsz);
printerror(stdout);
errors++;
}
}
static BOOL recurseshow(const char *path)
{
struct CALLBACK dircontext;
struct LINK *current;
BOOL isdir;
BOOL err;
err = FALSE;
dircontext.head = (struct LINK*)NULL;
dircontext.dir = path;
isdir = ntfs_read_directory(ntfs_context, path,
callback, &dircontext);
if (isdir) {
showfull(path,TRUE);
if (opt_v) {
if (cmd == CMD_BACKUP)
printf("#\n#\n");
else
printf("\n\n");
}
while (dircontext.head) {
current = dircontext.head;
if (recurseshow(current->name)) err = TRUE;
dircontext.head = dircontext.head->next;
free(current);
}
} else
if (errno == ENOTDIR) {
showfull(path,FALSE);
if (opt_v) {
if (cmd == CMD_BACKUP)
printf("#\n#\n");
else
printf("\n\n");
}
} else {
printf("** Could not access %s\n",path);
printerror(stdout);
errors++;
err = TRUE;
}
return (!err);
}
static BOOL singleshow(const char *path)
{
BOOL isdir;
BOOL err;
err = FALSE;
isdir = ntfs_read_directory(ntfs_context, path,
callback, (struct CALLBACK*)NULL);
if (isdir || (errno == ENOTDIR))
showfull(path,isdir);
else {
printf("** Could not access %s\n",path);
printerror(stdout);
errors++;
err = TRUE;
}
return (err);
}
#ifndef HAVE_WINDOWS_H
#ifdef HAVE_SETXATTR
static ssize_t ntfs_getxattr(const char *path, const char *name, void *value, size_t size)
{
#if defined(__APPLE__) || defined(__DARWIN__)
return getxattr(path, name, value, size, 0, 0);
#else /* defined(__APPLE__) || defined(__DARWIN__) */
return getxattr(path, name, value, size);
#endif /* defined(__APPLE__) || defined(__DARWIN__) */
}
/*
* Display all the parameters associated to a mounted file
*
* (Unix only)
*/
static BOOL showmounted(const char *fullname)
{
static char attr[MAXATTRSZ];
struct stat st;
#if POSIXACLS
struct POSIX_SECURITY *pxdesc;
#endif /* POSIXACLS */
BOOL mapped;
int attrsz;
int mode;
uid_t uid;
gid_t gid;
u32 attrib;
int level;
BOOL isdir;
BOOL err;
err = FALSE;
if (!stat(fullname,&st)) {
isdir = S_ISDIR(st.st_mode);
printf("%s ",(isdir ? "Directory" : "File"));
printname(stdout,fullname);
printf("\n");
attrsz = ntfs_getxattr(fullname,"system.ntfs_acl",attr,MAXATTRSZ);
if (attrsz > 0) {
if (opt_v) {
hexdump(attr,attrsz,8);
printf("Computed hash : 0x%08lx\n",
(unsigned long)hash((le32*)attr,attrsz));
}
if (ntfs_getxattr(fullname,"system.ntfs_attrib",&attrib,4) != 4) {
printf("** Could not get file attrib\n");
errors++;
} else
printf("Windows attrib : 0x%x\n",(int)attrib);
if (ntfs_valid_descr(attr,attrsz)) {
mapped = !local_build_mapping(context.mapping,fullname);
#if POSIXACLS
if (mapped) {
pxdesc = linux_permissions_posix(attr,isdir);
if (pxdesc)
mode = pxdesc->mode;
else
mode = 0;
} else {
pxdesc = (struct POSIX_SECURITY*)NULL;
mode = linux_permissions(attr,isdir);
printf("No user mapping : "
"cannot display the Posix ACL\n");
}
#else /* POSIXACLS */
mode = linux_permissions(attr,isdir);
#endif /* POSIXACLS */
if (opt_v >= 2) {
level = (cmd == CMD_BACKUP ? 4 : 0);
showheader(attr,level);
showusid(attr,level);
showgsid(attr,level);
showdacl(attr,isdir,level);
showsacl(attr,isdir,level);
}
showownership(attr);
if (mapped) {
uid = linux_owner(attr);
gid = linux_group(attr);
printf("Interpreted Unix owner %d, group %d, mode 0%03o\n",
(int)uid,(int)gid,mode);
} else {
printf("Interpreted Unix mode 0%03o (owner and group are unmapped)\n",
mode);
}
#if POSIXACLS
if (pxdesc) {
if ((pxdesc->defcnt
|| (pxdesc->tagsset
& (POSIX_ACL_USER
| POSIX_ACL_GROUP
| POSIX_ACL_MASK))))
showposix(pxdesc);
free(pxdesc);
}
if (mapped)
ntfs_free_mapping(context.mapping);
#endif /* POSIXACLS */
} else {
printf("Descriptor fails sanity check\n");
errors++;
}
} else {
printf("** Could not get the NTFS ACL, check whether file is on NTFS\n");
errors++;
}
} else {
printf("%s not found\n",fullname);
err = TRUE;
}
return (err);
}
static BOOL processmounted(const char *fullname)
{
static char attr[MAXATTRSZ];
struct stat st;
int attrsz;
BOOL err;
err = FALSE;
if (cmd != CMD_USERMAP)
err = showmounted(fullname);
else
if (!stat(fullname,&st)) {
attrsz = ntfs_getxattr(fullname,"system.ntfs_acl",attr,MAXATTRSZ);
if (attrsz > 0) {
if (opt_v) {
hexdump(attr,attrsz,8);
printf("Computed hash : 0x%08lx\n",
(unsigned long)hash((le32*)attr,attrsz));
}
if (ntfs_valid_descr(attr,attrsz)) {
err = proposal(fullname, attr);
} else {
printf("*** Descriptor fails sanity check\n");
errors++;
}
} else {
printf("** Could not get the NTFS ACL, check whether file is on NTFS\n");
errors++;
}
} else {
printf("%s not found\n",fullname);
err = TRUE;
}
return (err);
}
#else /* HAVE_SETXATTR */
static BOOL processmounted(const char *fullname __attribute__((unused)))
{
fprintf(stderr,"Not possible on this configuration,\n");
fprintf(stderr,"you have to use an unmounted partition\n");
return (TRUE);
}
#endif /* HAVE_SETXATTR */
#endif /* HAVE_WINDOWS_H */
#if POSIXACLS
static BOOL recurseset_posix(const char *path, const struct POSIX_SECURITY *pxdesc)
{
struct CALLBACK dircontext;
struct LINK *current;
BOOL isdir;
BOOL err;
err = FALSE;
dircontext.head = (struct LINK*)NULL;
dircontext.dir = path;
isdir = ntfs_read_directory(ntfs_context, path,
callback, &dircontext);
if (isdir) {
err = !setfull_posix(path,pxdesc,TRUE);
if (err) {
printf("** Failed to update %s\n",path);
printerror(stdout);
errors++;
} else {
if (cmd == CMD_BACKUP)
printf("#\n#\n");
else
printf("\n\n");
while (dircontext.head) {
current = dircontext.head;
recurseset_posix(current->name,pxdesc);
dircontext.head = dircontext.head->next;
free(current);
}
}
} else
if (errno == ENOTDIR) {
err = !setfull_posix(path,pxdesc,FALSE);
if (err) {
printf("** Failed to update %s\n",path);
printerror(stdout);
errors++;
}
} else {
printf("** Could not access %s\n",path);
printerror(stdout);
errors++;
err = TRUE;
}
return (!err);
}
#else /* POSIXACLS */
static BOOL recurseset(const char *path, int mode)
{
struct CALLBACK dircontext;
struct LINK *current;
BOOL isdir;
BOOL err;
err = FALSE;
dircontext.head = (struct LINK*)NULL;
dircontext.dir = path;
isdir = ntfs_read_directory(ntfs_context, path,
callback, &dircontext);
if (isdir) {
setfull(path,mode,TRUE);
if (cmd == CMD_BACKUP)
printf("#\n#\n");
else
printf("\n\n");
while (dircontext.head) {
current = dircontext.head;
recurseset(current->name,mode);
dircontext.head = dircontext.head->next;
free(current);
}
} else
if (errno == ENOTDIR)
setfull(path,mode,FALSE);
else {
printf("** Could not access %s\n",path);
printerror(stdout);
errors++;
err = TRUE;
}
return (!err);
}
#endif /* POSIXACLS */
#if POSIXACLS
static BOOL singleset_posix(const char *path, const struct POSIX_SECURITY *pxdesc)
{
BOOL isdir;
BOOL err;
err = FALSE;
isdir = ntfs_read_directory(ntfs_context, path,
callback, (struct CALLBACK*)NULL);
if (isdir || (errno == ENOTDIR)) {
err = !setfull_posix(path,pxdesc,isdir);
if (err) {
printf("** Failed to update %s\n",path);
printerror(stdout);
errors++;
}
} else {
printf("** Could not access %s\n",path);
printerror(stdout);
errors++;
err = TRUE;
}
return (!err);
}
#else /* POSIXACLS */
static BOOL singleset(const char *path, int mode)
{
BOOL isdir;
BOOL err;
err = FALSE;
isdir = ntfs_read_directory(ntfs_context, path,
callback, (struct CALLBACK*)NULL);
if (isdir || (errno == ENOTDIR))
setfull(path,mode,isdir);
else {
printf("** Could not access %s\n",path);
printerror(stdout);
errors++;
err = TRUE;
}
return (!err);
}
#endif /* POSIXACLS */
static int callback(void *ctx, const ntfschar *ntfsname,
const int length, const int type,
const s64 pos __attribute__((unused)),
const MFT_REF mft_ref __attribute__((unused)),
const unsigned int dt_type __attribute__((unused)))
{
struct LINK *linkage;
struct CALLBACK *dircontext;
char *name;
int newlth;
int size;
dircontext = (struct CALLBACK*)ctx;
size = utf8size(ntfsname,length);
if (dircontext
&& (type != 2) /* 2 : dos name (8+3) */
&& (size > 0) /* chars convertible to utf8 */
&& ((length > 2)
|| (ntfsname[0] != const_cpu_to_le16('.'))
|| ((length > 1)
&& (ntfsname[1] != const_cpu_to_le16('.'))))) {
linkage = (struct LINK*)malloc(sizeof(struct LINK)
+ strlen(dircontext->dir)
+ size + 2);
if (linkage) {
/* may find ".fuse_hidden*" files */
/* recommendation is not to hide them, so that */
/* the user has a clue to delete them */
strcpy(linkage->name,dircontext->dir);
if (linkage->name[strlen(linkage->name) - 1] != '/')
strcat(linkage->name,"/");
name = &linkage->name[strlen(linkage->name)];
newlth = makeutf8(name,ntfsname,length);
name[newlth] = 0;
linkage->next = dircontext->head;
dircontext->head = linkage;
}
}
return (0);
}
/*
* Backup security descriptors in a directory tree
*/
static BOOL backup(const char *volume, const char *root)
{
BOOL err;
int count;
int i,j;
time_t now;
const char *txtime;
now = time((time_t*)NULL);
txtime = ctime(&now);
if (!getuid() && open_security_api()) {
if (open_volume(volume,NTFS_MNT_RDONLY)) {
printf("#\n# Recursive ACL collection on %s#\n",txtime);
err = recurseshow(root);
count = 0;
for (i=0; i<(MAXSECURID + (1 << SECBLKSZ) - 1)/(1 << SECBLKSZ); i++)
if (securdata[i])
for (j=0; j<(1 << SECBLKSZ); j++)
if (securdata[i][j].filecount) {
count++;
}
printf("# %d security keys\n",count);
close_volume(volume);
} else {
fprintf(stderr,"Could not open volume %s\n",volume);
printerror(stdout);
err = TRUE;
}
close_security_api();
} else {
if (getuid())
fprintf(stderr,"This is only possible as root\n");
else
fprintf(stderr,"Could not open security API\n");
err = TRUE;
}
return (err);
}
/*
* List security descriptors in a directory tree
*/
static BOOL listfiles(const char *volume, const char *root)
{
BOOL err;
int i,j;
int count;
if (!getuid() && open_security_api()) {
if (open_volume(volume,NTFS_MNT_RDONLY)) {
if (opt_r) {
printf("\nRecursive file check\n");
err = recurseshow(root);
printf("Summary\n");
count = 0;
for (i=0; i<(MAXSECURID + (1 << SECBLKSZ) - 1)/(1 << SECBLKSZ); i++)
if (securdata[i])
for (j=0; j<(1 << SECBLKSZ); j++)
if (securdata[i][j].filecount) {
printf("Key 0x%x : %d files, mode 0%03o\n",
i*(1 << SECBLKSZ)+j,securdata[i][j].filecount,
securdata[i][j].mode);
count++;
}
printf("%d security keys\n",count);
} else
err = singleshow(root);
close_volume(volume);
} else {
err = TRUE;
}
close_security_api();
} else {
if (getuid())
fprintf(stderr,"This is only possible as root\n");
else
fprintf(stderr,"Could not open security API\n");
err = TRUE;
}
return (err);
}
#ifdef HAVE_WINDOWS_H
static BOOL mapproposal(const char *volume, const char *name)
{
BOOL err;
u32 attrsz;
int securindex;
char attr[256]; /* header (20) and a couple of SIDs (max 68 each) */
err = FALSE;
if (!getuid() && open_security_api()) {
if (open_volume(volume,NTFS_MNT_RDONLY)) {
attrsz = 0;
securindex = ntfs_get_file_security(ntfs_context,name,
OWNER_SECURITY_INFORMATION
| GROUP_SECURITY_INFORMATION,
(char*)attr,MAXATTRSZ,&attrsz);
if (securindex)
err = proposal(name,attr);
else {
fprintf(stderr,"*** Could not get the ACL of ");
printname(stderr, name);
fprintf(stderr,"\n");
printerror(stderr);
errors++;
}
close_volume(volume);
} else {
fprintf(stderr,"Could not open volume %s\n",volume);
printerror(stdout);
err = TRUE;
}
close_security_api();
} else {
if (getuid())
fprintf(stderr,"This is only possible as root\n");
else
fprintf(stderr,"Could not open security API\n");
err = TRUE;
}
return (err);
}
#endif
/*
* Check whether a SDS entry is valid
*/
static BOOL valid_sds(const char *attr, unsigned int offset,
unsigned int entrysz, unsigned int size, u32 prevkey,
BOOL second)
{
BOOL unsane;
u32 comphash;
u32 key;
unsane = FALSE;
if (!get4l(attr,0) && !get4l(attr,4)) {
printf("Entry at 0x%lx was deleted\n",(long)offset);
} else {
if ((ntfs_attr_size(&attr[20]) + 20) > entrysz) {
printf("** Entry is truncated (expected size %ld)\n",
(long)ntfs_attr_size(&attr[20] + 20));
unsane = TRUE;
errors++;
}
if ((ntfs_attr_size(&attr[20]) + 20) < entrysz) {
printf("** Extra data appended to entry (expected size %ld)\n",
(long)ntfs_attr_size(&attr[20]) + 20);
warnings++;
}
if (!unsane && !ntfs_valid_descr((const char*)&attr[20],size)) {
printf("** General sanity check has failed\n");
unsane = TRUE;
errors++;
}
if (!unsane) {
comphash = hash((const le32*)&attr[20],entrysz-20);
if ((u32)get4l(attr,0) == comphash) {
if (opt_v >= 2)
printf("Hash 0x%08lx (correct)\n",
(unsigned long)comphash);
} else {
printf("** hash 0x%08lx (computed : 0x%08lx)\n",
(unsigned long)get4l(attr,0),
(unsigned long)comphash);
unsane = TRUE;
errors++;
}
}
if (!unsane) {
if ((second ? get8l(attr,8) + 0x40000 : get8l(attr,8)) == offset) {
if (opt_v >= 2)
printf("Offset 0x%lx (correct)\n",(long)offset);
} else {
printf("** offset 0x%llx (expected : 0x%llx)\n",
(long long)get8l(attr,8),
(long long)(second ? get8l(attr,8) - 0x40000 : get8l(attr,8)));
// unsane = TRUE;
errors++;
}
}
if (!unsane) {
key = get4l(attr,4);
if (opt_v >= 2)
printf("Key 0x%x\n",(int)key);
if (key) {
if (key <= prevkey) {
printf("** Unordered key 0x%lx after 0x%lx\n",
(long)key,(long)prevkey);
unsane = TRUE;
errors++;
}
}
}
}
return (!unsane);
}
/*
* Check whether a SDS entry is consistent with other known data
* and store current data for subsequent checks
*/
static int consist_sds(const char *attr, unsigned int offset,
unsigned int entrysz, BOOL second)
{
int errcnt;
u32 key;
u32 comphash;
struct SECURITY_DATA *psecurdata;
errcnt = 0;
key = get4l(attr,4);
if ((key > 0) && (key < MAXSECURID)) {
printf("Valid entry at 0x%lx for key 0x%lx\n",
(long)offset,(long)key);
if (!securdata[key >> SECBLKSZ])
newblock(key);
if (securdata[key >> SECBLKSZ]) {
psecurdata = &securdata[key >> SECBLKSZ][key & ((1 << SECBLKSZ) - 1)];
comphash = hash((const le32*)&attr[20],entrysz-20);
if (psecurdata->flags & INSDS1) {
if (psecurdata->hash != comphash) {
printf("** Different hash values : $SDS-1 0x%08lx $SDS-2 0x%08lx\n",
(unsigned long)psecurdata->hash,
(unsigned long)comphash);
errcnt++;
errors++;
}
if (psecurdata->offset != get8l(attr,8)) {
printf("** Different offsets : $SDS-1 0x%llx $SDS-2 0x%llx\n",
(long long)psecurdata->offset,(long long)get8l(attr,8));
errcnt++;
errors++;
}
if (psecurdata->length != get4l(attr,16)) {
printf("** Different lengths : $SDS-1 0x%lx $SDS-2 0x%lx\n",
(long)psecurdata->length,(long)get4l(attr,16));
errcnt++;
errors++;
}
} else {
if (second) {
printf("** Entry was not present in $SDS-1\n");
errcnt++;
errors++;
}
psecurdata->hash = comphash;
psecurdata->offset = get8l(attr,8);
psecurdata->length = get4l(attr,16);
}
psecurdata->flags |= (second ? INSDS2 : INSDS1);
}
} else
if (key || get4l(attr,0)) {
printf("** Security_id 0x%x out of bounds\n",key);
warnings++;
}
return (errcnt);
}
/*
* Auditing of $SDS
*/
static int audit_sds(BOOL second)
{
static char attr[MAXATTRSZ + 20];
BOOL isdir;
BOOL done;
BOOL unsane;
u32 prevkey;
int errcnt;
int size;
unsigned int entrysz;
unsigned int entryalsz;
unsigned int offset;
int count;
int deleted;
int mode;
if (second)
printf("\nAuditing $SDS-2\n");
else
printf("\nAuditing $SDS-1\n");
errcnt = 0;
offset = (second ? 0x40000 : 0);
count = 0;
deleted = 0;
done = FALSE;
prevkey = 0;
/* get size of first record */
size = ntfs_read_sds(ntfs_context,(char*)attr,20,offset);
if (size != 20) {
if ((size < 0) && (errno == ENOTSUP))
printf("** There is no $SDS-%d in this volume\n",
(second ? 2 : 1));
else {
printf("** Could not open $SDS-%d, size %d\n",
(second ? 2 : 1),size);
errors++;
errcnt++;
}
} else
do {
entrysz = get4l(attr,16);
entryalsz = ((entrysz - 1) | 15) + 1;
if (entryalsz <= (MAXATTRSZ + 20)) {
/* read next header in anticipation, to get its size */
size = ntfs_read_sds(ntfs_context,
(char*)&attr[20],entryalsz,offset + 20);
if (opt_v)
printf("\nAt offset 0x%lx got %lu bytes\n",(long)offset,(long)size);
} else {
printf("** Security attribute is too long (%ld bytes) - stopping\n",
(long)entryalsz);
errcnt++;
}
if ((entryalsz > (MAXATTRSZ + 20)) || (size < (int)(entrysz - 20)))
done = TRUE;
else {
if (opt_v) {
printf("Entry size %d bytes\n",entrysz);
hexdump(&attr[20],size,8);
}
unsane = !valid_sds(attr,offset,entrysz,
size,prevkey,second);
if (!unsane) {
if (!get4l(attr,0) && !get4l(attr,4))
deleted++;
else
count++;
errcnt += consist_sds(attr,offset,
entrysz, second);
if (opt_v >= 2) {
isdir = guess_dir(&attr[20]);
printf("Assuming %s descriptor\n",(isdir ? "directory" : "file"));
showheader(&attr[20],0);
showusid(&attr[20],0);
showgsid(&attr[20],0);
showdacl(&attr[20],isdir,0);
showsacl(&attr[20],isdir,0);
showownership(&attr[20]);
mode = linux_permissions(
&attr[20],isdir);
printf("Interpreted Unix mode 0%03o\n",mode);
}
prevkey = get4l(attr,4);
}
if (!unsane) {
memcpy(attr,&attr[entryalsz],20);
offset += entryalsz;
if (!get4l(attr,16)
|| ((((offset - 1) | 0x3ffff) - offset + 1) < 20)) {
if (second)
offset = ((offset - 1) | 0x7ffff) + 0x40001;
else
offset = ((offset - 1) | 0x7ffff) + 1;
if (opt_v)
printf("Trying next SDS-%d block at offset 0x%lx\n",
(second ? 2 : 1), (long)offset);
size = ntfs_read_sds(ntfs_context,
(char*)attr,20,offset);
if (size != 20) {
if (opt_v)
printf("Assuming end of $SDS, got %d bytes\n",size);
done = TRUE;
}
}
} else {
printf("** Sanity check failed - stopping there\n");
errcnt++;
errors++;
done = TRUE;
}
}
} while (!done);
if (count || deleted || errcnt) {
printf("%d valid and %d deleted entries in $SDS-%d\n",
count,deleted,(second ? 2 : 1));
printf("%d errors in $SDS-%c\n",errcnt,(second ? '2' : '1'));
}
return (errcnt);
}
/*
* Check whether a SII entry is sane
*/
static BOOL valid_sii(const char *entry, u32 prevkey)
{
BOOL valid;
u32 key;
valid = TRUE;
key = get4l(entry,16);
if (key <= prevkey) {
printf("** Unordered key 0x%lx after 0x%lx\n",
(long)key,(long)prevkey);
valid = FALSE;
errors++;
}
prevkey = key;
if (get2l(entry,0) != 20) {
printf("** offset %d (instead of 20)\n",(int)get2l(entry,0));
valid = FALSE;
errors++;
}
if (get2l(entry,2) != 20) {
printf("** size %d (instead of 20)\n",(int)get2l(entry,2));
valid = FALSE;
errors++;
}
if (get4l(entry,4) != 0) {
printf("** fill1 %d (instead of 0)\n",(int)get4l(entry,4));
valid = FALSE;
errors++;
}
if (get2l(entry,12) & 1) {
if (get2l(entry,8) != 48) {
printf("** index size %d (instead of 48)\n",(int)get2l(entry,8));
valid = FALSE;
errors++;
}
} else
if (get2l(entry,8) != 40) {
printf("** index size %d (instead of 40)\n",(int)get2l(entry,8));
valid = FALSE;
errors++;
}
if (get2l(entry,10) != 4) {
printf("** index key size %d (instead of 4)\n",(int)get2l(entry,10));
valid = FALSE;
errors++;
}
if ((get2l(entry,12) & ~3) != 0) {
printf("** flags 0x%x (instead of < 4)\n",(int)get2l(entry,12));
valid = FALSE;
errors++;
}
if (get2l(entry,14) != 0) {
printf("** fill2 %d (instead of 0)\n",(int)get2l(entry,14));
valid = FALSE;
errors++;
}
if (get4l(entry,24) != key) {
printf("** key 0x%x (instead of 0x%x)\n",
(int)get4l(entry,24),(int)key);
valid = FALSE;
errors++;
}
return (valid);
}
/*
* Check whether a SII entry is consistent with other known data
*/
static int consist_sii(const char *entry)
{
int errcnt;
u32 key;
struct SECURITY_DATA *psecurdata;
errcnt = 0;
key = get4l(entry,16);
if ((key > 0) && (key < MAXSECURID)) {
printf("Valid entry for key 0x%lx\n",(long)key);
if (!securdata[key >> SECBLKSZ])
newblock(key);
if (securdata[key >> SECBLKSZ]) {
psecurdata = &securdata[key >> SECBLKSZ][key & ((1 << SECBLKSZ) - 1)];
psecurdata->flags |= INSII;
if (psecurdata->flags & (INSDS1 | INSDS2)) {
if ((u32)get4l(entry,20) != psecurdata->hash) {
printf("** hash 0x%x (instead of 0x%x)\n",
(unsigned int)get4l(entry,20),
(unsigned int)psecurdata->hash);
errors++;
}
if (get8l(entry,28) != psecurdata->offset) {
printf("** offset 0x%llx (instead of 0x%llx)\n",
(long long)get8l(entry,28),
(long long)psecurdata->offset);
errors++;
}
if (get4l(entry,36) != psecurdata->length) {
printf("** length 0x%lx (instead of %ld)\n",
(long)get4l(entry,36),
(long)psecurdata->length);
errors++;
}
} else {
printf("** Entry was not present in $SDS\n");
errors++;
psecurdata->hash = get4l(entry,20);
psecurdata->offset = get8l(entry,28);
psecurdata->length = get4l(entry,36);
if (opt_v) {
printf(" hash 0x%x\n",(unsigned int)psecurdata->hash);
printf(" offset 0x%llx\n",(long long)psecurdata->offset);
printf(" length %ld\n",(long)psecurdata->length);
}
errcnt++;
}
}
} else {
printf("** Security_id 0x%x out of bounds\n",key);
warnings++;
}
return (errcnt);
}
/*
* Auditing of $SII
*/
static int audit_sii(void)
{
char *entry;
int errcnt;
u32 prevkey;
BOOL valid;
BOOL done;
int count;
printf("\nAuditing $SII\n");
errcnt = 0;
count = 0;
entry = (char*)NULL;
prevkey = 0;
done = FALSE;
do {
entry = (char*)ntfs_read_sii(ntfs_context,(INDEX_ENTRY*)entry);
if (entry) {
valid = valid_sii(entry,prevkey);
if (valid) {
count++;
errcnt += consist_sii(entry);
prevkey = get4l(entry,16);
} else
errcnt++;
} else
if ((errno == ENOTSUP) && !prevkey)
printf("** There is no $SII in this volume\n");
} while (entry && !done);
if (count || errcnt) {
printf("%d valid entries in $SII\n",count);
printf("%d errors in $SII\n",errcnt);
}
return (errcnt);
}
/*
* Check whether a SII entry is sane
*/
static BOOL valid_sdh(const char *entry, u32 prevkey, u32 prevhash)
{
BOOL valid;
u32 key;
u32 currhash;
valid = TRUE;
currhash = get4l(entry,16);
key = get4l(entry,20);
if ((currhash < prevhash)
|| ((currhash == prevhash) && (key <= prevkey))) {
printf("** Unordered hash and key 0x%x 0x%x after 0x%x 0x%x\n",
(unsigned int)currhash,(unsigned int)key,
(unsigned int)prevhash,(unsigned int)prevkey);
valid = FALSE;
errors++;
}
if ((opt_v >= 2) && (currhash == prevhash))
printf("Hash collision (not an error)\n");
if (get2l(entry,0) != 24) {
printf("** offset %d (instead of 24)\n",(int)get2l(entry,0));
valid = FALSE;
errors++;
}
if (get2l(entry,2) != 20) {
printf("** size %d (instead of 20)\n",(int)get2l(entry,2));
valid = FALSE;
errors++;
}
if (get4l(entry,4) != 0) {
printf("** fill1 %d (instead of 0)\n",(int)get4l(entry,4));
valid = FALSE;
errors++;
}
if (get2l(entry,12) & 1) {
if (get2l(entry,8) != 56) {
printf("** index size %d (instead of 56)\n",(int)get2l(entry,8));
valid = FALSE;
errors++;
}
} else
if (get2l(entry,8) != 48) {
printf("** index size %d (instead of 48)\n",(int)get2l(entry,8));
valid = FALSE;
errors++;
}
if (get2l(entry,10) != 8) {
printf("** index key size %d (instead of 8)\n",(int)get2l(entry,10));
valid = FALSE;
errors++;
}
if ((get2l(entry,12) & ~3) != 0) {
printf("** flags 0x%x (instead of < 4)\n",(int)get2l(entry,12));
valid = FALSE;
errors++;
}
if (get2l(entry,14) != 0) {
printf("** fill2 %d (instead of 0)\n",(int)get2l(entry,14));
valid = FALSE;
errors++;
}
if ((u32)get4l(entry,24) != currhash) {
printf("** hash 0x%x (instead of 0x%x)\n",
(unsigned int)get4l(entry,24),(unsigned int)currhash);
valid = FALSE;
errors++;
}
if (get4l(entry,28) != key) {
printf("** key 0x%x (instead of 0x%x)\n",
(int)get4l(entry,28),(int)key);
valid = FALSE;
errors++;
}
if (get4l(entry,44)
&& (get4l(entry,44) != 0x490049)) {
printf("** fill3 0x%lx (instead of 0 or 0x490049)\n",
(long)get4l(entry,44));
valid = FALSE;
errors++;
}
return (valid);
}
/*
* Check whether a SDH entry is consistent with other known data
*/
static int consist_sdh(const char *entry)
{
int errcnt;
u32 key;
struct SECURITY_DATA *psecurdata;
errcnt = 0;
key = get4l(entry,20);
if ((key > 0) && (key < MAXSECURID)) {
printf("Valid entry for key 0x%lx\n",(long)key);
if (!securdata[key >> SECBLKSZ])
newblock(key);
if (securdata[key >> SECBLKSZ]) {
psecurdata = &securdata[key >> SECBLKSZ][key & ((1 << SECBLKSZ) - 1)];
psecurdata->flags |= INSDH;
if (psecurdata->flags & (INSDS1 | INSDS2 | INSII)) {
if ((u32)get4l(entry,24) != psecurdata->hash) {
printf("** hash 0x%x (instead of 0x%x)\n",
(unsigned int)get4l(entry,24),
(unsigned int)psecurdata->hash);
errors++;
}
if (get8l(entry,32) != psecurdata->offset) {
printf("** offset 0x%llx (instead of 0x%llx)\n",
(long long)get8l(entry,32),
(long long)psecurdata->offset);
errors++;
}
if (get4l(entry,40) != psecurdata->length) {
printf("** length %ld (instead of %ld)\n",
(long)get4l(entry,40),
(long)psecurdata->length);
errors++;
}
} else {
printf("** Entry was not present in $SDS nor in $SII\n");
errors++;
psecurdata->hash = get4l(entry,24);
psecurdata->offset = get8l(entry,32);
psecurdata->length = get4l(entry,40);
if (opt_v) {
printf(" offset 0x%llx\n",(long long)psecurdata->offset);
printf(" length %ld\n",(long)psecurdata->length);
}
errcnt++;
}
}
} else {
printf("** Security_id 0x%x out of bounds\n",key);
warnings++;
}
return (errcnt);
}
/*
* Auditing of $SDH
*/
static int audit_sdh(void)
{
char *entry;
int errcnt;
int count;
u32 prevkey;
u32 prevhash;
BOOL valid;
BOOL done;
printf("\nAuditing $SDH\n");
count = 0;
errcnt = 0;
prevkey = 0;
prevhash = 0;
entry = (char*)NULL;
done = FALSE;
do {
entry = (char*)ntfs_read_sdh(ntfs_context,(INDEX_ENTRY*)entry);
if (entry) {
valid = valid_sdh(entry,prevkey,prevhash);
if (valid) {
count++;
errcnt += consist_sdh(entry);
prevhash = get4l(entry,16);
prevkey = get4l(entry,20);
} else
errcnt++;
} else
if ((errno == ENOTSUP) && !prevkey)
printf("** There is no $SDH in this volume\n");
} while (entry && !done);
if (count || errcnt) {
printf("%d valid entries in $SDH\n",count);
printf("%d errors in $SDH\n",errcnt);
}
return (errcnt);
}
/*
* Audit summary
*/
static void audit_summary(void)
{
int count;
int flags;
int cnt;
int found;
int i,j;
count = 0;
found = 0;
if (opt_r) printf("Summary of security key use :\n");
for (i=0; i<(MAXSECURID + (1 << SECBLKSZ) - 1)/(1 << SECBLKSZ); i++)
if (securdata[i])
for (j=0; j<(1 << SECBLKSZ); j++) {
flags = securdata[i][j].flags & (INSDS1 + INSDS2 + INSII + INSDH);
if (flags) found++;
if (flags
&& (flags != (INSDS1 + INSDS2 + INSII + INSDH)))
{
if (!count && !opt_r)
printf("\n** Keys not present in all files :\n");
cnt = securdata[i][j].filecount;
if (opt_r)
printf("Key 0x%x used by %d %s, not in",
i*(1 << SECBLKSZ)+j,cnt,
(cnt > 1 ? "files" : "file"));
else
printf("Key 0x%x not in", i*(1 << SECBLKSZ)+j);
if (!(flags & INSDS1))
printf(" SDS-1");
if (!(flags & INSDS2))
printf(" SDS-2");
if (!(flags & INSII))
printf(" SII");
if (!(flags & INSDH))
printf(" SDH");
printf("\n");
count++;
} else {
cnt = securdata[i][j].filecount;
if (opt_r && cnt)
printf("Key 0x%x used by %d %s\n",
i*(1 << SECBLKSZ)+j,cnt,
(cnt > 1 ? "files" : "file"));
}
}
if (found) {
if (count)
printf("%d keys not present in all lists\n",count);
else
printf("All keys are present in all lists\n");
}
}
/*
* Auditing
*/
static BOOL audit(const char *volume)
{
BOOL err;
err = FALSE;
if (!getuid() && open_security_api()) {
if (open_volume(volume,NTFS_MNT_RDONLY)) {
if (audit_sds(FALSE)) err = TRUE;
if (audit_sds(TRUE)) err = TRUE;
if (audit_sii()) err = TRUE;
if (audit_sdh()) err = TRUE;
if (opt_r) recurseshow("/");
audit_summary();
close_volume(volume);
}
else {
fprintf(stderr,"Could not open volume %s\n",volume);
printerror(stdout);
err = TRUE;
}
close_security_api();
}
else {
if (getuid())
fprintf(stderr,"This is only possible as root\n");
else fprintf(stderr,"Could not open security API\n");
err = TRUE;
}
return (err);
}
#if POSIXACLS
/*
* Encode a Posix ACL string
* [d:]{ugmo}:uid[:perms],...
*/
static struct POSIX_SECURITY *encode_posix_acl(const char *str)
{
int acccnt;
int defcnt;
int i,k,l;
int c;
s32 id;
u16 perms;
u16 apermsset;
u16 dpermsset;
u16 tag;
u16 tagsset;
mode_t mode;
BOOL defacl;
BOOL dmask;
BOOL amask;
const char *p;
struct POSIX_ACL *acl;
struct POSIX_SECURITY *pxdesc;
enum { PXBEGIN, PXTAG, PXTAG1, PXID, PXID1, PXID2,
PXPERM, PXPERM1, PXPERM2, PXOCT, PXNEXT, PXEND, PXERR
} state;
/* raw evaluation of ACE count */
p = str;
amask = FALSE;
dmask = FALSE;
if (*p == 'd') {
acccnt = 0;
defcnt = 1;
} else {
if ((*p >= '0') && (*p <= '7'))
acccnt = 0;
else
acccnt = 1;
defcnt = 0;
}
while (*p)
if (*p++ == ',') {
if (*p == 'd') {
defcnt++;
if (p[1] && (p[2] == 'm'))
dmask = TRUE;
} else {
acccnt++;
if (*p == 'm')
amask = TRUE;
}
}
/* account for an implicit mask if none defined */
if (acccnt && !amask)
acccnt++;
if (defcnt && !dmask)
defcnt++;
pxdesc = (struct POSIX_SECURITY*)malloc(sizeof(struct POSIX_SECURITY)
+ (acccnt + defcnt)*sizeof(struct POSIX_ACE));
if (pxdesc) {
pxdesc->acccnt = acccnt;
pxdesc->firstdef = acccnt;
pxdesc->defcnt = defcnt;
acl = &pxdesc->acl;
p = str;
state = PXBEGIN;
id = 0;
defacl = FALSE;
mode = 0;
apermsset = 0;
dpermsset = 0;
tag = 0;
perms = 0;
k = l = 0;
c = *p++;
while ((state != PXEND) && (state != PXERR)) {
switch (state) {
case PXBEGIN :
if (c == 'd') {
defacl = TRUE;
state = PXTAG1;
break;
} else
if ((c >= '0') && (c <= '7')) {
mode = c - '0';
state = PXOCT;
break;
}
defacl = FALSE;
/* fall through */
case PXTAG :
switch (c) {
case 'u' :
tag = POSIX_ACL_USER;
state = PXID;
break;
case 'g' :
tag = POSIX_ACL_GROUP;
state = PXID;
break;
case 'o' :
tag = POSIX_ACL_OTHER;
state = PXID;
break;
case 'm' :
tag = POSIX_ACL_MASK;
state = PXID;
break;
default :
state = PXERR;
break;
}
break;
case PXTAG1 :
if (c == ':')
state = PXTAG;
else
state = PXERR;
break;
case PXID :
if (c == ':') {
if ((tag == POSIX_ACL_OTHER)
|| (tag == POSIX_ACL_MASK))
state = PXPERM;
else
state = PXID1;
} else
state = PXERR;
break;
case PXID1 :
if ((c >= '0') && (c <= '9')) {
id = c - '0';
state = PXID2;
} else
if (c == ':') {
id = -1;
if (tag == POSIX_ACL_USER)
tag = POSIX_ACL_USER_OBJ;
if (tag == POSIX_ACL_GROUP)
tag = POSIX_ACL_GROUP_OBJ;
state = PXPERM1;
} else
state = PXERR;
break;
case PXID2 :
if ((c >= '0') && (c <= '9'))
id = 10*id + c - '0';
else
if (c == ':')
state = PXPERM1;
else
state = PXERR;
break;
case PXPERM :
if (c == ':') {
id = -1;
state = PXPERM1;
} else
state = PXERR;
break;
case PXPERM1 :
if ((c >= '0') && (c <= '7')) {
perms = c - '0';
state = PXNEXT;
break;
}
state = PXPERM2;
perms = 0;
/* fall through */
case PXPERM2 :
switch (c) {
case 'r' :
perms |= POSIX_PERM_R;
break;
case 'w' :
perms |= POSIX_PERM_W;
break;
case 'x' :
perms |= POSIX_PERM_X;
break;
case ',' :
case '\0' :
if (defacl) {
i = acccnt + l++;
dpermsset |= perms;
} else {
i = k++;
apermsset |= perms;
}
acl->ace[i].tag = tag;
acl->ace[i].perms = perms;
acl->ace[i].id = id;
if (c == '\0')
state = PXEND;
else
state = PXBEGIN;
break;
}
break;
case PXNEXT :
if (!c || (c == ',')) {
if (defacl) {
i = acccnt + l++;
dpermsset |= perms;
} else {
i = k++;
apermsset |= perms;
}
acl->ace[i].tag = tag;
acl->ace[i].perms = perms;
acl->ace[i].id = id;
if (c == '\0')
state = PXEND;
else
state = PXBEGIN;
} else
state = PXERR;
break;
case PXOCT :
if ((c >= '0') && (c <= '7'))
mode = (mode << 3) + c - '0';
else
if (c == '\0')
state = PXEND;
else
state = PXBEGIN;
break;
default :
break;
}
c = *p++;
}
/* insert default mask if none defined */
if (acccnt && !amask) {
i = k++;
acl->ace[i].tag = POSIX_ACL_MASK;
acl->ace[i].perms = apermsset;
acl->ace[i].id = -1;
}
if (defcnt && !dmask) {
i = acccnt + l++;
acl->ace[i].tag = POSIX_ACL_MASK;
acl->ace[i].perms = dpermsset;
acl->ace[i].id = -1;
}
/* compute the mode and tagsset */
tagsset = 0;
for (i=0; i<acccnt; i++) {
tagsset |= acl->ace[i].tag;
switch (acl->ace[i].tag) {
case POSIX_ACL_USER_OBJ :
mode |= acl->ace[i].perms << 6;
break;
case POSIX_ACL_GROUP_OBJ :
/* unless mask seen first */
if (!(tagsset & POSIX_ACL_MASK))
mode |= acl->ace[i].perms << 3;
break;
case POSIX_ACL_OTHER :
mode |= acl->ace[i].perms;
break;
case POSIX_ACL_MASK :
/* overrides group */
mode = (mode & 07707)
| (acl->ace[i].perms << 3);
break;
default :
break;
}
}
pxdesc->mode = mode;
pxdesc->tagsset = tagsset;
pxdesc->acl.version = POSIX_VERSION;
pxdesc->acl.flags = 0;
pxdesc->acl.filler = 0;
if (state != PXERR)
ntfs_sort_posix(pxdesc);
showposix(pxdesc);
if ((state == PXERR)
|| (k != acccnt)
|| (l != defcnt)
|| !ntfs_valid_posix(pxdesc)) {
if (~pxdesc->tagsset
& (POSIX_ACL_USER_OBJ | POSIX_ACL_GROUP_OBJ | POSIX_ACL_OTHER))
fprintf(stderr,"User, group or other permissions missing\n");
else
fprintf(stderr,"Bad ACL description\n");
free(pxdesc);
pxdesc = (struct POSIX_SECURITY*)NULL;
} else
if (opt_v >= 2) {
printf("Interpreted input description :\n");
showposix(pxdesc);
}
} else
errno = ENOMEM;
return (pxdesc);
}
#endif /* POSIXACLS */
static BOOL setperms(const char *volume, const char *perms, const char *base)
{
const char *p;
BOOL cmderr;
int i;
#if POSIXACLS
struct POSIX_SECURITY *pxdesc;
#else /* POSIXACLS */
int mode;
#endif /* POSIXACLS */
cmderr = FALSE;
p = perms;
#if POSIXACLS
pxdesc = encode_posix_acl(p);
if (pxdesc) {
if (!getuid() && open_security_api()) {
if (open_volume(volume,NTFS_MNT_NONE)) {
if (opt_r) {
for (i=0; i<(MAXSECURID + (1 << SECBLKSZ) - 1)/(1 << SECBLKSZ); i++)
securdata[i] = (struct SECURITY_DATA*)NULL;
recurseset_posix(base,pxdesc);
} else
singleset_posix(base,pxdesc);
close_volume(volume);
} else {
fprintf(stderr,"Could not open volume %s\n",volume);
printerror(stderr);
cmderr = TRUE;
}
close_security_api();
} else {
if (getuid())
fprintf(stderr,"This is only possible as root\n");
else
fprintf(stderr,"Could not open security API\n");
cmderr = TRUE;
}
free(pxdesc);
} else
cmderr = TRUE;
#else /* POSIXACLS */
mode = 0;
while ((*p >= '0') && (*p <= '7'))
mode = (mode << 3) + (*p++) - '0';
if (*p) {
fprintf(stderr,"New mode should be given in octal\n");
cmderr = TRUE;
} else {
if (!getuid() && open_security_api()) {
if (open_volume(volume,NTFS_MNT_NONE)) {
if (opt_r) {
for (i=0; i<(MAXSECURID + (1 << SECBLKSZ) - 1)/(1 << SECBLKSZ); i++)
securdata[i] = (struct SECURITY_DATA*)NULL;
recurseset(base,mode);
} else
singleset(base,mode);
close_volume(volume);
} else {
fprintf(stderr,"Could not open volume %s\n",volume);
printerror(stderr);
cmderr = TRUE;
}
close_security_api();
} else {
if (getuid())
fprintf(stderr,"This is only possible as root\n");
else
fprintf(stderr,"Could not open security API\n");
cmderr = TRUE;
}
}
#endif /* POSIXACLS */
return (cmderr);
}
static void usage(void)
{
#ifdef HAVE_WINDOWS_H
fprintf(stderr,"Usage:\n");
#ifdef SELFTESTS
fprintf(stderr," ntfssecaudit -t\n");
fprintf(stderr," run self-tests\n");
#endif /* SELFTESTS */
fprintf(stderr," ntfssecaudit -h [file]\n");
fprintf(stderr," display security descriptors within file\n");
fprintf(stderr," ntfssecaudit -a[rv] volume\n");
fprintf(stderr," audit the volume\n");
fprintf(stderr," ntfssecaudit [-v] file\n");
fprintf(stderr," display the security parameters of file\n");
fprintf(stderr," ntfssecaudit -r[v] directory\n");
fprintf(stderr," display the security parameters of files in directory\n");
fprintf(stderr," ntfssecaudit -b[v] directory\n");
fprintf(stderr," backup the security parameters of files in directory\n");
fprintf(stderr," ntfssecaudit -s[ev] volume [backupfile]\n");
fprintf(stderr," set the security parameters as indicated in backup file\n");
fprintf(stderr," with -e also set extra parameters (Windows attrib)\n");
fprintf(stderr," ntfssecaudit perms file\n");
fprintf(stderr," set the security parameters of file to perms\n");
fprintf(stderr," ntfssecaudit -r[v] perms directory\n");
fprintf(stderr," set the security parameters of files in directory to perms\n");
fprintf(stderr," ntfssecaudit -u file\n");
fprintf(stderr," get a user mapping proposal applicable to file\n");
#if POSIXACLS
fprintf(stderr," Notes: perms can be an octal mode or a Posix ACL description\n");
#else /* POSIXACLS */
fprintf(stderr," Notes: perms is an octal mode\n");
#endif /* POSIXACLS */
fprintf(stderr," volume is a drive letter and colon (eg. D:)\n");
fprintf(stderr," -v is for verbose, -vv for very verbose\n");
#else /* HAVE_WINDOWS_H */
fprintf(stderr,"Usage:\n");
#ifdef SELFTESTS
fprintf(stderr," ntfssecaudit -t\n");
fprintf(stderr," run self-tests\n");
#endif /* SELFTESTS */
fprintf(stderr," ntfssecaudit -h [file]\n");
fprintf(stderr," display security descriptors within file\n");
fprintf(stderr," ntfssecaudit -a[rv] volume\n");
fprintf(stderr," audit the volume\n");
fprintf(stderr," ntfssecaudit [-v] volume file\n");
fprintf(stderr," display the security parameters of file\n");
fprintf(stderr," ntfssecaudit -r[v] volume directory\n");
fprintf(stderr," display the security parameters of files in directory\n");
fprintf(stderr," ntfssecaudit -b[v] volume directory\n");
fprintf(stderr," backup the security parameters of files in directory\n");
fprintf(stderr," ntfssecaudit -s[ev] volume [backupfile]\n");
fprintf(stderr," set the security parameters as indicated in backup file\n");
fprintf(stderr," with -e also set extra parameters (Windows attrib)\n");
fprintf(stderr," ntfssecaudit volume perms file\n");
fprintf(stderr," set the security parameters of file to perms\n");
fprintf(stderr," ntfssecaudit -r[v] volume perms directory\n");
fprintf(stderr," set the security parameters of files in directory to perms\n");
#ifdef HAVE_SETXATTR
fprintf(stderr," special cases, do not require being root :\n");
fprintf(stderr," ntfssecaudit -u mounted-file\n");
fprintf(stderr," get a user mapping proposal applicable to mounted file\n");
fprintf(stderr," ntfssecaudit [-v] mounted-file\n");
fprintf(stderr," display the security parameters of a mounted file\n");
#endif /* HAVE_SETXATTR */
#if POSIXACLS
fprintf(stderr," Notes: perms can be an octal mode or a Posix ACL description\n");
#else /* POSIXACLS */
fprintf(stderr," Notes: perms is an octal mode\n");
#endif /* POSIXACLS */
#if defined(__sun) && defined (__SVR4)
fprintf(stderr," volume is a partition designator (eg. /dev/dsk/c5t0d0p1)\n");
#else /* defined(__sun) && defined (__SVR4) */
fprintf(stderr," volume is a partition designator (eg. /dev/sdb2)\n");
#endif /* defined(__sun) && defined (__SVR4) */
fprintf(stderr," -v is for verbose, -vv for very verbose\n");
#endif /* HAVE_WINDOWS_H */
}
static void version(void)
{
static const char *EXEC_NAME = "ntfssecaudit";
// confusing (see banner)
printf("\n%s v%s (libntfs-3g) - Audit security data on a NTFS "
"Volume.\n\n", EXEC_NAME, VERSION);
printf(" Copyright (c) 2007-2016 Jean-Pierre Andre\n");
printf("\n%s\n%s%s\n", ntfs_gpl, ntfs_bugs, ntfs_home);
}
#ifdef HAVE_WINDOWS_H
/*
* Split a Windows file designator into volume and fullpath
*/
static BOOL splitarg(char **split, const char *arg)
{
char curdir[MAXFILENAME];
BOOL err;
BOOL withvol;
BOOL withfullpath;
int lthd;
char *volume;
char *filename;
err = TRUE;
withvol = arg[0] && (arg[1] == ':');
if (withvol)
withfullpath = (arg[2] == '/') || (arg[2] == '\\');
else
withfullpath = (arg[0] == '/') || (arg[0] == '\\');
lthd = 0;
if (!withvol || !withfullpath) {
if (getcwd(curdir, sizeof(curdir)))
lthd = strlen(curdir);
}
if (withvol && !withfullpath && arg[2]
&& ((arg[0] ^ curdir[0]) & 0x3f)) {
fprintf(stderr,"%c: is not the current drive,\n",arg[0]);
fprintf(stderr,"please use the full path\n");
} else {
if (withvol && !withfullpath && !arg[2]
&& ((arg[0] ^ curdir[0]) & 0x3f)) {
curdir[2] = '\\';
curdir[3] = 0;
lthd = 3;
}
volume = (char*)malloc(4);
if (volume) {
if (withvol)
volume[0] = arg[0];
else
volume[0] = curdir[0];
volume[1] = ':';
volume[2] = 0;
filename = (char*)malloc(strlen(arg) + lthd + 2);
if (filename) {
if (withfullpath) {
if (withvol)
strcpy(filename, &arg[2]);
else
strcpy(filename, arg);
} else {
strcpy(filename, &curdir[2]);
if (curdir[lthd - 1] != '\\')
strcat(filename, "\\");
if (withvol)
strcat(filename, &arg[2]);
else
strcat(filename, arg);
}
if (!cleanpath(filename)) {
split[0] = volume;
split[1] = filename;
err = FALSE;
} else {
fprintf(stderr,"Bad path %s\n", arg);
}
} else
free(volume);
}
}
return (err);
}
#endif /* HAVE_WINDOWS_H */
/*
* Parse the command-line options
*/
static int parse_options(int argc, char *argv[])
{
static const char *sopt = "-abehHrstuvV";
static const struct option lopt[] = {
{ "audit", no_argument, NULL, 'a' },
{ "backup", no_argument, NULL, 'b' },
{ "extra", no_argument, NULL, 'e' },
{ "help", no_argument, NULL, 'H' },
{ "hexdecode", no_argument, NULL, 'h' },
{ "recurse", no_argument, NULL, 'r' },
{ "set", no_argument, NULL, 's' },
{ "test", no_argument, NULL, 't' },
{ "user-mapping",no_argument, NULL, 'u' },
{ "verbose", no_argument, NULL, 'v' },
{ "version", no_argument, NULL, 'V' },
{ NULL, 0, NULL, 0 }
};
int c = -1;
int err = 0;
int ver = 0;
int help = 0;
int xarg = 0;
CMDS prevcmd;
opterr = 0; /* We'll handle the errors, thank you. */
opt_e = FALSE;
opt_r = FALSE;
opt_v = 0;
cmd = CMD_NONE;
prevcmd = CMD_NONE;
while ((c = getopt_long(argc, argv, sopt, lopt, NULL)) != -1) {
switch (c) {
case 1:
if (!xarg)
xarg = optind - 1;
break;
case 'a':
prevcmd = cmd;
cmd = CMD_AUDIT;
break;
case 'b':
prevcmd = cmd;
cmd = CMD_BACKUP;
break;
case 'e':
opt_e = TRUE;
break;
case 'h':
prevcmd = cmd;
cmd = CMD_HEX;
break;
case 'H':
help++;
break;
case 'r':
opt_r = TRUE;
break;
case 's':
prevcmd = cmd;
cmd = CMD_SET;
break;
#ifdef SELFTESTS
case 't':
prevcmd = cmd;
cmd = CMD_TEST;
break;
#endif
case 'u':
prevcmd = cmd;
cmd = CMD_USERMAP;
break;
case 'v':
opt_v++;
break;
case 'V':
ver++;
break;
default:
if ((c < 'a') || (c > 'z'))
fprintf(stderr,"Unhandled option case: %d.\n", c);
else
fprintf(stderr,"Invalid option -%c\n",c);
err++;
break;
}
if ((cmd != CMD_NONE)
&& (prevcmd != CMD_NONE)
&& (prevcmd != cmd)) {
fprintf(stderr,"Incompatible commands\n");
err++;
}
}
if (!xarg)
xarg = argc;
if (help || err)
cmd = CMD_HELP;
else
if (ver)
cmd = CMD_VERSION;
return (err ? 0 : xarg);
}
int main(int argc, char *argv[])
{
char *split[2];
char *uname;
FILE *fd;
int xarg;
BOOL cmderr;
BOOL fail;
int i;
printf("%s\n",BANNER);
cmderr = FALSE;
fail = FALSE;
errors = 0;
warnings = 0;
split[0] = split[1] = (char*)NULL;
uname = (char*)NULL;
xarg = parse_options(argc,argv);
if (xarg) {
for (i=0; i<(MAXSECURID + (1 << SECBLKSZ) - 1)/(1 << SECBLKSZ); i++)
securdata[i] = (struct SECURITY_DATA*)NULL;
#if POSIXACLS
context.mapping[MAPUSERS] = (struct MAPPING*)NULL;
context.mapping[MAPGROUPS] = (struct MAPPING*)NULL;
#endif /* POSIXACLS */
mappingtype = MAPNONE;
switch (cmd) {
case CMD_AUDIT :
if (xarg == (argc - 1))
fail = audit(argv[xarg]);
else
cmderr = TRUE;
break;
case CMD_BACKUP :
switch (argc - xarg) {
case 1 :
#ifdef HAVE_WINDOWS_H
if (!splitarg(split, argv[xarg]))
fail = backup(split[0], split[1]);
else
cmderr = TRUE;
#else
fail = backup(argv[xarg],"/");
#endif
break;
case 2 :
cmderr = backup(argv[xarg],argv[xarg+1]);
break;
default :
cmderr = TRUE;
break;
}
break;
case CMD_HEX :
switch (argc - xarg) {
case 0 :
showhex(stdin);
break;
case 1 :
fd = fopen(argv[xarg],"rb");
if (fd) {
showhex(fd);
fclose(fd);
} else {
fprintf(stderr,"Could not open %s\n",
argv[xarg]);
cmderr = TRUE;
}
break;
default :
cmderr = TRUE;
break;
}
break;
case CMD_NONE :
switch (argc - xarg) {
case 1 :
#ifdef HAVE_WINDOWS_H
if (!splitarg(split, argv[xarg]))
fail = listfiles(split[0], split[1]);
else
cmderr = TRUE;
#else
if (opt_r)
cmderr = listfiles(argv[xarg],"/");
else
cmderr = processmounted(argv[xarg]);
#endif
break;
case 2 :
#ifdef HAVE_WINDOWS_H
if (!splitarg(split, argv[xarg + 1]))
fail = setperms(split[0],
argv[xarg], split[1]);
else
cmderr = TRUE;
#else /* HAVE_WINDOWS_H */
fail = listfiles(argv[xarg],argv[xarg+1]);
#endif /* HAVE_WINDOWS_H */
break;
case 3 :
#ifdef HAVE_WINDOWS_H
uname = unixname(argv[xarg+2]);
if (uname)
fail = setperms(argv[xarg],
argv[xarg+1], uname);
else
cmderr = TRUE;
#else /* HAVE_WINDOWS_H */
cmderr = setperms(argv[xarg], argv[xarg+1],
argv[xarg+2]);
#endif
break;
default :
cmderr = TRUE;
break;
}
break;
case CMD_SET :
switch (argc - xarg) {
case 1 :
cmderr = dorestore(argv[xarg],stdin);
break;
case 2 :
fd = fopen(argv[xarg+1],"rb");
if (fd) {
if (dorestore(argv[xarg],fd))
cmderr = TRUE;
fclose(fd);
} else {
fprintf(stderr,"Could not open %s\n",
argv[xarg]);
cmderr = TRUE;
}
break;
default :
cmderr = TRUE;
}
break;
#ifdef SELFTESTS
case CMD_TEST :
if (xarg != argc)
cmderr = TRUE;
else
selftests();
break;
#endif
case CMD_USERMAP :
switch (argc - xarg) {
case 1 :
#ifdef HAVE_WINDOWS_H
if (!splitarg(split, argv[xarg]))
fail = mapproposal(split[0],
split[1]);
else
cmderr = TRUE;
#else /* HAVE_WINDOWS_H */
processmounted(argv[xarg]);
#endif /* HAVE_WINDOWS_H */
break;
case 2 :
#ifdef HAVE_WINDOWS_H
uname = unixname(argv[xarg+1]);
if (uname)
cmderr = mapproposal(argv[xarg],
uname);
else
cmderr = TRUE;
#else /* HAVE_WINDOWS_H */
cmderr = TRUE;
#endif /* HAVE_WINDOWS_H */
break;
default :
cmderr = TRUE;
}
break;
case CMD_HELP :
default :
usage();
break;
case CMD_VERSION :
version();
break;
}
if (warnings)
printf("** %u %s signalled\n",warnings,
(warnings > 1 ? "warnings were"
: "warning was"));
if (errors)
printf("** %u %s found\n",errors,
(errors > 1 ? "errors were" : "error was"));
else
if (fail)
printf("Command failed\n");
else
if (cmderr)
usage();
else
printf("No errors were found\n");
if (!isatty(1)) {
fflush(stdout);
if (warnings)
fprintf(stderr,"** %u %s signalled\n",warnings,
(warnings > 1 ? "warnings were"
: "warning was"));
if (errors)
fprintf(stderr,"** %u %s found\n",errors,
(errors > 1 ? "errors were"
: "error was"));
else
fprintf(stderr,"No errors were found\n");
}
if (split[0])
free(split[0]);
if (split[1])
free(split[1]);
if (uname)
free(uname);
freeblocks();
} else
usage();
if (cmderr || errors)
exit(1);
return (0);
}