diff --git a/src/Makefile.am b/src/Makefile.am index e04776c8..d125cd38 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -9,7 +9,7 @@ FUSE_CFLAGS = $(FUSE_MODULE_CFLAGS) FUSE_LIBS = $(FUSE_MODULE_LIBS) endif -bin_PROGRAMS = ntfs-3g.probe +bin_PROGRAMS = ntfs-3g.probe usermap secaudit rootbin_PROGRAMS = ntfs-3g rootsbin_DATA = #Create directory man_MANS = ntfs-3g.8 ntfs-3g.probe.8 @@ -23,14 +23,22 @@ ntfs_3g_CFLAGS = \ -DFUSE_USE_VERSION=26 \ $(FUSE_CFLAGS) \ -I$(top_srcdir)/include/ntfs-3g -ntfs_3g_SOURCES = ntfs-3g.c utils.c utils.h +ntfs_3g_SOURCES = ntfs-3g.c ntfs_3g_probe_LDADD = $(top_builddir)/libntfs-3g/libntfs-3g.la +usermap_LDADD = $(top_builddir)/libntfs-3g/libntfs-3g.la +secaudit_LDADD = $(top_builddir)/libntfs-3g/libntfs-3g.la if REALLYSTATIC ntfs_3g_probe_LDFLAGS = $(AM_LDFLAGS) -all-static +usermap_LDFLAGS = $(AM_LDFLAGS) -all-static +secaudit_LDFLAGS = $(AM_LDFLAGS) -all-static endif ntfs_3g_probe_CFLAGS = $(AM_CFLAGS) -I$(top_srcdir)/include/ntfs-3g -ntfs_3g_probe_SOURCES = ntfs-3g.probe.c utils.c utils.h +usermap_CFLAGS = $(AM_CFLAGS) -I$(top_srcdir)/include/ntfs-3g +secaudit_CFLAGS = $(AM_CFLAGS) -I$(top_srcdir)/include/ntfs-3g +ntfs_3g_probe_SOURCES = ntfs-3g.probe.c +usermap_SOURCES = usermap.c +secaudit_SOURCES = secaudit.c if RUN_LDCONFIG install-exec-hook: diff --git a/src/secaudit.c b/src/secaudit.c new file mode 100644 index 00000000..c4a71b0a --- /dev/null +++ b/src/secaudit.c @@ -0,0 +1,7148 @@ +/* + * Display and audit security attributes in an NTFS volume + * + * Copyright (c) 2007-2009 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 + * -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) : + * secaudit -h [file] + * display the security descriptors found in file + * secaudit -a[rv] volume + * audit the volume + * secaudit [-v] volume file + * display the security parameters of file + * secaudit -r[v] volume directory + * display the security parameters of files in directory + * secaudit -b[v] volume [directory] + * backup the security parameters of files in directory + * secaudit -s[ve] volume [backupfile] + * set the security parameters as indicated in backup + * with -e set extra parameters (Windows attrib) + * secaudit volume perms file + * set the security parameters of file to perms (mode or acl) + * secaudit -r[v] volume perms directory + * set the security parameters of files in directory to perms + * special case, does not require being root : + * secaudit [-v] mounted-file + * display the security parameters of mounted file + * + * + * On Windows (the volume being part of file name) + * secaudit -h [file] + * display the security descriptors found in file + * secaudit [-v] file + * display the security parameters of file + * secaudit -r[v] directory + * display the security parameters of files in directory + * secaudit -b[v] directory + * backup the security parameters of files in directory + * secaudit -s[v] [backupfile] + * set the security parameters as indicated in backup + * with -e set extra parameters (Windows attrib) + * secaudit perms file + * set the security parameters of file to perms (mode or acl) + * secaudit -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 + */ + +/* + * 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.3.8" + +#define GET_FILE_SECURITY "ntfs_get_file_security" +#define SET_FILE_SECURITY "ntfs_set_file_security" +#define GET_FILE_ATTRIBUTES "ntfs_get_file_attributes" +#define SET_FILE_ATTRIBUTES "ntfs_set_file_attributes" +#define READ_DIRECTORY "ntfs_read_directory" +#define READ_SDS "ntfs_read_sds" +#define READ_SII "ntfs_read_sii" +#define READ_SDH "ntfs_read_sdh" +#define GET_USER "ntfs_get_user" +#define GET_GROUP "ntfs_get_group" +#define GET_USID "ntfs_get_usid" +#define GET_GSID "ntfs_get_gsid" +#define INIT_FILE_SECURITY "ntfs_initialize_file_security" +#define LEAVE_FILE_SECURITY "ntfs_leave_file_security" + +/* + * External declarations + */ + +#include +#include +#include +#include +#include +#include +#include +#include + + /* + * integration into secaudit, check whether Win32, + * may have to be adapted to compiler or something else + */ + +#ifndef WIN32 +#if defined(__WIN32) | defined(__WIN32__) | defined(WNSC) +#define WIN32 1 +#endif +#endif + + /* + * integration into secaudit/Win32 + */ +#ifdef WIN32 +#include +#define __LITTLE_ENDIAN 1234 +#define __BYTE_ORDER __LITTLE_ENDIAN +#else + /* + * integration into secaudit/STSC + */ +#ifdef STSC +#include +#undef __BYTE_ORDER +#define __BYTE_ORDER __BIG_ENDIAN +#else + /* + * integration into secaudit/Linux + */ + +#include +#include +#include +#include +#endif /* STSC */ +#endif /* WIN32 */ +#include "secaudit.h" + +#ifndef WIN32 + +#ifndef STSC + +#if !defined(HAVE_CONFIG_H) && POSIXACLS + /* require if not integrated into ntfs-3g package */ +#define HAVE_SETXATTR 1 +#endif + +#ifdef HAVE_CONFIG_H + /* according to config.h if integrated into ntfs-3g package */ +#include "config.h" +#ifndef POSIXACLS +#define POSIXACLS 0 +#endif +#endif /* HAVE_CONFIG_H */ + +#ifdef HAVE_SETXATTR +#include +#else +#warning "The extended attribute package is not available" +#endif /* HAVE_SETXATTR */ + +#endif /* STSC */ + +#define MS_NONE 0 /* no flag for mounting the device */ +#define MS_RDONLY 1 /* flag for mounting the device read-only */ + +struct CALLBACK; + +typedef int (*dircallback)(struct CALLBACK *context, char *ntfsname, + int length, int type, long long pos, u64 mft_ref, + unsigned int dt_type); + +#if USESTUBS | defined(STSC) + +int ntfs_get_file_security(void *scapi, + const char *path, DWORD selection, + char *buf, DWORD buflen, LPDWORD psize); +BOOL ntfs_set_file_security(void *scapi, + const char *path, DWORD selection, const char *attr); +int ntfs_get_file_attributes(void *scapi, + const char *path); +BOOL ntfs_set_file_attributes(void *scapi, + const char *path, DWORD attrib); +BOOL ntfs_read_directory(void *scapi, + const char *path, dircallback callback, void *context); +int ntfs_read_sds(void *scapi, + char *buf, DWORD buflen, DWORD offset); +void *ntfs_read_sii(void *scapi, void *entry); +void *ntfs_read_sdh(void *scapi, void *entry); + +int ntfs_get_usid(void *scapi, uid_t uid, char *buf); +int ntfs_get_gsid(void *scapi, gid_t gid, char *buf); +int ntfs_get_user(void *scapi, const char *usid); +int ntfs_get_group(void *scapi, const char *gsid); + +void *ntfs_initialize_file_security(const char *device, int flags); +BOOL ntfs_leave_file_security(void *scapi); + +#else + +typedef int (*type_get_file_security)(void *scapi, + const char *path, DWORD selection, + char *buf, DWORD buflen, LPDWORD psize); +typedef BOOL (*type_set_file_security)(void *scapi, + const char *path, DWORD selection, const char *attr); +typedef int (*type_get_file_attributes)(void *scapi, + const char *path); +typedef BOOL (*type_set_file_attributes)(void *scapi, + const char *path, DWORD attrib); +typedef BOOL (*type_read_directory)(void *scapi, + const char *path, dircallback callback, void *context); +typedef int (*type_read_sds)(void *scapi, + char *buf, DWORD buflen, DWORD offset); +typedef void *(*type_read_sii)(void *scapi, void *entry); +typedef void *(*type_read_sdh)(void *scapi, void *entry); + +typedef int (*type_get_usid)(void *scapi, uid_t uid, char *buf); +typedef int (*type_get_gsid)(void *scapi, gid_t gid, char *buf); +typedef int (*type_get_user)(void *scapi, const char *usid); +typedef int (*type_get_group)(void *scapi, const char *gsid); + +typedef void *(*type_initialize_file_security)(const char *device, int flags); +typedef BOOL (*type_leave_file_security)(void *scapi); + +type_get_file_security ntfs_get_file_security; +type_set_file_security ntfs_set_file_security; +type_get_file_attributes ntfs_get_file_attributes; +type_set_file_attributes ntfs_set_file_attributes; +type_read_directory ntfs_read_directory; +type_read_sds ntfs_read_sds; +type_read_sii ntfs_read_sii; +type_read_sdh ntfs_read_sdh; + +type_get_usid ntfs_get_usid; +type_get_gsid ntfs_get_gsid; +type_get_user ntfs_get_user; +type_get_group ntfs_get_group; + +type_initialize_file_security ntfs_initialize_file_security; +type_leave_file_security ntfs_leave_file_security; + + +#endif /* USESTUBS | defined(STSC) */ +#endif /* WIN32 */ + +/* + * Prototypes for local functions + */ + +BOOL open_security_api(void); +BOOL close_security_api(void); +#ifndef WIN32 +BOOL open_volume(const char*, int flags); +BOOL close_volume(const char*); +#endif +unsigned int get2l(const char*, int); +unsigned long get4l(const char*, int); +u64 get6l(const char*, int); +u64 get6h(const char*, int); +u64 get8l(const char*, int); +void set2l(char*, unsigned int); +void set4l(char*, unsigned long); +void hexdump(const char*, int, int); +u32 hash(const le32*, int); +unsigned int utf8size(const char*, int); +unsigned int makeutf8(char*, const char*, int); +unsigned int utf16size(const char*); +unsigned int makeutf16(char*, const char*); +unsigned int utf16len(const char*); +void printname(FILE*, const char*); +void printerror(FILE*); +BOOL guess_dir(const char*); +void showsid(const char*, int, int); +void showusid(const char*, int); +void showgsid(const char*, int); +void showheader(const char*, int); +void showace(const char*, int, int, int); +void showacl(const char*, int, int, int); +void showdacl(const char*, int, int); +void showsacl(const char*, int, int); +void showall(const char*, int); +void showposix(const struct POSIX_SECURITY*); +int linux_permissions(const char*, BOOL); +uid_t linux_owner(const char*); +gid_t linux_group(const char*); +int basicread(void*, char*, size_t, off_t); +int dummyread(void*, char*, size_t, off_t); +int local_build_mapping(struct MAPPING *[], const char*); +void newblock(s32); +void freeblocks(void); +u32 getmsbhex(const char*); +u32 getlsbhex(const char*); +BOOL ishexdump(const char*, int, int); +void showhex(FILE*); +void showfull(const char*, BOOL); +BOOL applyattr(const char*, const char*, BOOL, int, s32); +BOOL restore(FILE*); +BOOL dorestore(const char*, FILE*); +u32 merge_rights(const struct POSIX_SECURITY*, BOOL); +void tryposix(struct POSIX_SECURITY*); +void check_samples(void); +void basictest(int, BOOL, const SID*, const SID*); +void posixtest(int, BOOL, const SID*, const SID*); +void selftests(void); +unsigned int getfull(char*, const char*); +BOOL updatefull(const char *name, DWORD flags, char *attr); +BOOL setfull(const char*, int, BOOL); +BOOL singleshow(const char*); +void showmounted(const char*); +BOOL recurseshow(const char*); +BOOL singleset(const char*, int); +BOOL recurseset(const char*, int); +#ifdef WIN32 +BOOL backup(const char*); +BOOL listfiles(const char*); +#if POSIXACLS +BOOL iterate(RECURSE, const char*, const struct POSIX_SECURITY*); +#else +BOOL iterate(RECURSE, const char*, mode_t); +#endif +#else +BOOL backup(const char*, const char*); +BOOL listfiles(const char*, const char*); +#endif +#if POSIXACLS +BOOL setfull_posix(const char *, const struct POSIX_SECURITY*, BOOL); +struct POSIX_SECURITY *linux_permissions_posix(const char*, BOOL); +BOOL recurseset_posix(const char*, const struct POSIX_SECURITY*); +BOOL singleset_posix(const char*, const struct POSIX_SECURITY*); +struct POSIX_SECURITY *encode_posix_acl(const char*); +#endif +static void stdfree(void*); + +BOOL valid_sds(const char*, unsigned int, unsigned int, + unsigned int, u32, BOOL); +BOOL valid_sii(const char*, u32); +BOOL valid_sdh(const char*, u32, u32); +int consist_sds(const char*, unsigned int, unsigned int, BOOL); +int consist_sii(const char*); +int consist_sdh(const char*); +int audit_sds(BOOL); +int audit_sii(void); +int audit_sdh(void); +void audit_summary(void); +BOOL audit(const char*); +int getoptions(int, char*[]); + +#ifndef WIN32 + +/* + * Structures for collecting directory contents (Linux only) + */ + +struct LINK { + struct LINK *next; + char name[1]; +} ; + +struct CALLBACK { + struct LINK *head; + const char *dir; +} ; + +int callback(struct CALLBACK *context, char *ntfsname, + int length, int type, long long pos, u64 mft_ref, + unsigned int dt_type); +#endif + +/* + * Global constants + */ + +#define BANNER "secaudit " AUDT_VERSION " : NTFS security data auditing" + +#if SELFTESTS & !USESTUBS + +/* + * 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 world (S-1-1-0) + */ + +static const char worldsidbytes[] = { + 1, /* revision */ + 1, /* auth count */ + 0, 0, 0, 0, 0, 1, /* base */ + 0, 0, 0, 0 /* 1st level */ +} ; +static const SID *worldsid = (const SID*)worldsidbytes; + +/* + * SID for administrator + */ + +static const char adminsidbytes[] = { + 1, /* revision */ + 2, /* auth count */ + 0, 0, 0, 0, 0, 5, /* base */ + 32, 0, 0, 0, /* 1st level */ + 32, 2, 0, 0 /* 2nd level */ +}; + +static const SID *adminsid = (const SID*)adminsidbytes; + +/* + * SID for system + */ + +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 + +/* + * Global variables + */ + +BOOL opt_a; /* audit security data */ +BOOL opt_b; /* backup NTFS ACLs */ +BOOL opt_e; /* restore extra (currently windows attribs) */ +BOOL opt_h; /* display an hexadecimal descriptor in a file */ +BOOL opt_r; /* recursively apply to subdirectories */ +BOOL opt_s; /* restore NTFS ACLs */ +#if SELFTESTS & !USESTUBS +BOOL opt_t; /* run self-tests */ +#endif +int opt_v; /* verbose or very verbose*/ +struct SECURITY_DATA *securdata[(MAXSECURID + (1 << SECBLKSZ) - 1)/(1 << SECBLKSZ)]; +#if FORCEMASK +BOOL opt_m; /* force a mask - dangerous */ +u32 forcemsk /* mask to force */ +#endif +unsigned int errors; /* number of severe errors */ +unsigned int warnings; /* number of non-severe errors */ + +struct CHKALLOC *firstalloc; +struct SECURITY_CONTEXT context; +MAPTYPE mappingtype; + +#ifndef WIN32 + +void *ntfs_handle; +void *ntfs_context = (void*)NULL; + +/* + * Open and close the security API (platform dependent) + */ + +BOOL open_security_api(void) +{ +#if USESTUBS | defined(STSC) + return (TRUE); +#else + char *error; + BOOL err; + const char *libfile; + + err = TRUE; + libfile = getenv(ENVNTFS3G); + if (!libfile) + libfile = (sizeof(char*) == 8 ? LIBFILE64 : LIBFILE); + ntfs_handle = dlopen(libfile,RTLD_LAZY); + if (ntfs_handle) { + ntfs_initialize_file_security = (type_initialize_file_security) + dlsym(ntfs_handle,INIT_FILE_SECURITY); + error = dlerror(); + if (error) + fprintf(stderr," %s\n",error); + else { + ntfs_leave_file_security = (type_leave_file_security) + dlsym(ntfs_handle,LEAVE_FILE_SECURITY); + ntfs_get_file_security = (type_get_file_security) + dlsym(ntfs_handle,GET_FILE_SECURITY); + ntfs_set_file_security = (type_set_file_security) + dlsym(ntfs_handle,SET_FILE_SECURITY); + ntfs_get_file_attributes = (type_get_file_attributes) + dlsym(ntfs_handle,GET_FILE_ATTRIBUTES); + ntfs_set_file_attributes = (type_set_file_attributes) + dlsym(ntfs_handle,SET_FILE_ATTRIBUTES); + ntfs_read_directory = (type_read_directory) + dlsym(ntfs_handle,READ_DIRECTORY); + ntfs_read_sds = (type_read_sds) + dlsym(ntfs_handle,READ_SDS); + ntfs_read_sii = (type_read_sii) + dlsym(ntfs_handle,READ_SII); + ntfs_read_sdh = (type_read_sdh) + dlsym(ntfs_handle,READ_SDH); + ntfs_get_user = (type_get_user) + dlsym(ntfs_handle,GET_USER); + ntfs_get_group = (type_get_group) + dlsym(ntfs_handle,GET_GROUP); + ntfs_get_usid = (type_get_usid) + dlsym(ntfs_handle,GET_USID); + ntfs_get_gsid = (type_get_gsid) + dlsym(ntfs_handle,GET_GSID); + err = !ntfs_initialize_file_security + || !ntfs_leave_file_security + || !ntfs_get_file_security + || !ntfs_set_file_security + || !ntfs_get_file_attributes + || !ntfs_set_file_attributes + || !ntfs_read_directory + || !ntfs_read_sds + || !ntfs_read_sii + || !ntfs_read_sdh + || !ntfs_get_user + || !ntfs_get_group + || !ntfs_get_usid + || !ntfs_get_gsid; + + if (error) + fprintf(stderr,"ntfs-3g API not available\n"); + } + } else { + fprintf(stderr,"Could not open ntfs-3g library\n"); + fprintf(stderr,"\nPlease set environment variable \"" ENVNTFS3G "\"\n"); + fprintf(stderr,"to appropriate path and retry\n"); + } + return (!err); +#endif /* USESTUBS | defined(STSC) */ +} + +BOOL close_security_api(void) +{ +#if USESTUBS | defined(STSC) + return (0); +#else + return (!dlclose(ntfs_handle)); +#endif /* USESTUBS | defined(STSC) */ +} + +/* + * Open and close a volume (platform dependent) + * Assumes a single volume is opened + */ + +BOOL open_volume(const char *volume, int 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 secaudit" + " are not compatible\n"); + } else { + fprintf(stderr,"\"%s\" opened\n",volume); + mappingtype = MAPEXTERN; + ok = TRUE; + } + } else { + fprintf(stderr,"Could not open \"%s\"\n",volume); + fprintf(stderr,"Make sure \"%s\" is not mounted\n",volume); + } + } else + fprintf(stderr,"A volume is already open\n"); + return (ok); +} + +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 = (void*)NULL; + return (r); +} + +#endif /* WIN32 */ + +/* + * Extract small or big endian data from an array of bytes + */ + +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); +} + +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); +} + +u64 get6l(const char *attr, int p) +{ + int i; + u64 v; + + v = 0; + for (i=0; i<6; i++) + v += ((long long)(attr[p+i] & 255)) << (8*i); + return (v); +} + +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); +} + +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 + */ + +void set2l(char *p, unsigned int v) +{ + int i; + + for (i=0; i<2; i++) + p[i] = ((v >> 8*i) & 255); +} + +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 + */ + +void hexdump(const char *attr, int size, int level) +{ + int i,j; + + for (i=0; i> 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 + */ + +unsigned int utf8size(const char *utf16, int length) +{ + int i; + unsigned int size; + enum { BASE, SURR, ERR } state; + + size = 0; + state = BASE; + for (i=0; i<2*length; i+=2) { + switch (state) { + case BASE : + if (utf16[i+1] & 0xf8) { + if ((utf16[i+1] & 0xf8) == 0xd8) + state = (utf16[i+1] & 4 ? ERR : SURR); + else +#if NOREVBOM + if (((utf16[i+1] & 0xff) == 0xff) + && ((utf16[i] & 0xfe) == 0xfe)) + state = ERR; + else + size += 3; +#else + size += 3; +#endif + } else + if ((utf16[i] & 0x80) || utf16[i+1]) + size += 2; + else + size++; + break; + case SURR : + if ((utf16[i+1] & 0xfc) == 0xdc) { + state = BASE; + size += 4; + } else + state = ERR; + break; + case ERR : + break; + } + } + if (state != BASE) + size = 0; + return (size); +} + +/* + * 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 + */ + +unsigned int makeutf8(char *utf8, const char *utf16, int length) +{ + int i; + unsigned int size; + unsigned int rem; + enum { BASE, SURR, ERR } state; + + size = 0; + rem = 0; + state = BASE; + for (i=0; i<2*length; i+=2) { + switch (state) { + case BASE : + if (utf16[i+1] & 0xf8) { + if ((utf16[i+1] & 0xf8) == 0xd8) { + if (utf16[i+1] & 4) + state = ERR; + else { + utf8[size++] = 0xf0 + (utf16[i+1] & 7) + + ((utf16[i] & 0xc0) == 0xc0); + utf8[size++] = 0x80 + (((utf16[i] + 64) >> 2) & 63); + rem = utf16[i] & 3; + state = SURR; + } + } else { +#if NOREVBOM + if (((utf16[i+1] & 0xff) == 0xff) + && ((utf16[i] & 0xfe) == 0xfe)) + state = ERR; + else { + utf8[size++] = 0xe0 + ((utf16[i+1] >> 4) & 15); + utf8[size++] = 0x80 + + ((utf16[i+1] & 15) << 2) + + ((utf16[i] >> 6) & 3); + utf8[size++] = 0x80 + (utf16[i] & 63); + } +#else + utf8[size++] = 0xe0 + ((utf16[i+1] >> 4) & 15); + utf8[size++] = 0x80 + + ((utf16[i+1] & 15) << 2) + + ((utf16[i] >> 6) & 3); + utf8[size++] = 0x80 + (utf16[i] & 63); +#endif + } + } else + if ((utf16[i] & 0x80) || utf16[i+1]) { + utf8[size++] = 0xc0 + + ((utf16[i+1] & 15) << 2) + + ((utf16[i] >> 6) & 3); + utf8[size++] = 0x80 + (utf16[i] & 63); + } else + utf8[size++] = utf16[i]; + break; + case SURR : + if ((utf16[i+1] & 0xfc) == 0xdc) { + utf8[size++] = 0x80 + (rem << 4) + + ((utf16[i+1] & 3) << 2) + + ((utf16[i] >> 6) & 3); + utf8[size++] = 0x80 + (utf16[i] & 63); + state = BASE; + } else + state = ERR; + break; + case ERR : + break; + } + } + utf8[size] = 0; + if (state != BASE) + state = ERR; + return (state == ERR ? 0 : size); +} + +#ifdef WIN32 + +/* + * Evaluate the size of UTF-16LE conversion of a UTF-8 text + * (basic conversions only) + * trailing '\0' not accounted for + */ + +unsigned int utf16size(const char *utf8) +{ + unsigned int size; + const char *p; + int c; + unsigned int code; + enum { BASE, TWO, THREE, THREE2, THREE3, FOUR, ERR } state; + + p = utf8; + size = 0; + state = BASE; + while (*p) { + c = *p++ & 255; + switch (state) { + case BASE : + if (!(c & 0x80)) + size++; + else + if (c < 0xc2) + state = ERR; + else + if (c < 0xe0) + state = TWO; + else + if (c < 0xf0) { + if (c == 0xe0) + state = THREE2; + else + if (c == 0xed) + state = THREE3; + else + state = THREE; + } else + if (c < 0xf8) { + state = FOUR; + code = c & 7; + } else + state = ERR; + break; + case TWO : + if ((c & 0xc0) != 0x80) + state = ERR; + else { + size++; + state = BASE; + } + break; + case THREE : + if ((c & 0xc0) != 0x80) + state = ERR; + else + state = TWO; + break; + case THREE2 : + if ((c & 0xe0) != 0xa0) + state = ERR; + else + state = TWO; + break; + case THREE3 : + if ((c & 0xe0) != 0x80) + state = ERR; + else + state = TWO; + break; + case FOUR : + if ((((code << 6) + (c & 63)) > 0x10f) + || (((code << 6) + (c & 63)) < 0x10)) + state = ERR; + else { + size++; + state = THREE; + } + break; + case ERR : + break; + } + } + if (state != BASE) size = 0; + return (size); +} + +/* + * Convert a UTF8 text to UTF-16LE + * (basic conversions only) + * Note : mbstowcs() not used because on Linux it fails for characters + * not present in current locale + */ + +unsigned int makeutf16(char *target, const char *utf8) +{ + unsigned int size; + unsigned int code; + const char *p; + int c; + enum { BASE, TWO, THREE, THREE2, THREE3, FOUR, FOUR2, FOUR3, ERR } state; + + p = utf8; + size = 0; + c = 0; + state = BASE; + while (*p) { + c = *p++ & 255; + switch (state) { + case BASE : + if (!(c & 0x80)) { + target[2*size] = c; + target[2*size + 1] = 0; + size++; + } else { + if (c < 0xc2) + state = ERR; + else + if (c < 0xe0) { + code = c & 31; + state = TWO; + } else + if (c < 0xf0) { + code = c & 15; + if (c == 0xe0) + state = THREE2; + else + if (c == 0xed) + state = THREE3; + else + state = THREE; + } else + if (c < 0xf8) { + code = c & 7; + state = FOUR; + } else + state = ERR; + } + break; + case TWO : +#if NOREVBOM + if (((c & 0xc0) != 0x80) + || ((code == 0x3ff) && (c >= 0xbe))) +#else + if ((c & 0xc0) != 0x80) +#endif + state = ERR; + else { + target[2*size] = ((code & 3) << 6) + (c & 63); + target[2*size + 1] = ((code >> 2) & 255); + size++; + state = BASE; + } + break; + case THREE : + if ((c & 0xc0) != 0x80) + state = ERR; + else { + code = ((code & 15) << 6) + (c & 63); + state = TWO; + } + break; + case THREE2 : + if ((c & 0xe0) != 0xa0) + state = ERR; + else { + code = ((code & 15) << 6) + (c & 63); + state = TWO; + } + break; + case THREE3 : + if ((c & 0xe0) != 0x80) + state = ERR; + else { + code = ((code & 15) << 6) + (c & 63); + state = TWO; + } + break; + case FOUR : + if ((c & 0xc0) != 0x80) + state = ERR; + else { + code = (code << 6) + (c & 63); + state = FOUR2; + } + break; + case FOUR2 : + if ((c & 0xc0) != 0x80) + state = ERR; + else { + code = (code << 6) + (c & 63); + state = FOUR3; + } + break; + case FOUR3 : + if ((code > 0x43ff) + || (code < 0x400) + || ((c & 0xc0) != 0x80)) + state = ERR; + else { + target[2*size] = ((code - 0x400) >> 4) & 255; + target[2*size+1] = 0xd8 + (((code - 0x400) >> 12) & 3); + target[2*size+2] = ((code & 3) << 6) + (c & 63); + target[2*size+3] = 0xdc + ((code >> 2) & 3); + size += 2; + state = BASE; + } + break; + case ERR : + break; + } + } + if (state != BASE) + size = 0; + target[2*size] = 0; + target[2*size + 1] = 0; + return (size); +} + +unsigned int utf16len(const char *str) +{ + unsigned int len; + + len = 0; + while (str[2*len] || str[2*len+1]) len++; + return (len); +} + +#endif + +/* + * Print a file name + * on Windows it prints UTF-16LE names as UTF-8 + */ + +void printname(FILE *file, const char *name) +{ +#ifdef WIN32 + char utf8name[MAXFILENAME]; + + makeutf8(utf8name,name,utf16len(name)); + fprintf(file,"%s",utf8name); +#else + fprintf(file,"%s",name); +#endif +} + +/* + * Print the last error code + */ + +void printerror(FILE *file) +{ +#ifdef WIN32 + int err; + const char *txt; + + err = GetLastError(); + switch (err) { + case 5 : + txt = "Access to security descriptor was denied"; + break; + case 1307 : + txt = "This SID may not be assigned as the owner of this object"; + break; + case 1308 : + txt = "This SID may not be assigned as the group of this object"; + break; + case 1314 : + txt = "You do not have the privilege to change this SID"; + break; + default : + txt = (const char*)NULL; + break; + } + if (txt) + fprintf(file,"Error %d : %s\n",err,txt); + else + fprintf(file,"Error %d\n",err); +#else +#ifdef STSC + if (errno) fprintf(file,"Error code %d\n",errno); +#else + if (errno) fprintf(file,"Error code %d : %s\n",errno,strerror(errno)); +#endif +#endif +} + +/* + * Guess whether a security attribute is intended for a directory + * based on the presence of inheritable ACE + * (not 100% reliable) + */ + +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> 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"); + +} + +void showacl(const char *attr, int off, int isdir, int level) +{ + int i; + int cnt; + int size; + int x; + char marker; + + if (opt_b) + 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; (iacccnt; + 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 */ + +#if defined(WIN32) | defined(STSC) + +#else + +/* + * Relay to get usid as defined during mounting + */ + +const SID *relay_find_usid(const struct MAPPING *usermapping __attribute__((unused)), + uid_t uid, SID *defusid) +{ + return (ntfs_get_usid(ntfs_context,uid,(char*)defusid) ? + defusid : (SID*)NULL); +} + +/* + * Relay to get gsid as defined during mounting + */ + +const SID *relay_find_gsid(const struct MAPPING *groupmapping __attribute__((unused)), + uid_t gid, SID *defgsid) +{ + return (ntfs_get_usid(ntfs_context,gid,(char*)defgsid) ? + defgsid : (SID*)NULL); +} + +/* + * Relay to get uid as defined during mounting + */ + +uid_t relay_find_user(const struct MAPPING *mapping __attribute__((unused)), + const SID *usid) +{ + int uid; + + uid = ntfs_get_user(ntfs_context,(const char*)usid); + return (uid < 0 ? 0 : uid); +} + +/* + * Relay to get gid as defined during mounting + */ + +gid_t relay_find_group(const struct MAPPING *mapping __attribute__((unused)), + const SID *gsid) +{ + int gid; + + gid = ntfs_get_group(ntfs_context,(const char*)gsid); + return (gid < 0 ? 0 : gid); +} + +#endif + +#if defined(WIN32) | defined(STSC) + +/* + * Dummy get uid from user name, out of a Linux environment + */ + +struct passwd *getpwnam(const char *user) +{ + ntfs_log_error("Cannot interpret id \"%s\"", user); + ntfs_log_error("please use numeric uids in UserMapping file\n"); + return ((struct passwd*)NULL); +} + +/* + * Dummy get gid from group name, out of a Linux environment + */ + +struct group *getgrnam(const char *group) +{ + ntfs_log_error("Cannot interpret id \"%s\"", group); + ntfs_log_error("please use numeric gids in UserMapping file\n"); + return ((struct group*)NULL); +} + +#endif /* defined(WIN32) | defined(STSC) */ + +#if POSIXACLS + +struct POSIX_SECURITY *linux_permissions_posix(const char *attr, BOOL isdir) +{ + const SECURITY_DESCRIPTOR_RELATIVE *phead; +#if OWNERFROMACL + const SID *osid; +#endif + 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); +#if SELFTESTS & !USESTUBS + if (!opt_t && !ntfs_same_sid(usid,osid)) + printf("== Linux owner is different from Windows owner\n"); +#else + if (!ntfs_same_sid(usid,osid)) + printf("== Linux owner is different from Windows owner\n"); +#endif +#else + usid = (const SID*)&attr[le32_to_cpu(phead->owner)]; +#endif + 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 */ + +int linux_permissions(const char *attr, BOOL isdir) +{ + const SECURITY_DESCRIPTOR_RELATIVE *phead; +#if OWNERFROMACL + const SID *osid; +#endif + 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); +#if SELFTESTS & !USESTUBS + if (!opt_t && !ntfs_same_sid(usid,osid)) + printf("== Linux owner is different from Windows owner\n"); +#else + if (!ntfs_same_sid(usid,osid)) + printf("== Linux owner is different from Windows owner\n"); +#endif +#else + usid = (const SID*)&attr[le32_to_cpu(phead->owner)]; +#endif + perm = ntfs_build_permissions((const char*)attr, usid, gsid, isdir); + if (perm < 0) { + printf("** Failed to build permissions\n"); + errors++; + } + return (perm); +} + +uid_t linux_owner(const char *attr) +{ + const SECURITY_DESCRIPTOR_RELATIVE *phead; + const SID *usid; + uid_t uid; + + phead = (const SECURITY_DESCRIPTOR_RELATIVE*)attr; +#if OWNERFROMACL + usid = ntfs_acl_owner((const char*)attr); +#else + usid = (const SID*)&attr[le32_to_cpu(phead->owner)]; +#endif +#if defined(WIN32) | defined(STSC) + uid = ntfs_find_user(context.mapping[MAPUSERS],usid); +#else + if (mappingtype == MAPEXTERN) + uid = relay_find_user(context.mapping[MAPUSERS],usid); + else + uid = ntfs_find_user(context.mapping[MAPUSERS],usid); +#endif + return (uid); +} + +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)]; +#if defined(WIN32) | defined(STSC) + gid = ntfs_find_group(context.mapping[MAPGROUPS],gsid); +#else + if (mappingtype == MAPEXTERN) + gid = relay_find_group(context.mapping[MAPGROUPS],gsid); + else + gid = ntfs_find_group(context.mapping[MAPGROUPS],gsid); +#endif + return (gid); +} + +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; + } + } +} + +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) + */ + +int basicread(void *fileid, char *buf, size_t size, + off_t pos __attribute__((unused))) +{ + return (read(*(int*)fileid, buf, size)); +} + +#if POSIXACLS & SELFTESTS & !USESTUBS + +/* + * Read a dummy mapping file for tests + */ + +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 /* POSIXACLS & SELFTESTS & !USESTUBS */ + +/* + * 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) + */ + +int local_build_mapping(struct MAPPING *mapping[], const char *usermap_path) +{ +#ifdef WIN32 + char mapfile[sizeof(MAPDIR) + sizeof(MAPFILE) + 6]; +#else + char *mapfile; + char *p; +#endif + int fd; + struct MAPLIST *item; + struct MAPLIST *firstitem = (struct MAPLIST*)NULL; + struct MAPPING *usermapping; + struct MAPPING *groupmapping; + + /* be sure not to map anything until done */ + mapping[MAPUSERS] = (struct MAPPING*)NULL; + mapping[MAPGROUPS] = (struct MAPPING*)NULL; + + if (usermap_path) { +#ifdef WIN32 +/* TODO : check whether the device can store acls */ + strcpy(mapfile,"x:\\" MAPDIR "\\" MAPFILE); + if (((le16*)usermap_path)[1] == ':') + mapfile[0] = usermap_path[0]; + else + mapfile[0] = getdrive() + 'A' - 1; + fd = open(mapfile,O_RDONLY); +#else + 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 = 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 + if (fd > 0) { + firstitem = ntfs_read_mapping(basicread, (void*)&fd); + close(fd); + } + } else { +#if SELFTESTS & !USESTUBS + firstitem = ntfs_read_mapping(dummyread, (void*)NULL); +#endif + } + + 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; +#if USESTUBS + stdfree(firstitem); /* allocated within library */ +#else + free(firstitem); +#endif + firstitem = item; + } + } + if (mapping[MAPUSERS]) + mappingtype = MAPLOCAL; + return (!mapping[MAPUSERS]); +} + +/* + * Get an hexadecimal number (source with MSB first) + */ + +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 + */ + +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 + */ + +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)= '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 + */ + +void showhex(FILE *fd) +{ + char attr[MAXATTRSZ]; + char line[MAXLINE+1]; +#if POSIXACLS + struct POSIX_SECURITY *pxdesc; +#endif + int lth; + int first; + unsigned int pos; + unsigned char b; + u32 v; + int c; + int isdir; + int mode; + unsigned int off; + int i; + BOOL isdump; + BOOL done; + + b = 0; + 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 ((!isdump || !off) && pos && ntfs_valid_descr((char*)attr,pos)) { + 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); + 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 + 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 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)) { +#ifdef WIN32 + badattrib = !SetFileAttributesW((LPCWSTR)fullname, attrib); +#else + badattrib = !ntfs_set_file_attributes(ntfs_context, fullname, attrib); +#endif + 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) { + phead = (const SECURITY_DESCRIPTOR_RELATIVE*)curattr; +#ifdef WIN32 + /* SACL currently not set, need some special privilege */ + selection = OWNER_SECURITY_INFORMATION + | GROUP_SECURITY_INFORMATION + | DACL_SECURITY_INFORMATION; + bad = !SetFileSecurityW((LPCWSTR)fullname, + selection, (char*)curattr); + if (bad) + switch (GetLastError()) { + case 1307 : + case 1314 : +#if 0 +{ + /* try to change the DACL - no improvement */ +int try; +printf("Trying to set canall\n"); +showall((char*)canall,0); +try = SetFileSecurityW((LPCWSTR)fullname,DACL_SECURITY_INFORMATION,(char*)canall); +printf("try 0x%lx err %d\n",try,GetLastError()); +try = SetFileSecurityW((LPCWSTR)fullname, selection, (char*)curattr); +printf("try again 0x%lx err %d\n",try,GetLastError()); +} +#endif + printf("** Could not set owner of "); + printname(stdout,fullname); + printf(", retrying with no owner setting\n"); + warnings++; + bad = !SetFileSecurityW((LPCWSTR)fullname, + selection & ~OWNER_SECURITY_INFORMATION, (char*)curattr); + break; + default : + break; + } +#else + 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); +#endif + 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 + */ + +BOOL restore(FILE *fd) +{ + char attr[MAXATTRSZ]; + char line[MAXFILENAME+25]; + char fullname[MAXFILENAME+25]; + SECURITY_DESCRIPTOR_RELATIVE *phead; + int lth; + int first; + unsigned int pos; + unsigned int size; + unsigned char b; + int c; + int isdir; + int mode; + s32 key; + BOOL isdump; + unsigned int off; + u32 v; + u32 oldhash; + int i; + int count; + int attrib; + BOOL withattr; + BOOL done; + + b = 0; + pos = 0; + size = 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 */ + 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); + printf("Interpreted Unix mode 0%03o\n",mode); + } + size = pos; + 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 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) { +#ifdef WIN32 + if (!strncmp(line,"File ",5)) + makeutf16(fullname,&line[5]); + else + makeutf16(fullname,&line[10]); +#else + if (!strncmp(line,"File ",5)) + strcpy(fullname,&line[5]); + else + strcpy(fullname,&line[10]); +#endif + } + } + } while (!done); + printf("%d ACLs have been applied\n",count); + return (FALSE); +} + +/* + * Open the security API in rw mode for an ACL restoration + */ + +#ifdef WIN32 +#else +BOOL dorestore(const char *volume, FILE *fd) +{ + BOOL err; + + if (!getuid()) { + if (open_security_api()) { + if (open_volume(volume,MS_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); +} +#endif /* WIN32 */ + +#if POSIXACLS & SELFTESTS & !USESTUBS + +/* + * 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 + */ +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); +} + +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(AUTH1), cpu_to_le32(AUTH2), + cpu_to_le32(AUTH3), 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(AUTH1), cpu_to_le32(AUTH2), + cpu_to_le32(AUTH3), 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; + } +} + +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 & SELFTESTS & !USESTUBS */ + +#if SELFTESTS & !USESTUBS + +/* + * 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; itype = (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 + */ + +void check_samples() +{ + 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 + 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(4243175381), + 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(4243175381), + 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(AUTH1), cpu_to_le32(AUTH2), + cpu_to_le32(AUTH3), 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(AUTH1), cpu_to_le32(AUTH2), + cpu_to_le32(AUTH3), cpu_to_le32(513) + } ; + +#if POSIXACLS + struct { + struct POSIX_SECURITY head; + struct POSIX_ACE ace[4]; + } sampletry1 = + { + { 0645, 4, 0, 4, 0x35, + { 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, + { 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, + { 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, + { 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, + { 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, + { 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\n",txsample); + showposix(pxsample); + showall(descr,0); + 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<=8; 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); + expectacc = expect = 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 = 0700; + 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; + 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); +exit(1); + } + free(pxdesc); + } +#endif + } + free(descr); + } + } +} + + +/* + * Check whether any basic permission setting is interpreted + * back exactly as set + */ + +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, + 25416, 29512 + } ; + u32 expecthash[] = { + 0x8f80865b, 0x7bc7960, + 0x8fd9ecfe, 0xddd4db0, + 0xa8b07400, 0xa189c20, + 0xc5689a00, 0xb6c09000, + 0x94bfb419, 0xa4311791 + } ; +#if POSIXACLS + struct POSIX_SECURITY *pxdesc; + char *pxattr; + u32 pxcount; + u32 pxacecount; + u32 pxglobhash; +#endif + + count = 0; + acecount = 0; + globhash = 0; +#if POSIXACLS + pxcount = 0; + pxacecount = 0; + pxglobhash = 0; +#endif + 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 + 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 + 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 : 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++; + } +#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 : expected ACE count %lu\n", + (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 + */ + +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 */ + } err; + u32 expectcnt[] = { +#ifdef STSC + 32400, 34992, + 25920, 28512, + 25920, 28512, + 25920, 28512, + 26460, 29052, + 0, 0, + 0, 0, + 0, 0, + 24516, 27108, + 20736, 23328, + 20736, 23328, + 20736, 23328, + 21060, 23652, +#else + 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, +#endif + 0, 0, + 0, 0, + 0, 0, + 16368, 18672, + 0, 0, + 13824, 0, + 0, 0, + 14640, 0 + } ; + u32 expecthash[] = { +#ifdef STSC + 0xf9f82115, 0x40666647, + 0xde826d30, 0xa181b660, + 0x952d4500, 0x8ac49450, + 0xf80acee0, 0xbd9ec6c0, + 0xfe09b868, 0xde24e84d, + 0, 0, + 0, 0, + 0, 0, + 0x2381438d, 0x3ab42dc6, + 0x7cccf6f8, 0x108ad430, + 0x5e448840, 0x83ab6c40, + 0x9b037100, 0x8f7c3b40, + 0x04a359dc, 0xa4619609, +#else + 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, +#endif + 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); +#ifdef STSC + minmsk = (kind & 32 ? 0 : 3); + maxmsk = (kind & 32 ? 7 : 3); +#else + minmsk = 0; + maxmsk = 7; +#endif + 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; + } + + err = ERRNO; + 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++; +exit(1); + } + } 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 */ + +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(AUTH1), cpu_to_le32(AUTH2), + cpu_to_le32(AUTH3), 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(AUTH1), cpu_to_le32(AUTH2), + cpu_to_le32(AUTH3), cpu_to_le32(513) + } ; +#ifdef STSC + unsigned char kindlist[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 16, 17, 18, 20, 22, 24, + 32, 33, 36, 40 } ; +#else + 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 } ; +#endif + unsigned int k; + int kind; + const SID *owner; + const SID *group; + BOOL isdir; + +#if POSIXACLS + local_build_mapping(context.mapping, (const char*)NULL); +#endif + /* first check samples */ + mappingtype = MAPDUMMY; + check_samples(); +if (errors) exit(1); + /* + * 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= 10) + printf("** too many errors, test aborted\n"); +} +#endif /* SELFTESTS & !USESTUBS */ + +#ifdef WIN32 + +/* + * Get the security descriptor of a file (Windows version) + */ + +unsigned int getfull(char *attr, const char *fullname) +{ + char part[MAXATTRSZ]; + BIGSID ownsid; + int xowner; + int ownersz; + u16 ownerfl; + ULONG attrsz; + ULONG partsz; + BOOL overflow; + + attrsz = 0; + partsz = 0; + overflow = FALSE; + if (GetFileSecurityW((LPCWSTR)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 (!GetFileSecurityW((LPCWSTR)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 (GetFileSecurityW((LPCWSTR)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 (opt_b) + 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 (GetFileSecurityW((LPCWSTR)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++; + } + set2l(&attr[2],get2l(attr,2) + | const_le16_to_cpu(SE_SELF_RELATIVE)); + if (overflow) { + printf("** Descriptor was too long (> %d)\n",MAXATTRSZ); + warnings++; + attrsz = 0; + } else + if (!ntfs_valid_descr((char*)attr,attrsz)) { + printf("** Descriptor for "); + printname(stdout,fullname); + printf(" is not valid\n"); + errors++; + attrsz = 0; + } + + } else { + printf("** Could not get owner of "); + printname(stdout,fullname); + printf(", partsz %d\n",partsz); + printerror(stdout); + warnings++; + attrsz = 0; + } + return (attrsz); +} + +/* + * Update a security descriptor (Windows version) + */ + +BOOL updatefull(const char *name, DWORD flags, char *attr) +{ + BOOL bad; + + bad = !SetFileSecurityW((LPCWSTR)name, flags, attr); + if (bad + && (flags & OWNER_SECURITY_INFORMATION) + && (GetLastError() == 1307)) { + printf("** Could not set owner of "); + printname(stdout,name); + printf(", retrying with no owner setting\n"); + warnings++; + bad = !SetFileSecurityW((LPCWSTR)name, + flags & ~OWNER_SECURITY_INFORMATION, (char*)attr); + } + if (bad) { + printf("** Could not change attributes of "); + printname(stdout,name); + printf("\n"); + printerror(stdout); + errors++; + } + return (!bad); +} + +#else + +/* + * Get the security descriptor of a file (Linux version) + */ + +unsigned int getfull(char *attr, const char *fullname) +{ + 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 (opt_b) + 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 (Linux version) + */ + +BOOL updatefull(const char *name, DWORD flags, char *attr) +{ + BOOL ok; + + ok = !ntfs_set_file_security(ntfs_context, name, flags, attr); + if (ok) { + printf("** Could not change attributes of %s\n",name); + printerror(stdout); + errors++; + } + return (ok); +} + + +#endif + +#if POSIXACLS + +/* + * Set all the parameters associated to a file + */ + +BOOL setfull_posix(const char *fullname, const struct POSIX_SECURITY *pxdesc, + BOOL isdir) +{ + 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 + + 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 + usid = (const SID*)&attr[le32_to_cpu(phead->owner)]; +#endif + 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); + } + +#ifdef WIN32 + /* + * avoid getting a set owner error on Windows + * owner should not be changed anyway + */ + if (!updatefull(fullname, + DACL_SECURITY_INFORMATION + | GROUP_SECURITY_INFORMATION + | OWNER_SECURITY_INFORMATION, + newattr)) +#else + if (!updatefull(fullname, + DACL_SECURITY_INFORMATION + | GROUP_SECURITY_INFORMATION + | OWNER_SECURITY_INFORMATION, + newattr)) +#endif + 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); +} + +#endif + +BOOL setfull(const char *fullname, int mode, BOOL isdir) +{ + 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 + + printf("%s ",(isdir ? "Directory" : "File")); + printname(stdout,fullname); + printf(" mode 0%03o\n",mode); + attrsz = getfull(attr, fullname); + 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 + usid = (const SID*)&attr[le32_to_cpu(phead->owner)]; +#endif + 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); + } + +#ifdef WIN32 + /* + * avoid getting a set owner error on Windows + * owner should not be changed anyway + */ + if (!updatefull(fullname, + DACL_SECURITY_INFORMATION + | GROUP_SECURITY_INFORMATION + | OWNER_SECURITY_INFORMATION, + newattr)) +#else + if (!updatefull(fullname, + DACL_SECURITY_INFORMATION + | GROUP_SECURITY_INFORMATION + | OWNER_SECURITY_INFORMATION, + newattr)) +#endif + err = TRUE; + free(newattr); + } + + } else + err = TRUE; + return (err); +} + +#ifdef WIN32 + +/* + * Check whether a directory with reparse data is a junction + * or a symbolic link + */ + +BOOL islink(const char *filename) +{ + WIN32_FIND_DATAW found; + HANDLE search; + BOOL link; + + link = FALSE; + search = FindFirstFileW((LPCWSTR)filename, &found); + if (search != INVALID_HANDLE_VALUE) { + link = (found.dwReserved0 == IO_REPARSE_TAG_MOUNT_POINT) + || (found.dwReserved0 == IO_REPARSE_TAG_SYMLINK); + FindClose(search); + } + return (link); +} + +#if POSIXACLS +BOOL iterate(RECURSE call, const char *fullname, const struct POSIX_SECURITY *pxdesc) +#else +BOOL iterate(RECURSE call, const char *fullname, mode_t mode) +#endif +{ + WIN32_FIND_DATAW found; + HANDLE search; + BOOL err; + unsigned int len; + char *filter; + char *inner; + + err = FALSE; + filter = (char*)malloc(MAXFILENAME); + inner = (char*)malloc(MAXFILENAME); + if (filter && inner) { + len = utf16len(fullname); + memcpy(filter, fullname, 2*len); + makeutf16(&filter[2*len],"\\*.*"); + search = FindFirstFileW((LPCWSTR)filter, &found); + if (search != INVALID_HANDLE_VALUE) { + do { + if (found.cFileName[0] != UNICODE('.')) { + memcpy(inner, fullname, 2*len); + inner[2*len] = '\\'; + inner[2*len+1] = 0; + memcpy(&inner[2*len+2],found.cFileName, + 2*utf16len((char*)found.cFileName)+2); + if (opt_v) + if (opt_b) + printf("#\n#\n"); + else + printf("\n\n"); + switch (call) { + case RECSHOW : + if (recurseshow(inner)) + err = TRUE; + break; +#if POSIXACLS + case RECSETPOSIX : + if (recurseset_posix(inner,pxdesc)) + err = TRUE; + break; +#else + case RECSET : + if (recurseset(inner,mode)) + err = TRUE; + break; +#endif + default : + err = TRUE; + } + } + } while (FindNextFileW(search, &found)); + FindClose(search); + } + free(filter); + free(inner); + } else { + printf("** Cannot process deeper : not enough memory\n"); + errors++; + err = TRUE; + } + return (err); +} + + + +/* + * Display all the parameters associated to a file (Windows version) + */ + +void showfull(const char *fullname, BOOL isdir) +{ + char attr[MAXATTRSZ]; +#if POSIXACLS + struct POSIX_SECURITY *pxdesc; +#endif + ULONG attrsz; + int mode; + uid_t uid; + gid_t gid; + int attrib; + int level; + + 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 hash code */ + + attrsz = getfull(attr, fullname); + if (attrsz) { + if (opt_v || opt_b) { + hexdump(attr,attrsz,8); + printf("Computed hash : 0x%08lx\n", + (unsigned long)hash((le32*)attr,attrsz)); + } + if (opt_v && opt_b) { + printf("# %s ",(isdir ? "Directory" : "File")); + printname(stdout,fullname); + printf(" hash 0x%lx\n", + (unsigned long)hash((le32*)attr,attrsz)); + } + attrib = GetFileAttributesW((LPCWSTR)fullname); + if (attrib == INVALID_FILE_ATTRIBUTES) { + printf("** Could not get file attrib\n"); + errors++; + } else + printf("Windows attrib : 0x%x\n",attrib); + if (ntfs_valid_descr(attr,attrsz)) { +#if POSIXACLS + pxdesc = linux_permissions_posix(attr,isdir); + if (pxdesc) + mode = pxdesc->mode; + else + mode = 0; +#else + mode = linux_permissions(attr,isdir); +#endif + if (opt_v >= 2) { + level = (opt_b ? 4 : 0); + showheader(attr,level); + showusid(attr,level); + showgsid(attr,level); + showdacl(attr,isdir,level); + showsacl(attr,isdir,level); + } + uid = linux_owner(attr); + gid = linux_group(attr); + if (opt_b) { + printf("# Interpreted Unix owner %d, group %d, mode 0%03o\n", + (int)uid,(int)gid,mode); + } else { + printf("Interpreted Unix owner %d, group %d, mode 0%03o\n", + (int)uid,(int)gid,mode); + } +#if POSIXACLS + if (pxdesc) { + if (!opt_b + && (pxdesc->defcnt + || (pxdesc->tagsset + & (POSIX_ACL_USER + | POSIX_ACL_GROUP + | POSIX_ACL_MASK)))) + showposix(pxdesc); + free(pxdesc); + } +#endif + } else + if (opt_b) + printf("# Descriptor fails sanity check\n"); + else + printf("Descriptor fails sanity check\n"); + } +} + +BOOL recurseshow(const char *fullname) +{ + int attrib; + int err; + BOOL isdir; + + err = FALSE; + attrib = GetFileAttributesW((LPCWSTR)fullname); + if (attrib != INVALID_FILE_ATTRIBUTES) { + isdir = (attrib & FILE_ATTRIBUTE_DIRECTORY) != 0; + showfull(fullname,isdir); + if (isdir + && !((attrib & FILE_ATTRIBUTE_REPARSE_POINT) + && islink(fullname))) { +#if POSIXACLS + err = iterate(RECSHOW, fullname, (struct POSIX_SECURITY*)NULL); +#else + err = iterate(RECSHOW, fullname, 0); +#endif + } + } else { + printf("** Could not access "); + printname(stdout,fullname); + printf("\n"); + printerror(stdout); + errors++; + err = TRUE; + } + return (err); +} + + +BOOL singleshow(const char *fullname) +{ + int attrib; + int err; + BOOL isdir; + + err = FALSE; + attrib = GetFileAttributesW((LPCWSTR)fullname); + if (attrib != INVALID_FILE_ATTRIBUTES) { + isdir = (attrib & FILE_ATTRIBUTE_DIRECTORY) != 0; + showfull(fullname,isdir); + } else { + printf("** Could not access "); + printname(stdout,fullname); + printf("\n"); + printerror(stdout); + errors++; + err = TRUE; + } + return (err); +} + +#if POSIXACLS + +BOOL recurseset_posix(const char *fullname, const struct POSIX_SECURITY *pxdesc) +{ + int attrib; + int err; + BOOL isdir; + + err = FALSE; + attrib = GetFileAttributesW((LPCWSTR)fullname); + if (attrib != INVALID_FILE_ATTRIBUTES) { + isdir = (attrib & FILE_ATTRIBUTE_DIRECTORY) != 0; + err = !setfull_posix(fullname,pxdesc,isdir); + if (err) { + printf("** Failed to update "); + printname(stdout,fullname); + printf("\n"); + errors++; + } else + if (isdir + && !((attrib & FILE_ATTRIBUTE_REPARSE_POINT) + && islink(fullname))) + iterate(RECSETPOSIX, fullname, pxdesc); + } else { + err = GetLastError(); + printf("** Could not access "); + printname(stdout,fullname); + printf("\n"); + printerror(stdout); + err = TRUE; + errors++; + } + return (err); +} + +#else + +BOOL recurseset(const char *fullname, int mode) +{ + int attrib; + int err; + BOOL isdir; + + err = FALSE; + attrib = GetFileAttributesW((LPCWSTR)fullname); + if (attrib != INVALID_FILE_ATTRIBUTES) { + isdir = (attrib & FILE_ATTRIBUTE_DIRECTORY) != 0; + setfull(fullname,mode,isdir); + if (isdir + && !((attrib & FILE_ATTRIBUTE_REPARSE_POINT) + && islink(fullname))) + iterate(RECSETPOSIX, fullname, mode); + } else { + err = GetLastError(); + printf("** Could not access "); + printname(stdout,fullname); + printf("\n"); + printerror(stdout); + err = TRUE; + errors++; + } + return (err); +} + +#endif + +#if POSIXACLS + +BOOL singleset_posix(const char *path, const struct POSIX_SECURITY *pxdesc) +{ + BOOL isdir; + BOOL err; + int attrib; + + err = FALSE; + attrib = GetFileAttributesW((LPCWSTR)path); + if (attrib != INVALID_FILE_ATTRIBUTES) { + isdir = (attrib & FILE_ATTRIBUTE_DIRECTORY); + err = !setfull_posix(path,pxdesc,isdir); + if (err) { + printf("** Failed to update "); + printname(stdout,path); + printf("\n"); + errors++; + } + } else { + printf("** Could not access "); + printname(stdout,path); + printf("\n"); + printerror(stdout); + errors++; + err = TRUE; + } + return (!err); +} + +#endif + +BOOL singleset(const char *path, int mode) +{ + BOOL isdir; + BOOL err; + int attrib; + + err = FALSE; + attrib = GetFileAttributesW((LPCWSTR)path); + if (attrib != INVALID_FILE_ATTRIBUTES) { + isdir = (attrib & FILE_ATTRIBUTE_DIRECTORY); + setfull(path,mode,isdir); + } else { + printf("** Could not access "); + printname(stdout,path); + printf("\n"); + printerror(stdout); + errors++; + err = TRUE; + } + return (!err); +} + +#else + +/* + * Display all the parameters associated to a file (Linux version) + */ + +void showfull(const char *fullname, BOOL isdir) +{ + char attr[MAXATTRSZ]; + char part[MAXATTRSZ]; +#if POSIXACLS + struct POSIX_SECURITY *pxdesc; +#endif + 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 || opt_b) + printf("%s %s\n",(isdir ? "Directory" : "File"),fullname); + + /* 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 && !opt_b) + || !securdata[securindex >> SECBLKSZ] + || !securdata[securindex >> SECBLKSZ][securindex & ((1 << SECBLKSZ) - 1)].filecount))) + { + if (opt_v || opt_b) { + 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 %s",(isdir ? "Directory" : "File"),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 (opt_v && (opt_a || opt_b) && psecurdata) { + newattr = (char*)malloc(attrsz); + printf("# %s %s hash 0x%lx\n",(isdir ? "Directory" : "File"), + fullname, + (unsigned long)hash((le32*)attr,attrsz)); + if (newattr) { + memcpy(newattr,attr,attrsz); + psecurdata->attr = newattr; + } + } + if ((opt_v || opt_b) + && ((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 + mode = linux_permissions(attr,isdir); +#endif + attrib = ntfs_get_file_attributes(ntfs_context,fullname); + if (opt_v >= 2) { + level = (opt_b ? 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 (opt_b) { + printf("# Interpreted Unix owner %d, group %d, mode 0%03o\n", + (int)uid,(int)gid,mode); + } else { + printf("Interpreted Unix owner %d, group %d, mode 0%03o\n", + (int)uid,(int)gid,mode); + } +#if POSIXACLS + if (pxdesc) { + if (!opt_b + && (pxdesc->defcnt + || (pxdesc->tagsset + & (POSIX_ACL_USER + | POSIX_ACL_GROUP + | POSIX_ACL_MASK)))) + showposix(pxdesc); + free(pxdesc); + } +#endif + if ((opt_r || opt_b) && (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 (opt_b || opt_r) { + if (!opt_b && !opt_v) + printf("%s %s\n",(isdir ? "Directory" : "File"),fullname); + 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 %s",(isdir ? "Directory" : "File"),fullname); + printf(" : key 0x%x\n",securindex); + } + if ((opt_a || opt_b) && opt_v + && psecurdata && psecurdata->attr) { + printf("# %s %s hash 0x%lx\n",(isdir ? "Directory" : "File"), + fullname, + (unsigned long)hash((le32*)psecurdata->attr, + ntfs_attr_size(psecurdata->attr))); + } + } + } else { + if (!opt_v && !opt_b) + printf("%s %s",(isdir ? "Directory" : "File"),fullname); + printf(" (Failed)\n"); + printf("** Could not get security data of %s, partsz %d\n", + fullname,partsz); + printerror(stdout); + errors++; + } +} + +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 (opt_b) + 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 (opt_b) + printf("#\n#\n"); + else + printf("\n\n"); + } + } else { + printf("** Could not access %s\n",path); + printerror(stdout); + errors++; + err = TRUE; + } + return (!err); +} + + +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); +} + +/* + * Display all the parameters associated to a mounted file + */ + +void showmounted(const char *fullname) +{ +#ifdef HAVE_SETXATTR + + char attr[MAXATTRSZ]; + struct stat st; +#if POSIXACLS + struct POSIX_SECURITY *pxdesc; +#endif + BOOL mapped; + int attrsz; + int mode; + uid_t uid; + gid_t gid; + u32 attrib; + int level; + BOOL isdir; + + if (!stat(fullname,&st)) { + isdir = S_ISDIR(st.st_mode); + printf("%s ",(isdir ? "Directory" : "File")); + printname(stdout,fullname); + printf("\n"); + + attrsz = 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 (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 + mode = linux_permissions(attr,isdir); +#endif + if (opt_v >= 2) { + level = (opt_b ? 4 : 0); + showheader(attr,level); + showusid(attr,level); + showgsid(attr,level); + showdacl(attr,isdir,level); + showsacl(attr,isdir,level); + } + 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); +#if USESTUBS + stdfree(pxdesc); /* allocated within library */ +#else + free(pxdesc); +#endif + } + if (mapped) + ntfs_free_mapping(context.mapping); +#endif + } else + printf("Descriptor fails sanity check\n"); + } else { + printf("** Could not get the NTFS ACL, check whether file is on NTFS\n"); + errors++; + } + } else + printf("%s not found\n",fullname); +#endif +} + +#if POSIXACLS + +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 (opt_b) + 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 + +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 (opt_b) + 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 + +#if POSIXACLS + +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); +} + +#endif + +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); +} + +int callback(struct CALLBACK *dircontext, char *ntfsname, + int length, int type, + long long pos __attribute__((unused)), u64 mft_ref __attribute__((unused)), + unsigned int dt_type __attribute__((unused))) +{ + struct LINK *linkage; + char *name; + int newlth; + int size; + + size = utf8size(ntfsname,length); + if (dircontext + && (type != 2) /* 2 : dos name (8+3) */ + && (size > 0) /* chars convertible to utf8 */ + && ((length > 2) + || (ntfsname[0] != '.') + || (ntfsname[1] != '\0') + || ((ntfsname[2] || ntfsname[3]) + && ((ntfsname[2] != '.') || (ntfsname[3] != '\0'))))) { + 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); +} + +#endif + +#ifdef WIN32 + +/* + * Backup security descriptors in a directory tree (Windows mode) + */ + +BOOL backup(const char *root) +{ + BOOL err; + time_t now; + const char *txtime; + + now = time((time_t*)NULL); + txtime = ctime(&now); + printf("#\n# Recursive ACL collection on %s#\n",txtime); + err = recurseshow(root); + return (err); +} + +#else + +/* + * Backup security descriptors in a directory tree (Linux mode) + */ + +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,MS_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); +} + +#endif + +#ifdef WIN32 + +/* + * List security descriptors in a directory tree (Windows mode) + */ + +BOOL listfiles(const char *root) +{ + BOOL err; + + if (opt_r) { + printf("\nRecursive file check\n"); + err = recurseshow(root); + } else + err = singleshow(root); + return (err); +} + +#else + +/* + * List security descriptors in a directory tree (Linux mode) + */ + +BOOL listfiles(const char *volume, const char *root) +{ + BOOL err; + int i,j; + int count; + + if (!getuid() && open_security_api()) { + if (open_volume(volume,MS_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 { + 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 + +#ifndef WIN32 + +/* + * Check whether a SDS entry is valid + */ + +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 + */ + +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 (Linux only) + */ + +int audit_sds(BOOL second) +{ + 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); + 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 + */ + +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 + */ + +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 (Linux only) + */ + +int audit_sii() +{ + 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,(void*)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 + */ + +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 + */ + +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 (Linux only) + */ + +int audit_sdh() +{ + 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,(void*)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 + */ + +void audit_summary() +{ + 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 (Linux only) + */ + +BOOL audit(const char *volume) +{ + BOOL err; + + err = FALSE; + if (!getuid() && open_security_api()) { + if (open_volume(volume,MS_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); +} + +#endif + +#if POSIXACLS + +/* + * Encode a Posix ACL string + * [d:]{ugmo}:uid[:perms],... + */ + +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; iace[i].tag; + switch (acl->ace[i].tag) { + case POSIX_ACL_USER_OBJ : + mode |= acl->ace[i].perms << 6; + break; + case POSIX_ACL_GROUP_OBJ : + mode |= acl->ace[i].perms << 3; + break; + case POSIX_ACL_OTHER : + mode |= acl->ace[i].perms; + 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 */ + + +int getoptions(int argc, char *argv[]) +{ + int xarg; + int narg; + const char *parg; + BOOL err; + + opt_a = FALSE; + opt_b = FALSE; + opt_e = FALSE; + opt_h = FALSE; +#if FORCEMASK + opt_m = FALSE; +#endif + opt_r = FALSE; + opt_s = FALSE; +#if SELFTESTS & !USESTUBS + opt_t = FALSE; +#endif + opt_v = 0; + xarg = 1; + err = FALSE; + while ((xarg < argc) && (argv[xarg][0] == '-')) { + parg = argv[xarg++]; + while (*++parg) + switch (*parg) + { +#ifndef WIN32 + case 'a' : + opt_a = TRUE; + break; +#endif + case 'b' : + opt_b = TRUE; + break; + case 'e' : + opt_e = TRUE; + break; + case 'h' : + opt_h = TRUE; + break; +#if FORCEMASK + case 'm' : + opt_m = TRUE; + break; +#endif + case 'r' : + case 'R' : + opt_r = TRUE; + break; + case 's' : + opt_s = TRUE; + break; +#if SELFTESTS & !USESTUBS + case 't' : + opt_t = TRUE; + break; +#endif + case 'v' : + opt_v++; + break; + default : + fprintf(stderr,"Invalid option -%c\n",*parg); + err = TRUE; + } + } + narg = argc - xarg; +#ifdef WIN32 + if ( ((opt_h || opt_s) && (narg > 1)) + || ((opt_r || opt_b) && ((narg < 1) || (narg > 2))) +#if SELFTESTS & !USESTUBS + || (opt_t && (narg > 0)) +#endif + || (opt_e && !opt_s) + || (!opt_h && !opt_r && !opt_b && !opt_s +#if SELFTESTS & !USESTUBS + && !opt_t +#endif + && ((narg < 1) || (narg > 2)))) + + err = TRUE; + if (err) { + xarg = 0; + fprintf(stderr,"Usage:\n"); +#if SELFTESTS & !USESTUBS + fprintf(stderr," secaudit -t\n"); + fprintf(stderr," run self-tests\n"); +#endif + fprintf(stderr," secaudit -h [file]\n"); + fprintf(stderr," display security descriptors within file\n"); + fprintf(stderr," secaudit [-v] file\n"); + fprintf(stderr," display the security parameters of file\n"); + fprintf(stderr," secaudit -r[v] directory\n"); + fprintf(stderr," display the security parameters of files in directory\n"); + fprintf(stderr," secaudit -b[v] directory\n"); + fprintf(stderr," backup the security parameters of files in directory\n"); + fprintf(stderr," secaudit -s[ev] [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," secaudit perms file\n"); + fprintf(stderr," set the security parameters of file to perms\n"); + fprintf(stderr," secaudit -r[v] perms directory\n"); + fprintf(stderr," set the security parameters of files in directory to perms\n"); +#if POSIXACLS + fprintf(stderr," Note: perms can be an octal mode or a Posix ACL description\n"); +#else + fprintf(stderr," Note: perms is an octal mode\n"); +#endif + fprintf(stderr," -v is for verbose, -vv for very verbose\n"); + } +#else + if ( (opt_h && (narg > 1)) + || (opt_a && (narg != 1)) + || ((opt_r || opt_b || opt_s) && ((narg < 1) || (narg > 3))) +#if SELFTESTS & !USESTUBS + || (opt_t && (narg > 0)) +#endif + || (opt_e && !opt_s) + || (!opt_h && !opt_a && !opt_r && !opt_b && !opt_s +#if SELFTESTS & !USESTUBS + && !opt_t +#endif +#ifdef HAVE_SETXATTR + && ((narg < 1) || (narg > 3)))) +#else + && ((narg < 2) || (narg > 3)))) +#endif + err = TRUE; + if (err) { + xarg = 0; + fprintf(stderr,"Usage:\n"); +#if SELFTESTS & !USESTUBS + fprintf(stderr," secaudit -t\n"); + fprintf(stderr," run self-tests\n"); +#endif + fprintf(stderr," secaudit -h [file]\n"); + fprintf(stderr," display security descriptors within file\n"); + fprintf(stderr," secaudit -a[rv] volume\n"); + fprintf(stderr," audit the volume\n"); + fprintf(stderr," secaudit [-v] volume file\n"); + fprintf(stderr," display the security parameters of file\n"); + fprintf(stderr," secaudit -r[v] volume directory\n"); + fprintf(stderr," display the security parameters of files in directory\n"); + fprintf(stderr," secaudit -b[v] volume directory\n"); + fprintf(stderr," backup the security parameters of files in directory\n"); + fprintf(stderr," secaudit -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," secaudit volume perms file\n"); + fprintf(stderr," set the security parameters of file to perms\n"); + fprintf(stderr," secaudit -r[v] volume perms directory\n"); + fprintf(stderr," set the security parameters of files in directory to perms\n"); + fprintf(stderr," special case, does not require being root :\n"); + fprintf(stderr," secaudit [-v] mounted-file\n"); + fprintf(stderr," display the security parameters of a mounted file\n"); +#if POSIXACLS + fprintf(stderr," Note: perms can be an octal mode or a Posix ACL description\n"); +#else + fprintf(stderr," Note: perms is an octal mode\n"); +#endif + fprintf(stderr," -v is for verbose, -vv for very verbose\n"); + } +#endif + if ((sizeof(SID) != 12) && !err) { + fprintf(stderr,"Possible alignment problem, check your compiler options\n"); + err = TRUE; + xarg = 0; + } + return (xarg); +} + +/* + * Memory allocation with checks + */ + +#undef malloc +#undef calloc +#undef free +#undef isalloc + +void dumpalloc(const char *txt) +{ + struct CHKALLOC *q; + + if (firstalloc) { + printf("alloc table at %s\n",txt); + for (q=firstalloc; q; q=q->next) + printf("%08lx : %u bytes at %08lx allocated at %s line %d\n", + (long)q,(unsigned int)q->size, + (long)q->alloc,q->file,q->line); + } +} + +void *chkmalloc(size_t size, const char *file, int line) +{ + void *p; + struct CHKALLOC *q; + + p = (void*)malloc(size+1); + if (p) { + ((unsigned char*)p)[size] = 0xaa; + q = (struct CHKALLOC*)malloc(sizeof(struct CHKALLOC)); + if (q) { + q->next = firstalloc; + q->alloc = p; + q->size = size; + q->file = file; + q->line = line; + firstalloc = q; + } + } + return (p); +} + +void *chkcalloc(size_t cnt, size_t size, const char *file, int line) +{ + return (chkmalloc(cnt*size,file,line)); +} + +void chkfree(void *p, const char *file, int line) +{ + struct CHKALLOC *q; + struct CHKALLOC *r; + + if (p) { + if (firstalloc && (firstalloc->alloc == p)) { + r = firstalloc; + firstalloc = firstalloc->next; + } else { + q = firstalloc; + if (q) + while (q->next && (q->next->alloc != p)) + q = q->next; + if (q && q->next) { + r = q->next; + q->next = r->next; + } else { + r = (struct CHKALLOC*)NULL; + printf("** freeing unallocated memory in %s line %d\n",file,line); + if (!isatty(1)) + fprintf(stderr,"** freeing unallocated memory in %s line %d\n",file,line); + } + } + if (r) { + if (((unsigned char*)p)[r->size] != 0xaa) { + printf("** memory corruption, alloc in %s line %d release in %s %d\n", + r->file,r->line,file,line); + if (!isatty(1)) + fprintf(stderr,"** memory corruption, alloc in %s line %d release in %s %d\n", + r->file,r->line,file,line); + } + memset(p,0xaa,r->size); + free(r); + free(p); + } + } +} + +void stdfree(void *p) +{ + free(p); +} + +BOOL chkisalloc(void *p, const char *file, int line) +{ + struct CHKALLOC *q; + + if (p) { + q = firstalloc; + while (q && (q->alloc != p)) + q = q->next; + } else + q = (struct CHKALLOC*)NULL; + if (!p || !q) { + printf("error in %s %d : 0x%lx not allocated\n",file,line,(long)p); + } + return (p && q); +} + + + + +#ifdef WIN32 + +/* + * Windows version + */ + +main(argc,argv) +int argc; +char *argv[]; +{ + FILE *fd; + int xarg; + int mode; + unsigned int size; + BOOL cmderr; + char *filename; + const char *p; + int i; +#if POSIXACLS + struct POSIX_SECURITY *pxdesc; +#endif + + printf("%s\n",BANNER); + cmderr = FALSE; + errors = 0; + warnings = 0; + xarg = getoptions(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 + firstalloc = (struct CHKALLOC*)NULL; + mappingtype = MAPNONE; + switch (argc - xarg) { + case 0 : + if (opt_h) + showhex(stdin); + else + if (opt_s) + restore(stdin); +#if SELFTESTS & !USESTUBS + if (opt_t) + selftests(); +#endif + break; + case 1 : + if (opt_h || opt_s) { + fd = fopen(argv[xarg],"r"); + if (fd) { + if (opt_h) + showhex(fd); + else + restore(fd); + fclose(fd); + } else { + fprintf(stderr,"Could not open %s\n",argv[xarg]); + cmderr = TRUE; + } + } else { + size = utf16size(argv[xarg]); + if (size) { + filename = (char*)malloc(2*size + 2); + if (filename) { + makeutf16(filename,argv[xarg]); +#if POSIXACLS + if (local_build_mapping(context.mapping,filename)) { + printf("*** Could not get user mapping data\n"); + warnings++; + } +#endif + if (opt_b) + cmderr = backup(filename); + else { + if (opt_r) + cmderr = listfiles(filename); + else + cmderr = singleshow(filename); + } +#if POSIXACLS + ntfs_free_mapping(context.mapping); +#endif + free(filename); + } else { + fprintf(stderr,"No more memory\n"); + cmderr = TRUE; + } + } else { + fprintf(stderr,"Bad UTF-8 name \"%s\"\n",argv[xarg]); + cmderr = TRUE; + } + } + break; + case 2 : + mode = 0; + p = argv[xarg]; +#if POSIXACLS + pxdesc = encode_posix_acl(p); + if (pxdesc) { + size = utf16size(argv[xarg + 1]); + if (size) { + filename = (char*)malloc(2*size + 2); + if (filename) { + makeutf16(filename,argv[xarg + 1]); + if (local_build_mapping(context.mapping,filename)) { + printf("*** Could not get user mapping data\n"); + warnings++; + } + if (opt_r) { + for (i=0; i<(MAXSECURID + (1 << SECBLKSZ) - 1)/(1 << SECBLKSZ); i++) + securdata[i] = (struct SECURITY_DATA*)NULL; + recurseset_posix(filename,pxdesc); + } else + singleset_posix(filename,pxdesc); + ntfs_free_mapping(context.mapping); + free(filename); + } else { + fprintf(stderr,"No more memory\n"); + cmderr = TRUE; + } + chkfree(pxdesc,__FILE__,__LINE__); + } else { + fprintf(stderr,"Bad UTF-8 name \"%s\"\n",argv[xarg + 1]); + cmderr = TRUE; + } + } +#else + 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 { + size = utf16size(argv[xarg + 1]); + if (size) { + filename = (char*)malloc(2*size + 2); + if (filename) { + makeutf16(filename,argv[xarg + 1]); +#if POSIXACLS + if (local_build_mapping(&context,filename)) { + printf("*** Could not get user mapping data\n"); + warnings++; + } +#endif + if (opt_r) { + for (i=0; i<(MAXSECURID + (1 << SECBLKSZ) - 1)/(1 << SECBLKSZ); i++) + securdata[i] = (struct SECURITY_DATA*)NULL; + recurseset(filename,mode); + } else + singleset(filename,mode); + free(filename); + } else { + fprintf(stderr,"No more memory\n"); + cmderr = TRUE; + } + } else { + fprintf(stderr,"Bad UTF-8 name \"%s\"\n",argv[xarg + 1]); + cmderr = TRUE; + } + } +#endif + break; +#if FORCEMASK + case 3 : + mode = 0; + forcemsk = 0; + p = argv[xarg]; + while (*p) { + if ((*p >= '0') && (*p <= '9')) + forcemsk = (forcemsk << 4) + *p - '0'; + else forcemsk = (forcemsk << 4) + (*p & 7) + 9; + p++; + } + p = argv[xarg + 1]; + 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 (opt_r) { + recurseset(argv[xarg + 2],mode); + } + else singleset(argv[xarg + 2],mode); + } + break; +#endif + } + 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 + 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"); + freeblocks(); + } + } + dumpalloc("termination"); + if (cmderr || errors) + exit(1); + return (0); +} + +#else + +/* + * Linux version + */ + +int main(int argc, char *argv[]) +{ + FILE *fd; + unsigned int mode; + const char *p; + int xarg; + BOOL cmderr; + int i; +#if POSIXACLS + struct POSIX_SECURITY *pxdesc; +#endif + + printf("%s\n",BANNER); + cmderr = FALSE; + errors = 0; + warnings = 0; + xarg = getoptions(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 + firstalloc = (struct CHKALLOC*)NULL; + mappingtype = MAPNONE; + switch (argc - xarg) { + case 0 : + if (opt_h) + showhex(stdin); +#if SELFTESTS & !USESTUBS + if (opt_t) + selftests(); +#endif + break; + case 1 : + if (opt_a) + cmderr = audit(argv[xarg]); + else + if (opt_h) { + fd = fopen(argv[xarg],"rb"); + if (fd) { + showhex(fd); + fclose(fd); + } else { + fprintf(stderr,"Could not open %s\n",argv[xarg]); + cmderr = TRUE; + } + } else + if (opt_b) + cmderr = backup(argv[xarg],"/"); + else + if (opt_r) + cmderr = listfiles(argv[xarg],"/"); + else + if (opt_s) + cmderr = dorestore(argv[xarg],stdin); + else + showmounted(argv[xarg]); + break; + case 2 : + if (opt_b) + cmderr = backup(argv[xarg],argv[xarg+1]); + else + if (opt_s) { + 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; + } + } else + cmderr = listfiles(argv[xarg],argv[xarg+1]); + break; + case 3 : + mode = 0; + p = argv[xarg+1]; +#if POSIXACLS + pxdesc = encode_posix_acl(p); + if (pxdesc) { + if (!getuid() && open_security_api()) { + if (open_volume(argv[xarg],MS_NONE)) { + if (opt_r) { + for (i=0; i<(MAXSECURID + (1 << SECBLKSZ) - 1)/(1 << SECBLKSZ); i++) + securdata[i] = (struct SECURITY_DATA*)NULL; + recurseset_posix(argv[xarg + 2],pxdesc); + } else + singleset_posix(argv[xarg + 2],pxdesc); + close_volume(argv[xarg]); + } else { + fprintf(stderr,"Could not open volume %s\n",argv[xarg]); + 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; + } + chkfree(pxdesc,__FILE__,__LINE__); + } else + cmderr = TRUE; +#else + 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(argv[xarg],MS_NONE)) { + if (opt_r) { + for (i=0; i<(MAXSECURID + (1 << SECBLKSZ) - 1)/(1 << SECBLKSZ); i++) + securdata[i] = (struct SECURITY_DATA*)NULL; + recurseset(argv[xarg + 2],mode); + } else + singleset(argv[xarg + 2],mode); + close_volume(argv[xarg]); + } else { + fprintf(stderr,"Could not open volume %s\n",argv[xarg]); + 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 + 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 (!cmderr) + 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 + if (!cmderr) + fprintf(stderr,"No errors were found\n"); + } + freeblocks(); + } else + cmderr = TRUE; + dumpalloc("termination"); + if (cmderr || errors) + exit(1); + return (0); +} + +#endif diff --git a/src/secaudit.h b/src/secaudit.h new file mode 100644 index 00000000..b13a3ac8 --- /dev/null +++ b/src/secaudit.h @@ -0,0 +1,727 @@ +/* + * General declarations for secaudit + * + * These declarations are organized to enable code sharing with ntfs-3g + * library, but should only be used to build tools runnable both + * on Linux (dynamic linking) and Windows (static linking) + * + * Copyright (c) 2007-2008 Jean-Pierre Andre + * + */ + +/* + * 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 SELFTESTS 1 /* include code for self-testing */ +#define POSIXACLS 0 /* include code for processing Posix ACLs */ +#define NOREVBOM 0 /* temporary */ + +#define OWNERFROMACL 1 /* must match option in security.c */ + +#define MAXATTRSZ 30000 /* Max sec attr size (16448 met for WinXP) */ +#define MAXSECURID 262144 +#define SECBLKSZ 8 +#define MAXFILENAME 4096 +#define FORCEMASK 0 /* Special (dangerous) option -m to force a mask */ +#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 */ + +/* + * Definitions for Linux + * Use explicit or implicit dynamic linking + */ + +#ifdef HAVE_CONFIG_H +#undef POSIXACLS /* override default by configure option */ +#define USESTUBS 1 /* API stubs generated at link time */ +#else +#define USESTUBS 0 /* direct calls to API, based on following definitions */ +#define ENVNTFS3G "NTFS3G" +#define LIBFILE64 "/lib64/libntfs-3g.so.4921" +#define LIBFILE "/lib/libntfs-3g.so.4921" +#endif + +#define MAPDIR ".NTFS-3G" +#define MAPFILE "UserMapping" +#define MAGIC_API 0x09042009 + +#ifndef _NTFS_ENDIANS_H + +typedef char s8; +typedef short s16; +typedef long long s64; +typedef unsigned char u8; +typedef unsigned short le16, be16, u16; +typedef unsigned long long u64; +#ifdef STSC +typedef long s32; +typedef unsigned long le32, be32, u32; +#else +typedef int s32; +typedef unsigned int le32, be32, u32; +#endif + +#ifdef STSC +#define endian_rev16(x) ((((x) & 255L) << 8) + (((x) >> 8) & 255L)) +#define endian_rev32(x) ((((x) & 255L) << 24) + (((x) & 0xff00L) << 8) \ + + (((x) >> 8) & 0xff00L) + (((x) >> 24) & 255L)) +#else +#define endian_rev16(x) ((((x) & 255) << 8) + (((x) >> 8) & 255)) +#define endian_rev32(x) ((((x) & 255) << 24) + (((x) & 0xff00) << 8) \ + + (((x) >> 8) & 0xff00) + (((x) >> 24) & 255)) +#endif +#define endian_rev64(x) ((((x) & 255LL) << 56) + (((x) & 0xff00LL) << 40) \ + + (((x) & 0xff0000LL) << 24) + (((x) & 0xff000000LL) << 8) \ + + (((x) >> 8) & 0xff000000LL) + (((x) >> 24) & 0xff0000LL) \ + + (((x) >> 40) & 0xff00LL) + (((x) >> 56) & 255LL)) + +#if __BYTE_ORDER == __LITTLE_ENDIAN + +#define cpu_to_be16(x) endian_rev16(x) +#define cpu_to_be32(x) endian_rev32(x) +#define cpu_to_le16(x) (x) +#define cpu_to_le32(x) (x) +#define cpu_to_le64(x) (x) +#define le16_to_cpu(x) (x) +#define le32_to_cpu(x) (x) +#define le64_to_cpu(x) (x) + +#else + +#define cpu_to_be16(x) (x) +#define cpu_to_be32(x) (x) +#define cpu_to_le16(x) endian_rev16(x) +#define cpu_to_le32(x) endian_rev32(x) +#define cpu_to_le64(x) endian_rev64(x) +#define le16_to_cpu(x) endian_rev16(x) +#define le32_to_cpu(x) endian_rev32(x) +#define le64_to_cpu(x) endian_rev64(x) + +#endif + +#define const_le16_to_cpu(x) le16_to_cpu(x) +#define const_cpu_to_le16(x) cpu_to_le16(x) +#define const_cpu_to_le32(x) cpu_to_le32(x) +#define const_cpu_to_be16(x) cpu_to_be16(x) +#define const_cpu_to_be32(x) cpu_to_be32(x) + +#endif /* _NTFS_ENDIANS_H */ + +#ifndef FALSE +enum { FALSE, TRUE } ; +#endif /* FALSE */ + +#ifdef WIN32 + +typedef unsigned short uid_t; +typedef unsigned short gid_t; + +#define UNICODE(c) ((unsigned short)(c)) + +#define __attribute__(x) + +#else + +#ifndef BOOL +typedef int BOOL; /* Already defined in windows.h */ +#endif /* BOOL */ + +#ifdef STSC + +#define ENOTSUP 95 + +#endif /* STSC */ + +typedef u32 DWORD; /* must be 32 bits whatever the platform */ +typedef DWORD *LPDWORD; + +#define MS_NONE 0 /* no flag for mounting the device */ +#define MS_RDONLY 1 /* flag for mounting the device read-only */ + +#endif /* WIN32 */ + +#if defined(WIN32) | defined(STSC) + +/* + * On non-Linux computers, there is no mount and the user mapping + * if fetched from a real file (or a dummy one for self tests) + */ + +#define NTFS_FIND_USID(map,uid,buf) ntfs_find_usid(map,uid,buf) +#define NTFS_FIND_GSID(map,gid,buf) ntfs_find_gsid(map,gid,buf) +#define NTFS_FIND_USER(map,usid) ntfs_find_user(map,usid) +#define NTFS_FIND_GROUP(map,gsid) ntfs_find_group(map,gsid) + +#else + +/* + * On Linux computers, there is a mount and the user mapping + * if either obtained through the mount process or fetched + * from a dummy file for self-tests + */ + +#define NTFS_FIND_USID(map,uid,buf) (mappingtype != MAPEXTERN ? \ + ntfs_find_usid(map,uid,buf) : relay_find_usid(map,uid,buf)) +#define NTFS_FIND_GSID(map,gid,buf) (mappingtype != MAPEXTERN ? \ + ntfs_find_gsid(map,gid,buf) : relay_find_gsid(map,gid,buf)) +#define NTFS_FIND_USER(map,usid) (mappingtype != MAPEXTERN ? \ + ntfs_find_user(map,usid) : relay_find_user(map,usid)) +#define NTFS_FIND_GROUP(map,gsid) (mappingtype != MAPEXTERN ? \ + ntfs_find_group(map,gsid) : relay_find_group(map,gsid)) + +#endif + +/* + * A few name hijackings or definitions + * needed for using code from ntfs-3g + */ + +#ifdef WIN32 +#define ACL MY_ACL +#define SID MY_SID +#define ACCESS_ALLOWED_ACE MY_ACCESS_ALLOWED_ACE +#define ACCESS_DENIED_ACE MY_ACCESS_DENIED_ACE +#define FILE_ATTRIBUTE_REPARSE_POINT 0x400 +#define IO_REPARSE_TAG_MOUNT_POINT 0xa0000003 +#define IO_REPARSE_TAG_SYMLINK 0xa000000c +#else +#define SE_OWNER_DEFAULTED const_cpu_to_le16(1) +#define SE_GROUP_DEFAULTED const_cpu_to_le16(2) +#define SE_DACL_PRESENT const_cpu_to_le16(4) +#define SE_SACL_PRESENT const_cpu_to_le16(0x10) +#define SE_DACL_DEFAULTED const_cpu_to_le16(8) +#define SE_SELF_RELATIVE const_cpu_to_le16(0x8000) +#define SID_REVISION 1 +#endif /* WIN32 */ +#define SE_DACL_PROTECTED const_cpu_to_le16(0x1000) +#define SE_SACL_PROTECTED const_cpu_to_le16(0x2000) +#define SE_DACL_AUTO_INHERITED const_cpu_to_le16(0x400) +#define SE_SACL_AUTO_INHERITED const_cpu_to_le16(0x800) +#define SE_DACL_AUTO_INHERIT_REQ cpu_to_le16(0x100) +#define SE_SACL_AUTO_INHERIT_REQ cpu_to_le16(0x200) + +typedef le16 ntfschar; + +typedef struct { + le32 a; + le16 b,c; + struct { + le16 m,n,o,p, q,r,s,t; + } ; +} GUID; + +#define ntfs_log_error(args...) do { printf("** " args); if (!isatty(1)) fprintf(stderr,args); } while(0) + +/* + * Struct to hold the input mapping file + * (private to this module) + */ + +struct MAPLIST { + struct MAPLIST *next; + char *uidstr; /* uid text from the same record */ + char *gidstr; /* gid text from the same record */ + char *sidstr; /* sid text from the same record */ + char maptext[LINESZ + 1]; +}; + +/* + * A few dummy declarations needed for using code from security.c + */ + +#define MFT_RECORD_IS_DIRECTORY const_cpu_to_le16(1) + +struct SECURITY_DATA { + u64 offset; + char *attr; + u32 hash; + u32 length; + unsigned int filecount:16; + unsigned int mode:12; + unsigned int flags:4; +} ; + +#define AUTH1 3141592653U +#define AUTH2 589793238 +#define AUTH3 462843383 +#define OWNERID 1016 +#define GROUPID 513 + + +#define INSDS1 1 +#define INSDS2 2 +#define INSII 4 +#define INSDH 8 + +#ifdef WIN32 + +typedef enum { RECSHOW, RECSET, RECSETPOSIX } RECURSE; + +#endif + +/* + * A type large enough to hold any SID + */ + +typedef char BIGSID[40]; + +/* + * Declarations for memory allocation checks + */ + +struct CHKALLOC + { + struct CHKALLOC *next; + void *alloc; + const char *file; + int line; + size_t size; + } ; + +#if defined(WIN32) | defined(STSC) + +#define S_ISVTX 01000 +#define S_ISGID 02000 +#define S_ISUID 04000 +#define S_IXUSR 0100 +#define S_IWUSR 0200 +#define S_IRUSR 0400 +#define S_IXGRP 010 +#define S_IWGRP 020 +#define S_IRGRP 040 +#define S_IXOTH 001 +#define S_IWOTH 002 +#define S_IROTH 004 + +#endif + +#ifdef WIN32 +#else +/* + * + * See http://msdn2.microsoft.com/en-us/library/aa379649.aspx + */ + +typedef enum { + DACL_SECURITY_INFORMATION = 4, // The DACL of the object is being referenced. + SACL_SECURITY_INFORMATION = 8, // The SACL of the object is being referenced. + LABEL_SECURITY_INFORMATION = 8, // The mandatory integrity label is being referenced. + GROUP_SECURITY_INFORMATION = 2, // The primary group identifier of the object is being referenced. + OWNER_SECURITY_INFORMATION = 1, // The owner identifier of the object is being referenced. +} SECURITY_INFORMATION; + +#define STANDARD_RIGHTS_READ cpu_to_le32(0x20000) +#define STANDARD_RIGHTS_WRITE cpu_to_le32(0x20000) +#define STANDARD_RIGHTS_EXECUTE cpu_to_le32(0x20000) +#define STANDARD_RIGHTS_REQUIRED cpu_to_le32(0xf0000) + +#endif + +typedef struct SECHEAD { + s8 revision; + s8 alignment; + le16 control; + le32 owner; + le32 group; + le32 sacl; + le32 dacl; +} SECURITY_DESCRIPTOR_RELATIVE; + +typedef struct ACL { + s8 revision; + s8 alignment1; + le16 size; + le16 ace_count; + le16 alignment2; +} ACL; + +typedef struct { + union { + struct { + unsigned char revision; + unsigned char sub_authority_count; + } ; + struct { + /* evade an alignment problem when a 4 byte field */ + /* in a struct implies alignment of the struct */ + le16 dummy; + be16 high_part; + be32 low_part; + } identifier_authority; + } ; + le32 sub_authority[1]; +} SID; + +typedef u8 ACE_FLAGS; + +typedef struct ACE { + u8 type; + u8 flags; + le16 size; + le32 mask; + SID sid; +} ACCESS_ALLOWED_ACE, ACCESS_DENIED_ACE; + + +/* + * item in the mapping list + */ + +struct MAPPING { + struct MAPPING *next; + int xid; /* linux id : uid or gid */ + SID *sid; /* Windows id : usid or gsid */ + int grcnt; /* group count (for users only) */ + gid_t *groups; /* groups which the user is member of */ +}; + +/* + * Posix ACL structures + */ + +struct POSIX_ACE { + u16 tag; + u16 perms; + s32 id; +} ; + +struct POSIX_ACL { + u8 version; + u8 flags; + u16 filler; + struct POSIX_ACE ace[0]; +} ; + +struct POSIX_SECURITY { + mode_t mode; + int acccnt; + int defcnt; + int firstdef; + u16 tagsset; + struct POSIX_ACL acl; +} ; + +/* + * Posix tags, cpu-endian 16 bits + */ + +enum { + POSIX_ACL_USER_OBJ = 1, + POSIX_ACL_USER = 2, + POSIX_ACL_GROUP_OBJ = 4, + POSIX_ACL_GROUP = 8, + POSIX_ACL_MASK = 16, + POSIX_ACL_OTHER = 32, + POSIX_ACL_SPECIAL = 64 /* internal use only */ +} ; + +/* + * Posix permissions, cpu-endian 16 bits + */ + +enum { + POSIX_PERM_X = 1, + POSIX_PERM_W = 2, + POSIX_PERM_R = 4, + POSIX_PERM_DENIAL = 64 /* internal use only */ +} ; + +#define POSIX_VERSION 2 + +/* + * A few definitions adapted from winnt.h + * (Windows version uses actual definitions from winnt.h, which are + * not compatible with code from security.c on a big-endian computer) + */ + +#ifndef WIN32 + +#define DELETE cpu_to_le32(0x00010000L) +#define READ_CONTROL cpu_to_le32(0x00020000L) +#define WRITE_DAC cpu_to_le32(0x00040000L) +#define WRITE_OWNER cpu_to_le32(0x00080000L) +#define SYNCHRONIZE cpu_to_le32(0x00100000L) + + +#define FILE_READ_DATA cpu_to_le32( 0x0001 ) // file & pipe +#define FILE_LIST_DIRECTORY cpu_to_le32( 0x0001 ) // directory + +#define FILE_WRITE_DATA cpu_to_le32( 0x0002 ) // file & pipe +#define FILE_ADD_FILE cpu_to_le32( 0x0002 ) // directory + +#define FILE_APPEND_DATA cpu_to_le32( 0x0004 ) // file +#define FILE_ADD_SUBDIRECTORY cpu_to_le32( 0x0004 ) // directory +#define FILE_CREATE_PIPE_INSTANCE cpu_to_le32( 0x0004 ) // named pipe + + +#define FILE_READ_EA cpu_to_le32( 0x0008 ) // file & directory + +#define FILE_WRITE_EA cpu_to_le32( 0x0010 ) // file & directory + +#define FILE_EXECUTE cpu_to_le32( 0x0020 ) // file +#define FILE_TRAVERSE cpu_to_le32( 0x0020 ) // directory + +#define FILE_DELETE_CHILD cpu_to_le32( 0x0040 ) // directory + +#define FILE_READ_ATTRIBUTES cpu_to_le32( 0x0080 ) // all + +#define FILE_WRITE_ATTRIBUTES cpu_to_le32( 0x0100 ) // all + +#define FILE_ALL_ACCESS (STANDARD_RIGHTS_REQUIRED | SYNCHRONIZE | \ + cpu_to_le32(0x1FF)) + +#define FILE_GENERIC_READ (STANDARD_RIGHTS_READ |\ + FILE_READ_DATA |\ + FILE_READ_ATTRIBUTES |\ + FILE_READ_EA |\ + SYNCHRONIZE) + + +#define FILE_GENERIC_WRITE (STANDARD_RIGHTS_WRITE |\ + FILE_WRITE_DATA |\ + FILE_WRITE_ATTRIBUTES |\ + FILE_WRITE_EA |\ + FILE_APPEND_DATA |\ + SYNCHRONIZE) + + +#define FILE_GENERIC_EXECUTE (STANDARD_RIGHTS_EXECUTE |\ + FILE_READ_ATTRIBUTES |\ + FILE_EXECUTE |\ + SYNCHRONIZE) + +#define GENERIC_READ cpu_to_le32(0x80000000L) +#define GENERIC_WRITE cpu_to_le32(0x40000000L) +#define GENERIC_EXECUTE cpu_to_le32(0x20000000L) +#define GENERIC_ALL cpu_to_le32(0x10000000L) + + +#define OBJECT_INHERIT_ACE (0x1) +#define CONTAINER_INHERIT_ACE (0x2) +#define NO_PROPAGATE_INHERIT_ACE (0x4) +#define INHERIT_ONLY_ACE (0x8) +#define VALID_INHERIT_FLAGS (0xF) + +/* + * Other useful definitions + */ + +#define ACL_REVISION 2 +#define ACCESS_ALLOWED_ACE_TYPE 0 +#define ACCESS_DENIED_ACE_TYPE 1 +#define SECURITY_DESCRIPTOR_REVISION 1 + +#endif /* !WIN32 */ + +/* + * Matching of ntfs permissions to Linux permissions + * these constants are adapted to endianness + * when setting, set them all + * when checking, check one is present + */ + + /* flags which are set to mean exec, write or read */ + +#define FILE_READ (FILE_READ_DATA) +#define FILE_WRITE (FILE_WRITE_DATA | FILE_APPEND_DATA \ + | READ_CONTROL | FILE_WRITE_ATTRIBUTES | FILE_WRITE_EA) +#define FILE_EXEC (FILE_EXECUTE) +#define DIR_READ FILE_LIST_DIRECTORY +#define DIR_WRITE (FILE_ADD_FILE | FILE_ADD_SUBDIRECTORY | FILE_DELETE_CHILD \ + | READ_CONTROL | FILE_WRITE_ATTRIBUTES | FILE_WRITE_EA) +#define DIR_EXEC (FILE_TRAVERSE) + + /* flags tested for meaning exec, write or read */ + /* tests for write allow for interpretation of a sticky bit */ + +#define FILE_GREAD (FILE_READ_DATA | GENERIC_READ) +#define FILE_GWRITE (FILE_WRITE_DATA | FILE_APPEND_DATA | GENERIC_WRITE) +#define FILE_GEXEC (FILE_EXECUTE | GENERIC_EXECUTE) +#define DIR_GREAD (FILE_LIST_DIRECTORY | GENERIC_READ) +#define DIR_GWRITE (FILE_ADD_FILE | FILE_ADD_SUBDIRECTORY | GENERIC_WRITE) +#define DIR_GEXEC (FILE_TRAVERSE | GENERIC_EXECUTE) + + /* 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 */ + + +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; + } ; + +#ifndef INVALID_FILE_ATTRIBUTES /* not defined in old windows.h */ +#define INVALID_FILE_ATTRIBUTES (-1) +#endif + +enum { MAPUSERS, MAPGROUPS, MAPCOUNT } ; + +struct SECURITY_CONTEXT { + struct MAPPING *mapping[MAPCOUNT]; +} ; + +typedef enum { MAPNONE, MAPEXTERN, MAPLOCAL, MAPDUMMY } MAPTYPE; + + + +struct passwd { + uid_t pw_uid; +} ; + +struct group { + gid_t gr_gid; +} ; + +typedef int (*FILEREADER)(void *fileid, char *buf, size_t size, off_t pos); + +/* + * Data defined in secaudit.c + */ + +extern MAPTYPE mappingtype; + +/* + * Functions defined in acls.c + */ + +BOOL ntfs_valid_descr(const char *securattr, unsigned int attrsz); +BOOL ntfs_valid_posix(const struct POSIX_SECURITY *pxdesc); +BOOL ntfs_valid_pattern(const SID *sid); +BOOL ntfs_same_sid(const SID *first, const SID *second); + + +int ntfs_sid_size(const SID * sid); +unsigned int ntfs_attr_size(const char *attr); + +const SID *ntfs_find_usid(const struct MAPPING *usermapping, + uid_t uid, SID *pdefsid); +const SID *ntfs_find_gsid(const struct MAPPING *groupmapping, + gid_t gid, SID *pdefsid); +uid_t ntfs_find_user(const struct MAPPING *usermapping, const SID *usid); +gid_t ntfs_find_group(const struct MAPPING *groupmapping, const SID * gsid); +const SID *ntfs_acl_owner(const char *secattr); + +void ntfs_sort_posix(struct POSIX_SECURITY *pxdesc); +int ntfs_merge_mode_posix(struct POSIX_SECURITY *pxdesc, mode_t mode); + + +struct POSIX_SECURITY *ntfs_build_permissions_posix( + struct MAPPING* const mapping[], + const char *securattr, + const SID *usid, const SID *gsid, BOOL isdir); +int ntfs_build_permissions(const char *securattr, + const SID *usid, const SID *gsid, BOOL isdir); +struct MAPLIST *ntfs_read_mapping(FILEREADER reader, void *fileid); +struct MAPPING *ntfs_do_user_mapping(struct MAPLIST *firstitem); +struct MAPPING *ntfs_do_group_mapping(struct MAPLIST *firstitem); +void ntfs_free_mapping(struct MAPPING *mapping[]); + +struct POSIX_SECURITY *ntfs_merge_descr_posix(const struct POSIX_SECURITY *first, + const struct POSIX_SECURITY *second); +char *ntfs_build_descr_posix(struct MAPPING* const mapping[], + struct POSIX_SECURITY *pxdesc, + int isdir, const SID *usid, const SID *gsid); +char *ntfs_build_descr(mode_t mode, + int isdir, const SID * usid, const SID * gsid); + +/* + * Functions defined in secaudit.c + */ + +void *chkmalloc(size_t, const char*, int); +void *chkcalloc(size_t, size_t, const char *, int); +void chkfree(void*, const char*, int); +BOOL chkisalloc(void*, const char*, int); +void dumpalloc(const char*); + +#define malloc(sz) chkmalloc(sz, __FILE__, __LINE__) +#define calloc(cnt,sz) chkcalloc(cnt, sz, __FILE__, __LINE__) +#define free(ptr) chkfree(ptr, __FILE__, __LINE__) +#define isalloc(ptr) chkisalloc(ptr, __FILE__, __LINE__) +#define ntfs_malloc(sz) chkmalloc(sz, __FILE__, __LINE__) + +struct passwd *getpwnam(const char *user); +struct group *getgrnam(const char *group); + +const SID *relay_find_usid(const struct MAPPING *usermapping, + uid_t uid, SID *pdefsid); +const SID *relay_find_gsid(const struct MAPPING *groupmapping, + gid_t gid, SID *pdefsid); +uid_t relay_find_user(const struct MAPPING *usermapping, const SID *usid); +gid_t relay_find_group(const struct MAPPING *groupmapping, const SID * gsid); + diff --git a/src/usermap.c b/src/usermap.c new file mode 100644 index 00000000..847f6510 --- /dev/null +++ b/src/usermap.c @@ -0,0 +1,1333 @@ +/* + * Windows to Linux user mapping for ntfs-3g + * + * + * Copyright (c) 2007-2008 Jean-Pierre Andre + * + * A quick'n dirty program scanning owners of files in + * "c:\Documents and Settings" (and "c:\Users") + * and asking user to map them to Linux accounts + * + * History + * + * Sep 2007 + * - first version, limited to Win32 + * + * Oct 2007 + * - ported to Linux (rewritten would be more correct) + * + * Nov 2007 Version 1.0.0 + * - added more defaults + * + * Nov 2007 Version 1.0.1 + * - avoided examining files whose name begin with a '$' + * + * Jan 2008 Version 1.0.2 + * - moved user mapping file to directory .NTFS-3G (hidden for Linux) + * - fixed an error case in Windows version + * + * Nov 2008 Version 1.1.0 + * - fixed recursions for account in Linux version + * - searched owner in c:\Users (standard location for Vista) + * + * May 2009 Version 1.1.1 + * - reordered mapping records to limit usage of same SID for user and group + * - fixed decoding SIDs on 64-bit systems + * - fixed a pointer to dynamic data in mapping tables + * - fixed default mapping on Windows + * - fixed bug for renaming UserMapping on Windows + * + * May 2009 Version 1.1.2 + * - avoided selecting DOS names on Linux + */ + +/* + * 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 + */ + +#ifdef HAVE_CONFIG_H +#define USESTUBS 1 /* API stubs generated at link time */ +#else +#define USESTUBS 0 /* direct calls to API, based on following definitions */ +#define ENVNTFS3G "NTFS3G" +#define LIBFILE64 "/lib64/libntfs-3g.so.4921" +#define LIBFILE "/lib/libntfs-3g.so.4921" +#endif + +#define GET_FILE_SECURITY "ntfs_get_file_security" +#define SET_FILE_SECURITY "ntfs_set_file_security" +#define READ_DIRECTORY "ntfs_read_directory" +#define INIT_FILE_SECURITY "ntfs_initialize_file_security" +#define LEAVE_FILE_SECURITY "ntfs_leave_file_security" + +#define VERSION "1.1.2" +#define MAPDIR ".NTFS-3G" +#define MAPFILE "UserMapping" +#define MAXATTRSZ 2048 +#define MAXSIDSZ 80 +#define MAXNAMESZ 256 +#define OWNERS1 "Documents and Settings" +#define OWNERS2 "Users" + +/* + * Define WIN32 for a Windows execution + * may have to be adapted to compiler or something else + */ + +#ifndef WIN32 +#if defined(__WIN32) | defined(__WIN32__) | defined(WNSC) +#define WIN32 1 +#endif +#endif + +#ifdef WIN32 +#define BANNER "Generated by usermap for Windows, v " VERSION +#else +#define BANNER "Generated by usermap for Linux, v " VERSION +#endif + + +#include +#include +#include +#include +#include +#include +#include + +/* + * Define the security API according to platform + */ + +#ifdef WIN32 + +#include +#include + +#define STATIC + +typedef enum { false, true } boolean; + +#else + +#include +#include + +typedef enum { false, true } boolean, BOOL; +typedef unsigned int DWORD; /* must be 32 bits whatever the platform */ +typedef DWORD *LPDWORD; + +enum { OWNER_SECURITY_INFORMATION = 1, + GROUP_SECURITY_INFORMATION = 2, + DACL_SECURITY_INFORMATION = 4, + SACL_SECURITY_INFORMATION = 8 +} ; + +struct CALLBACK { + const char *accname; + const char *dir; + int levels; + int docset; +} ; + +typedef int (*dircallback)(struct CALLBACK *context, char *ntfsname, + int length, int type, long long pos, unsigned long long mft_ref, + unsigned int dt_type); + +#if USESTUBS + +#define STATIC static + +BOOL ntfs_get_file_security(void *scapi, + const char *path, DWORD selection, + char *buf, DWORD buflen, LPDWORD psize); +BOOL ntfs_set_file_security(void *scapi, + const char *path, DWORD selection, const char *attr); +BOOL ntfs_read_directory(void *scapi, + const char *path, dircallback callback, void *context); +void *ntfs_initialize_file_security(const char *device, + int flags); +BOOL ntfs_leave_file_security(void *scapi); + +#else + +#define STATIC + +BOOL (*ntfs_get_file_security)(void *scapi, + const char *path, DWORD selection, + char *buf, DWORD buflen, LPDWORD psize); +BOOL (*ntfs_set_file_security)(void *scapi, + const char *path, DWORD selection, const char *attr); +BOOL (*ntfs_read_directory)(void *scapi, + const char *path, dircallback callback, void *context); +void *(*ntfs_initialize_file_security)(const char *device, + int flags); +BOOL (*ntfs_leave_file_security)(void *scapi); + +#endif + +STATIC boolean open_security_api(void); +STATIC boolean close_security_api(void); +STATIC boolean open_volume(const char *volume); +STATIC boolean close_volume(const char *volume); + +#endif + +struct MAPPING { + struct MAPPING *next; + const char *uidstr; + const char *gidstr; + const char *sidstr; + const unsigned char *sid; + const char *login; + boolean defined; +}; + +struct MAPPING *firstmapping; +struct MAPPING *lastmapping; + +#ifdef WIN32 +char *currentwinname; +char *currentdomain; +unsigned char *currentsid; +#endif + +#ifndef WIN32 + +void *ntfs_handle; +void *ntfs_context = (void*)NULL; + +/* + * Open and close the security API (platform dependent) + */ + +STATIC boolean open_security_api(void) +{ +#if USESTUBS + return (true); +#else + char *error; + boolean err; + const char *libfile; + + err = true; + libfile = getenv(ENVNTFS3G); + if (!libfile) + libfile = (sizeof(char*) == 8 ? LIBFILE64 : LIBFILE); + ntfs_handle = dlopen(libfile,RTLD_LAZY); + if (ntfs_handle) { + ntfs_initialize_file_security = + dlsym(ntfs_handle,INIT_FILE_SECURITY); + error = dlerror(); + if (error) + fprintf(stderr," %s\n",error); + else { + ntfs_leave_file_security = + dlsym(ntfs_handle,LEAVE_FILE_SECURITY); + ntfs_get_file_security = + dlsym(ntfs_handle,GET_FILE_SECURITY); + ntfs_set_file_security = + dlsym(ntfs_handle,SET_FILE_SECURITY); + ntfs_read_directory = + dlsym(ntfs_handle,READ_DIRECTORY); + err = !ntfs_initialize_file_security + || !ntfs_leave_file_security + || !ntfs_get_file_security + || !ntfs_set_file_security + || !ntfs_read_directory; + if (error) + fprintf(stderr,"ntfs-3g API not available\n"); + } + } else { + fprintf(stderr,"Could not open ntfs-3g library\n"); + fprintf(stderr,"\nPlease set environment variable \"" ENVNTFS3G "\"\n"); + fprintf(stderr,"to appropriate path and retry\n"); + } + return (!err); +#endif +} + +STATIC boolean close_security_api(void) +{ +#if USESTUBS + return (0); +#else + return (!dlclose(ntfs_handle)); +#endif +} + +/* + * Open and close a volume (platform dependent) + * assuming a single volume needs to be opened at any time + */ + +STATIC boolean open_volume(const char *volume) +{ + boolean ok; + + ok = false; + if (!ntfs_context) { + ntfs_context = ntfs_initialize_file_security(volume,0); + if (ntfs_context) { + fprintf(stderr,"\"%s\" opened\n",volume); + ok = true; + } else { + fprintf(stderr,"Could not open \"%s\"\n",volume); + fprintf(stderr,"Make sure \"%s\" is not mounted\n",volume); + } + } else + fprintf(stderr,"A volume is already open\n"); + return (ok); +} + +STATIC boolean close_volume(const char *volume) +{ + boolean 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 = (void*)NULL; + return (r); +} + +/* + * A poor man's conversion of Unicode to UTF8 + * We are assuming outputs to terminal expect UTF8 + */ + +STATIC void to_utf8(char *dst, const char *src, unsigned int cnt) +{ + unsigned int ch; + unsigned int i; + + for (i=0; i> 6); + *dst++ = 0x80 + (ch & 63); + } else { + *dst++ = 0xe0 + (ch >> 12); + *dst++ = 0x80 + ((ch >> 6) & 63); + *dst++ = 0x80 + (ch & 63); + } + } + *dst = 0; +} + +STATIC int utf8_size(const char *src, unsigned int cnt) +{ + unsigned int ch; + unsigned int i; + int size; + + size = 0; + for (i=0; isidstr, sidstr)) + mapping = mapping->next; + if (mapping + && (mapping->defined + || !accname + || !strcmp(mapping->login, accname))) + free(sidstr); /* decision already known */ + else { + do { + reject = false; + printf("\n"); + if (accname) + printf("Under Windows login \"%s\"\n", accname); + printf(" file \"%s\" has no mapped %s\n", + filename,(type ? "group" : "owner")); + printf("By which Linux login should this file be owned ?\n"); + printf("Enter %s of login, or just press \"enter\" if this file\n", + (type ? "gid" : "uid")); + printf("does not belong to a user, or you do not known to whom\n"); + printf("\n"); + if (type) + printf("Group : "); + else + printf("User : "); + p = fgets(buf, 80, stdin); + if (p && p[0] && (p[strlen(p) - 1] == '\n')) + p[strlen(p) - 1] = '\0'; + + if (p && p[0] + && ((p[0] == '0') || !strcmp(p, "root"))) { + printf("Please do not map users to root\n"); + printf("Administrators will be mapped automatically\n"); + reject = true; + } + if (reject) + printf("Please retry\n"); + } while (reject); + if (!mapping) { + mapping = + (struct MAPPING *) + malloc(sizeof(struct MAPPING)); + mapping->next = (struct MAPPING *)NULL; + mapping->defined = false; + if (lastmapping) + lastmapping->next = mapping; + else + firstmapping = mapping; + lastmapping = mapping; + } + if (mapping) { + if (p && p[0]) { + idstr = (char *)malloc(strlen(p) + 1); + if (idstr) { + strcpy(idstr, p); + if (type) { + mapping->uidstr = ""; + mapping->gidstr = idstr; + } else { + mapping->uidstr = idstr; + mapping->gidstr = idstr; + } + mapping->defined = true; + } + } + mapping->sidstr = sidstr; + if (accname) { + login = (char*)malloc(strlen(accname) + 1); + if (login) + strcpy(login,accname); + mapping->login = login; + } else + mapping->login = (char*)NULL; + sidsz = 8 + sid[1]*4; + p = (char*)malloc(sidsz); + if (p) { + memcpy(p, sid, sidsz); + } + mapping->sid = (unsigned char*)p; + } + } + } +} + +STATIC void listaclusers(const char *accname, const unsigned char *attr, int off) +{ + int i; + int cnt; + int x; + + cnt = get2l(attr, off + 4); + x = 8; + for (i = 0; i < cnt; i++) { + domapping(accname, (char *)NULL, &attr[off + x + 8], 2); + x += get2l(attr, off + x + 2); + } +} + +#ifdef WIN32 + +STATIC void account(const char *accname, const char *dir, const char *name, int type) +{ + unsigned char attr[MAXATTRSZ]; + unsigned long attrsz; + char *fullname; + int attrib; + + fullname = (char *)malloc(strlen(dir) + strlen(name) + 2); + if (fullname) { + strcpy(fullname, dir); + strcat(fullname, "\\"); + strcat(fullname, name); + attrib = GetFileAttributes(fullname); + if (attrib & 0x10) { /* only directories processed */ + if (GetFileSecurity + (fullname, OWNER_SECURITY_INFORMATION, attr, MAXATTRSZ, + &attrsz)) { + domapping(accname, name, &attr[20], 0); + attrsz = 0; + if (GetFileSecurity + (fullname, GROUP_SECURITY_INFORMATION, attr, + MAXATTRSZ, &attrsz)) + domapping(accname, name, &attr[20], 1); + else + printf(" No group SID\n"); + attrsz = 0; + if (GetFileSecurityA + (fullname, DACL_SECURITY_INFORMATION, attr, + MAXATTRSZ, &attrsz)) { + if (type == 0) + listaclusers(accname, attr, 20); + } else + printf + (" No discretionary access control list\n"); + } + } + free(fullname); + } +} + +#else + +STATIC void account(const char *accname, const char *dir, const char *name, int type) +{ + unsigned char attr[MAXATTRSZ]; + DWORD attrsz; + char *fullname; + + fullname = (char *)malloc(strlen(dir) + strlen(name) + 2); + if (fullname) { + strcpy(fullname, dir); + strcat(fullname, "/"); + strcat(fullname, name); + if (ntfs_get_file_security(ntfs_context, + fullname, OWNER_SECURITY_INFORMATION, + (char*)attr, MAXATTRSZ, &attrsz)) { + domapping(accname, name, &attr[20], 0); + attrsz = 0; + if (ntfs_get_file_security(ntfs_context, + fullname, GROUP_SECURITY_INFORMATION, + (char*)attr, MAXATTRSZ, &attrsz)) + domapping(accname, name, &attr[20], 1); + else + printf(" No group SID\n"); + attrsz = 0; + if (ntfs_get_file_security(ntfs_context, + fullname, DACL_SECURITY_INFORMATION, + (char*)attr, MAXATTRSZ, &attrsz)) { + if (type == 0) + listaclusers(accname, attr, 20); + } else + printf(" No discretionary access control list for %s !\n", + dir); + } + free(fullname); + } +} + +#endif + + +/* + * recursive search of file owners and groups in a directory + */ + +#ifdef WIN32 + +STATIC boolean recurse(const char *accname, const char *dir, int levels) +{ + WIN32_FIND_DATA found; + HANDLE search; + char *filter; + char *fullname; + boolean err; + + err = false; + filter = (char *)malloc(strlen(dir) + 5); + if (filter) { + strcpy(filter, dir); + strcat(filter, "\\*.*"); + search = FindFirstFile(filter, &found); + if (search != INVALID_HANDLE_VALUE) { + do { + if (found.cFileName[0] != '.') { + account(accname, dir, found.cFileName,1); + if (levels > 0) { + fullname = + (char *)malloc(strlen(dir) + + strlen(found.cFileName) + + 2); + if (fullname) { + strcpy(fullname, dir); + strcat(fullname, "\\"); + strcat(fullname, + found.cFileName); + recurse(accname, + fullname, + levels - 1); + free(fullname); + } + } + } + } while (FindNextFile(search, &found)); + FindClose(search); + } + free(filter); + } else { + printf("Directory %s not found\n",dir); + err = true; + } + return (!err); +} + +#else + +STATIC boolean recurse(const char *accname, const char *dir, int levels, int docset); + +STATIC int callback(struct CALLBACK *context, char *ntfsname, + int length, int type, long long pos, unsigned long long mft_ref, + unsigned int dt_type) +{ + char *fullname; + char *accname; + char *name; + + fullname = (char *)malloc(strlen(context->dir) + + utf8_size(ntfsname, length) + 2); + if (fullname) { + if (strcmp(context->dir,"/")) { + strcpy(fullname, context->dir); + strcat(fullname, "/"); + } else + strcpy(fullname,"/"); + /* Unicode to ascii conversion by a lazy man */ + name = &fullname[strlen(fullname)]; + to_utf8(name, ntfsname, length); + /* ignore special files and DOS names */ + if ((type != 2) + && strcmp(name,".") + && strcmp(name,"..") + && (name[0] != '$')) { + switch (context->docset) { + case 2 : + /* + * only "Documents and Settings" + * or "Users" + */ + if (!strcmp(name,OWNERS1) + || !strcmp(name,OWNERS2)) { + recurse((char*)NULL, fullname, 2, 1); + } + break; + /* + * within "Documents and Settings" + * or "Users" + */ + case 1 : + accname = (char*)malloc(strlen(name) + 1); + if (accname) { + strcpy(accname, name); + if (context->levels > 0) + recurse(name, fullname, + context->levels - 1, 0); + } + break; + /* + * not related to "Documents and Settings" + * or "Users" + */ + case 0 : + account(context->accname, context->dir, + name, 1); + if (context->levels > 0) + recurse(context->accname, fullname, + context->levels - 1, 0); + break; + } + } + free(fullname); + } +/* check expected return value */ + return (0); +} + +STATIC boolean recurse(const char *accname, const char *dir, int levels, int docset) +{ + struct CALLBACK context; + boolean err; + + err = false; + context.dir = dir; + context.accname = accname; + context.levels = levels; + context.docset = docset; + ntfs_read_directory(ntfs_context,dir,callback,&context); + return (!err); +} +#endif + +/* + * Search directory "Documents and Settings" for user accounts + */ + +#ifdef WIN32 + +STATIC boolean getusers(const char *dir, int levels) +{ + WIN32_FIND_DATA found; + HANDLE search; + char *filter; + char *fullname; + char *accname; + boolean err; + const char *docset; + + /* first get files from "Documents and Settings" */ + err = false; + if (sizeof(OWNERS1) > sizeof(OWNERS2)) + filter = (char *)malloc(strlen(dir) + strlen(OWNERS1) + 6); + else + filter = (char *)malloc(strlen(dir) + strlen(OWNERS2) + 6); + if (filter) { + docset = OWNERS1; + strcpy(filter, dir); + strcat(filter, "\\"); + strcat(filter, docset); + strcat(filter, "\\*.*"); + search = FindFirstFile(filter, &found); + /* if failed, retry with "Users" */ + if (search == INVALID_HANDLE_VALUE) { + docset = OWNERS2; + strcpy(filter, dir); + strcat(filter, "\\"); + strcat(filter, docset); + strcat(filter, "\\*.*"); + search = FindFirstFile(filter, &found); + } + if (search != INVALID_HANDLE_VALUE) { + do { + if (found.cFileName[0] != '.') { + fullname = + (char *)malloc(strlen(dir) + + strlen(docset) + + strlen(found.cFileName) + 3); + accname = (char *) + malloc(strlen(found.cFileName) + 1); + if (fullname && accname) { + strcpy(accname, + found.cFileName); + + strcpy(fullname, dir); + strcat(fullname, "\\"); + strcat(fullname, docset); + strcat(fullname, "\\"); + strcat(fullname, + found.cFileName); + recurse(accname, fullname, 2); + + free(fullname); + } + } + } while (FindNextFile(search, &found)); + FindClose(search); + } else { + printf("No subdirectory found in %s\\%s\n",dir,docset); + } + /* now search in other directories */ + strcpy(filter, dir); + strcat(filter, "\\*.*"); + search = FindFirstFile(filter, &found); + if (search != INVALID_HANDLE_VALUE) { + do { + if ((found.cFileName[0] != '.') + && strcmp(found.cFileName,OWNERS1) + && strcmp(found.cFileName,OWNERS2)) { + fullname = + (char *)malloc(strlen(dir) + + strlen(found.cFileName) + 2); + if (fullname) { + strcpy(fullname, dir); + strcat(fullname, "\\"); + strcat(fullname, + found.cFileName); + recurse((char*)NULL, fullname, 2); + free(fullname); + } + } + } while (FindNextFile(search, &found)); + FindClose(search); + } else { + printf("No directory found in %s\n",dir); + err = true; + } + } + return (!err); +} + +#else + +STATIC boolean getusers(const char *dir, int levels) +{ + boolean err; + struct CALLBACK context; + + printf("* Search for \"" OWNERS1 "\" and \"" OWNERS2 "\"\n"); + err = false; + context.dir = dir; + context.accname = (const char*)NULL; + context.levels = levels; + context.docset = 2; + ntfs_read_directory(ntfs_context,dir,callback,&context); + printf("* Search for other directories %s\n",dir); + context.docset = 0; + ntfs_read_directory(ntfs_context,dir,callback,&context); + + return (!err); +} + +#endif + +#ifdef WIN32 +/* + * Get the current login name (Win32 only) + */ + +STATIC void loginname(boolean silent) +{ + char *winname; + char *domain; + unsigned char *sid; + unsigned long namesz; + unsigned long sidsz; + unsigned long domainsz; + int nametype; + boolean ok; + int r; + + ok = FALSE; + winname = (char*)malloc(MAXNAMESZ); + domain = (char*)malloc(MAXNAMESZ); + sid = (char*)malloc(MAXSIDSZ); + + namesz = MAXNAMESZ; + domainsz = MAXNAMESZ; + sidsz = MAXSIDSZ; + if (winname + && domain + && sid + && GetUserName(winname,&namesz)) { + winname[namesz] = '\0'; + if (!silent) + printf("Your current user name is %s\n",winname); + nametype = 1; + r = LookupAccountName((char*)NULL,winname,sid,&sidsz, + domain,&domainsz,&nametype); + if (r) { + domain[domainsz] = '\0'; + if (!silent) + printf("Your account domain is %s\n",domain); + ok = true; + } + } + if (ok) { + currentwinname = winname; + currentdomain = domain; + currentsid = sid; + } else { + currentwinname = (char*)NULL; + currentdomain = (char*)NULL; + currentsid = (unsigned char*)NULL; + } +} + +/* + * Minimal output on stdout + */ + +boolean minimal(unsigned char *sid) +{ + const unsigned char *groupsid; + boolean ok; + + ok = false; + if (sid) { + groupsid = makegroupsid(sid); + printf("# %s\n",BANNER); + printf("# For Windows account \"%s\" in domain \"%s\"\n", + currentwinname, currentdomain); + printf("# Replace \"user\" and \"group\" hereafter by matching Linux login\n"); + printf("user::%s\n",decodesid(sid)); + printf(":group:%s\n",decodesid(groupsid)); + ok = true; + } + return (ok); +} + +#endif + +STATIC boolean outputmap(const char *volume, const char *dir) +{ + char buf[256]; + int fn; + char *fullname; + char *backup; + struct MAPPING *mapping; + boolean done; + boolean err; + boolean undecided; +#ifdef WIN32 +#else + struct stat st; + int s; +#endif + + done = false; + fullname = (char *)malloc(strlen(MAPFILE) + 1 + + strlen(volume) + 1 + + (dir ? strlen(dir) + 1 : 0)); + if (fullname) { +#ifdef WIN32 + strcpy(fullname, volume); + if (dir && dir[0]) { + strcat(fullname, "\\"); + strcat(fullname,dir); + } + + /* build directory, if not present */ + if (GetFileAttributes(fullname) & 0x80000000) { + printf("* Creating directory %s\n", fullname); + mkdir(fullname); + } + + strcat(fullname, "\\"); + strcat(fullname, MAPFILE); + printf("\n"); + + if (!(GetFileAttributes(fullname) & 0x80000000)) { + backup = (char*)malloc(strlen(fullname) + 5); + strcpy(backup,fullname); + strcat(backup,".bak"); + unlink(backup); + if (!rename(fullname,backup)) + printf("* Old mapping file moved to %s\n",backup); + } +#else + strcpy(fullname, MAPFILE); + printf("\n"); + + s = stat(fullname,&st); + if (!s) { + backup = (char*)malloc(strlen(fullname + 5)); + strcpy(backup,fullname); + strcat(backup,".bak"); + if (rename(fullname,backup)) + printf("* Old mapping file moved to %s\n",backup); + } +#endif + + printf("* Creating file %s\n", fullname); + err = false; +#ifdef WIN32 + fn = open(fullname,O_CREAT + O_TRUNC + O_WRONLY + O_BINARY, + S_IREAD + S_IWRITE); +#else + fn = open(fullname,O_CREAT + O_TRUNC + O_WRONLY, + S_IREAD + S_IWRITE); +#endif + if (fn > 0) { + sprintf(buf,"# %s\n",BANNER); + if (!write(fn,buf,strlen(buf))) + err = true; + printf("%s",buf); + undecided = false; + /* records for owner only or group only */ + for (mapping = firstmapping; mapping && !err; + mapping = mapping->next) + if (mapping->defined + && (!mapping->uidstr[0] || !mapping->gidstr[0])) { + sprintf(buf,"%s:%s:%s\n", + mapping->uidstr, + mapping->gidstr, + mapping->sidstr); + if (!write(fn,buf,strlen(buf))) + err = true; + printf("%s",buf); + } else + undecided = true; + /* records for both owner and group */ + for (mapping = firstmapping; mapping && !err; + mapping = mapping->next) + if (mapping->defined + && mapping->uidstr[0] && mapping->gidstr[0]) { + sprintf(buf,"%s:%s:%s\n", + mapping->uidstr, + mapping->gidstr, + mapping->sidstr); + if (!write(fn,buf,strlen(buf))) + err = true; + printf("%s",buf); + } else + undecided = true; + done = !err; + close(fn); + if (undecided) { + printf("Undecided :\n"); + for (mapping = firstmapping; mapping; + mapping = mapping->next) + if (!mapping->defined) { + printf(" %s\n", mapping->sidstr); + } + } +#ifndef WIN32 + printf("\n* You will have to move the file \"" MAPFILE "\"\n"); + printf(" to directory \"" MAPDIR "\" after mounting\n"); +#endif + } + } + if (!done) + fprintf(stderr, "* Could not create mapping file \"%s\"\n", fullname); + return (done); +} + +STATIC boolean sanitize(void) +{ + char buf[81]; + boolean ok; + int ownercnt; + int groupcnt; + struct MAPPING *mapping; + struct MAPPING *firstowner; + struct MAPPING *genericgroup; + struct MAPPING *group; + char *sidstr; + + /* count owners and groups */ + /* and find first user, and a generic group */ + ownercnt = 0; + groupcnt = 0; + firstowner = (struct MAPPING*)NULL; + genericgroup = (struct MAPPING*)NULL; + for (mapping=firstmapping; mapping; mapping=mapping->next) { + if (mapping->defined && mapping->uidstr[0]) { + if (!ownercnt) + firstowner = mapping; + ownercnt++; + } + if (mapping->defined && mapping->gidstr[0] && !mapping->uidstr[0]) { + groupcnt++; + } + if (!mapping->defined && isgenericgroup(mapping->sidstr)) { + genericgroup = mapping; + } + } +#ifdef WIN32 + /* no user defined, on Windows, suggest a mapping */ + /* based on account currently used */ + if (!ownercnt && currentwinname && currentsid) { + char *owner; + char *p; + + printf("\nYou have defined no file owner,\n"); + printf(" please enter the Linux login which should be mapped\n"); + printf(" to account you are currently using\n"); + printf(" Linux user ? "); + p = fgets(buf, 80, stdin); + if (p && p[0] && (p[strlen(p) - 1] == '\n')) + p[strlen(p) - 1] = '\0'; + if (p && p[0]) { + firstowner = (struct MAPPING*)malloc(sizeof(struct MAPPING)); + owner = (char*)malloc(strlen(p) + 1); + if (firstowner && owner) { + strcpy(owner, p); + firstowner->next = firstmapping; + firstowner->uidstr = owner; + firstowner->gidstr = ""; + firstowner->sidstr = decodesid(currentsid); + firstowner->sid = currentsid; + firstmapping = firstowner; + ownercnt++; + /* prefer a generic group with the same authorities */ + for (mapping=firstmapping; mapping; + mapping=mapping->next) + if (!mapping->defined + && isgenericgroup(mapping->sidstr) + && !memcmp(firstowner->sidstr, + mapping->sidstr, + strlen(mapping->sidstr)-3)) + genericgroup = mapping; + } + } + } +#endif + if (ownercnt) { + /* + * No group was selected, but there were a generic group + * insist in using it, associated to the first user + */ + if (!groupcnt) { + printf("\nYou have defined no group, this can cause problems\n"); + printf("Do you accept defining a standard group ?\n"); + if (!fgets(buf,80,stdin) + || ((buf[0] != 'n') + && (buf[0] != 'N'))) { + if (genericgroup) { + genericgroup->uidstr = ""; + genericgroup->gidstr = firstowner->uidstr; + genericgroup->defined = true; + } else { + group = (struct MAPPING*) + malloc(sizeof(struct MAPPING)); + sidstr = decodesid( + makegroupsid(firstowner->sid)); + if (group && sidstr) { + group->uidstr = ""; + group->gidstr = firstowner-> + uidstr; + group->sidstr = sidstr; + group->defined = true; + group->next = firstmapping; + firstmapping = group; + } + } + } + } + ok = true; + } else { + printf("\nYou have defined no user, no mapping can be built\n"); + ok = false; + } + + return (ok); +} + +STATIC boolean checkoptions(int argc, char *argv[], boolean silent) +{ + boolean err; +#ifdef WIN32 + int xarg; + const char *pvol; + + if (silent) + err = (argc != 1); + else { + err = (argc < 2); + for (xarg=1; (xarg= 'A') && (pvol[0] <= 'Z')) + || ((pvol[0] >= 'a') && (pvol[0] <= 'z'))); + } + } + } + if (err) { + fprintf(stderr, "Usage : usermap [vol1: [vol2: ...]]\n"); + fprintf(stderr, " \"voln\" are the letters of the partition to share with Linux\n"); + fprintf(stderr, " eg C:\n"); + fprintf(stderr, " the Windows system partition should be named first\n"); + } +#else + err = (argc < 2); + if (err) { + fprintf(stderr, "Usage : usermap dev1 [dev2 ...]\n"); + fprintf(stderr, " \"dev.\" are the devices to share with Windows\n"); + fprintf(stderr, " eg /dev/sdb1\n"); + fprintf(stderr, " the devices should not be mounted\n"); + fprintf(stderr, " the Windows system partition should be named first\n"); + } else + if (getuid()) { + fprintf(stderr, "\nSorry, only root can start usermap\n"); + err = true; + } +#endif + return (!err); +} + +STATIC boolean process(int argc, char *argv[]) +{ + boolean ok; + int xarg; + int targ; + + firstmapping = (struct MAPPING *)NULL; + lastmapping = (struct MAPPING *)NULL; + ok = true; +#ifdef WIN32 + for (xarg=1; (xarg 2 ? 2 : 1); + if (!outputmap(argv[targ],MAPDIR)) { + printf("Trying to write file on root directory\n"); + if (outputmap(argv[targ],(const char*)NULL)) { + printf("\nNote : you will have to move the file to directory \"%s\" on Linux\n", + MAPDIR); + } else + ok = false; + } else + ok = false; + } else + ok = false; + return (ok); +} + +int main(int argc, char *argv[]) +{ + boolean ok; + boolean silent; + + silent = !isatty(1); + if (!silent) + welcome(); + if (checkoptions(argc, argv, silent)) { +#ifdef WIN32 + loginname(silent); + if (silent) + ok = minimal(currentsid); + else + ok = process(argc, argv); +#else + if (open_security_api()) { + ok = process(argc,argv); + if (!close_security_api()) ok = false; + } +#endif + } else + ok = false; + if (!ok) + exit(1); + return (0); +}