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