diff --git a/src/.msvc/rufus.vcxproj b/src/.msvc/rufus.vcxproj index 3212fddf..5992d19d 100644 --- a/src/.msvc/rufus.vcxproj +++ b/src/.msvc/rufus.vcxproj @@ -91,7 +91,7 @@ 4091;28252;28253;%(DisableSpecificWarnings) - setupapi.lib;comctl32.lib;wininet.lib;shlwapi.lib;%(AdditionalDependencies) + setupapi.lib;comctl32.lib;wininet.lib;shlwapi.lib;crypt32.lib;wintrust.lib;%(AdditionalDependencies) RequireAdministrator true Windows @@ -119,7 +119,7 @@ 4091;28252;28253;%(DisableSpecificWarnings) - setupapi.lib;comctl32.lib;wininet.lib;shlwapi.lib;%(AdditionalDependencies) + setupapi.lib;comctl32.lib;wininet.lib;shlwapi.lib;crypt32.lib;wintrust.lib;%(AdditionalDependencies) RequireAdministrator true Windows @@ -143,7 +143,7 @@ 4091;28252;28253;%(DisableSpecificWarnings) - setupapi.lib;comctl32.lib;wininet.lib;shlwapi.lib;%(AdditionalDependencies) + setupapi.lib;comctl32.lib;wininet.lib;shlwapi.lib;crypt32.lib;wintrust.lib;%(AdditionalDependencies) RequireAdministrator false Windows @@ -170,7 +170,7 @@ 4091;28252;28253;%(DisableSpecificWarnings) - setupapi.lib;comctl32.lib;wininet.lib;shlwapi.lib;%(AdditionalDependencies) + setupapi.lib;comctl32.lib;wininet.lib;shlwapi.lib;crypt32.lib;wintrust.lib;%(AdditionalDependencies) RequireAdministrator false Windows @@ -192,6 +192,7 @@ + diff --git a/src/.msvc/rufus.vcxproj.filters b/src/.msvc/rufus.vcxproj.filters index b7a5d182..bd16ab97 100644 --- a/src/.msvc/rufus.vcxproj.filters +++ b/src/.msvc/rufus.vcxproj.filters @@ -72,6 +72,9 @@ Source Files + + Source Files + diff --git a/src/.msvc/rufus_sources b/src/.msvc/rufus_sources index 68dd6d5e..f14f1178 100644 --- a/src/.msvc/rufus_sources +++ b/src/.msvc/rufus_sources @@ -19,6 +19,8 @@ TARGETLIBS=$(SDK_LIB_PATH)\kernel32.lib \ $(SDK_LIB_PATH)\shell32.lib \ $(SDK_LIB_PATH)\wininet.lib \ $(SDK_LIB_PATH)\shlwapi.lib \ + $(SDK_LIB_PATH)\crypt32.lib \ + $(SDK_LIB_PATH)\wintrust.lib \ .\bled\bled.lib \ .\ms-sys\ms-sys.lib \ .\syslinux\libfat\libfat.lib \ @@ -39,14 +41,15 @@ SOURCES=badblocks.c \ format.c \ icon.c \ iso.c \ - stdfn.c \ - stdio.c \ - stdlg.c \ localization.c \ net.c \ parser.c \ + pki.c \ rufus.c \ smart.c \ + stdfn.c \ + stdio.c \ + stdlg.c \ syslinux.c \ usb.c \ vhd.c \ diff --git a/src/Makefile.am b/src/Makefile.am index d0c90327..0833d07e 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -10,9 +10,9 @@ AM_V_WINDRES = $(AM_V_WINDRES_$(V)) %_rc.o: %.rc ../res/localization/embedded.loc $(AM_V_WINDRES) $(AM_RCFLAGS) -i $< -o $@ -rufus_SOURCES = badblocks.c checksum.c dos.c dos_locale.c drive.c format.c icon.c iso.c localization.c net.c smart.c \ - stdfn.c stdio.c stdlg.c rufus.c parser.c syslinux.c usb.c vhd.c +rufus_SOURCES = badblocks.c checksum.c dos.c dos_locale.c drive.c format.c icon.c iso.c localization.c net.c parser.c \ + pki.c rufus.c smart.c stdfn.c stdio.c stdlg.c syslinux.c usb.c vhd.c rufus_CFLAGS = -I./ms-sys/inc -I./syslinux/libfat -I./syslinux/libinstaller -I./libcdio $(AM_CFLAGS) rufus_LDFLAGS = $(AM_LDFLAGS) -mwindows rufus_LDADD = rufus_rc.o bled/libbled.a ms-sys/libmssys.a syslinux/libfat/libfat.a syslinux/libinstaller/libinstaller.a \ - libcdio/iso9660/libiso9660.a libcdio/udf/libudf.a libcdio/driver/libdriver.a -lsetupapi -lole32 -lgdi32 -lwininet -lshlwapi + libcdio/iso9660/libiso9660.a libcdio/udf/libudf.a libcdio/driver/libdriver.a -lsetupapi -lole32 -lgdi32 -lwininet -lshlwapi -lcrypt32 -lwintrust diff --git a/src/Makefile.in b/src/Makefile.in index e52be8fd..dd429125 100644 --- a/src/Makefile.in +++ b/src/Makefile.in @@ -92,11 +92,11 @@ am_rufus_OBJECTS = rufus-badblocks.$(OBJEXT) rufus-checksum.$(OBJEXT) \ rufus-drive.$(OBJEXT) rufus-format.$(OBJEXT) \ rufus-icon.$(OBJEXT) rufus-iso.$(OBJEXT) \ rufus-localization.$(OBJEXT) rufus-net.$(OBJEXT) \ - rufus-smart.$(OBJEXT) rufus-stdfn.$(OBJEXT) \ - rufus-stdio.$(OBJEXT) rufus-stdlg.$(OBJEXT) \ - rufus-rufus.$(OBJEXT) rufus-parser.$(OBJEXT) \ - rufus-syslinux.$(OBJEXT) rufus-usb.$(OBJEXT) \ - rufus-vhd.$(OBJEXT) + rufus-parser.$(OBJEXT) rufus-pki.$(OBJEXT) \ + rufus-rufus.$(OBJEXT) rufus-smart.$(OBJEXT) \ + rufus-stdfn.$(OBJEXT) rufus-stdio.$(OBJEXT) \ + rufus-stdlg.$(OBJEXT) rufus-syslinux.$(OBJEXT) \ + rufus-usb.$(OBJEXT) rufus-vhd.$(OBJEXT) rufus_OBJECTS = $(am_rufus_OBJECTS) rufus_DEPENDENCIES = rufus_rc.o bled/libbled.a ms-sys/libmssys.a \ syslinux/libfat/libfat.a syslinux/libinstaller/libinstaller.a \ @@ -270,13 +270,13 @@ AM_V_WINDRES_0 = @echo " RC $@";$(WINDRES) AM_V_WINDRES_1 = $(WINDRES) AM_V_WINDRES_ = $(AM_V_WINDRES_$(AM_DEFAULT_VERBOSITY)) AM_V_WINDRES = $(AM_V_WINDRES_$(V)) -rufus_SOURCES = badblocks.c checksum.c dos.c dos_locale.c drive.c format.c icon.c iso.c localization.c net.c smart.c \ - stdfn.c stdio.c stdlg.c rufus.c parser.c syslinux.c usb.c vhd.c +rufus_SOURCES = badblocks.c checksum.c dos.c dos_locale.c drive.c format.c icon.c iso.c localization.c net.c parser.c \ + pki.c rufus.c smart.c stdfn.c stdio.c stdlg.c syslinux.c usb.c vhd.c rufus_CFLAGS = -I./ms-sys/inc -I./syslinux/libfat -I./syslinux/libinstaller -I./libcdio $(AM_CFLAGS) rufus_LDFLAGS = $(AM_LDFLAGS) -mwindows rufus_LDADD = rufus_rc.o bled/libbled.a ms-sys/libmssys.a syslinux/libfat/libfat.a syslinux/libinstaller/libinstaller.a \ - libcdio/iso9660/libiso9660.a libcdio/udf/libudf.a libcdio/driver/libdriver.a -lsetupapi -lole32 -lgdi32 -lwininet -lshlwapi + libcdio/iso9660/libiso9660.a libcdio/udf/libudf.a libcdio/driver/libdriver.a -lsetupapi -lole32 -lgdi32 -lwininet -lshlwapi -lcrypt32 -lwintrust all: all-recursive @@ -392,6 +392,24 @@ rufus-net.o: net.c rufus-net.obj: net.c $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(rufus_CFLAGS) $(CFLAGS) -c -o rufus-net.obj `if test -f 'net.c'; then $(CYGPATH_W) 'net.c'; else $(CYGPATH_W) '$(srcdir)/net.c'; fi` +rufus-parser.o: parser.c + $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(rufus_CFLAGS) $(CFLAGS) -c -o rufus-parser.o `test -f 'parser.c' || echo '$(srcdir)/'`parser.c + +rufus-parser.obj: parser.c + $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(rufus_CFLAGS) $(CFLAGS) -c -o rufus-parser.obj `if test -f 'parser.c'; then $(CYGPATH_W) 'parser.c'; else $(CYGPATH_W) '$(srcdir)/parser.c'; fi` + +rufus-pki.o: pki.c + $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(rufus_CFLAGS) $(CFLAGS) -c -o rufus-pki.o `test -f 'pki.c' || echo '$(srcdir)/'`pki.c + +rufus-pki.obj: pki.c + $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(rufus_CFLAGS) $(CFLAGS) -c -o rufus-pki.obj `if test -f 'pki.c'; then $(CYGPATH_W) 'pki.c'; else $(CYGPATH_W) '$(srcdir)/pki.c'; fi` + +rufus-rufus.o: rufus.c + $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(rufus_CFLAGS) $(CFLAGS) -c -o rufus-rufus.o `test -f 'rufus.c' || echo '$(srcdir)/'`rufus.c + +rufus-rufus.obj: rufus.c + $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(rufus_CFLAGS) $(CFLAGS) -c -o rufus-rufus.obj `if test -f 'rufus.c'; then $(CYGPATH_W) 'rufus.c'; else $(CYGPATH_W) '$(srcdir)/rufus.c'; fi` + rufus-smart.o: smart.c $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(rufus_CFLAGS) $(CFLAGS) -c -o rufus-smart.o `test -f 'smart.c' || echo '$(srcdir)/'`smart.c @@ -416,18 +434,6 @@ rufus-stdlg.o: stdlg.c rufus-stdlg.obj: stdlg.c $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(rufus_CFLAGS) $(CFLAGS) -c -o rufus-stdlg.obj `if test -f 'stdlg.c'; then $(CYGPATH_W) 'stdlg.c'; else $(CYGPATH_W) '$(srcdir)/stdlg.c'; fi` -rufus-rufus.o: rufus.c - $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(rufus_CFLAGS) $(CFLAGS) -c -o rufus-rufus.o `test -f 'rufus.c' || echo '$(srcdir)/'`rufus.c - -rufus-rufus.obj: rufus.c - $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(rufus_CFLAGS) $(CFLAGS) -c -o rufus-rufus.obj `if test -f 'rufus.c'; then $(CYGPATH_W) 'rufus.c'; else $(CYGPATH_W) '$(srcdir)/rufus.c'; fi` - -rufus-parser.o: parser.c - $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(rufus_CFLAGS) $(CFLAGS) -c -o rufus-parser.o `test -f 'parser.c' || echo '$(srcdir)/'`parser.c - -rufus-parser.obj: parser.c - $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(rufus_CFLAGS) $(CFLAGS) -c -o rufus-parser.obj `if test -f 'parser.c'; then $(CYGPATH_W) 'parser.c'; else $(CYGPATH_W) '$(srcdir)/parser.c'; fi` - rufus-syslinux.o: syslinux.c $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(rufus_CFLAGS) $(CFLAGS) -c -o rufus-syslinux.o `test -f 'syslinux.c' || echo '$(srcdir)/'`syslinux.c diff --git a/src/pki.c b/src/pki.c new file mode 100644 index 00000000..0b4b0487 --- /dev/null +++ b/src/pki.c @@ -0,0 +1,164 @@ +/* + * Rufus: The Reliable USB Formatting Utility + * PKI functions (code signing, etc.) + * Copyright © 2015 Pete Batard + * + * 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 3 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. If not, see . + */ + +/* Memory leaks detection - define _CRTDBG_MAP_ALLOC as preprocessor macro */ +#ifdef _CRTDBG_MAP_ALLOC +#include +#include +#endif + +#include +#include +#include +#include + +#include "rufus.h" +#include "msapi_utf8.h" + +#define ENCODING (X509_ASN_ENCODING | PKCS_7_ASN_ENCODING) + +// Signatures names we accept (may be suffixed, but signature must start with one of those) +const char* valid_cert_names[] = { "Akeo Consulting", "Akeo Systems", "Pete Batard" }; + +typedef struct { + LPWSTR lpszProgramName; + LPWSTR lpszPublisherLink; + LPWSTR lpszMoreInfoLink; +} SPROG_PUBLISHERINFO, *PSPROG_PUBLISHERINFO; + +// Mostly from https://support.microsoft.com/en-us/kb/323809 +BOOL CheckSignatureAttributes(const char* path) +{ + BOOL r = FALSE; + HCERTSTORE hStore = NULL; + HCRYPTMSG hMsg = NULL; + PCCERT_CONTEXT pCertContext = NULL; + DWORD dwEncoding, dwContentType, dwFormatType, dwSubjectSize; + PCMSG_SIGNER_INFO pSignerInfo = NULL; + PCMSG_SIGNER_INFO pCounterSignerInfo = NULL; + DWORD dwSignerInfo = 0; + CERT_INFO CertInfo = { 0 }; + SPROG_PUBLISHERINFO ProgPubInfo = { 0 }; + wchar_t *szFileName = utf8_to_wchar(path); + char szSubjectName[128]; + int i; + + // Get message handle and store handle from the signed file. + r = CryptQueryObject(CERT_QUERY_OBJECT_FILE, szFileName, + CERT_QUERY_CONTENT_FLAG_PKCS7_SIGNED_EMBED, CERT_QUERY_FORMAT_FLAG_BINARY, + 0, &dwEncoding, &dwContentType, &dwFormatType, &hStore, &hMsg, NULL); + if (!r) { + uprintf("PKI: Failed to get store handle for '%s': %s", path, WindowsErrorString()); + goto out; + } + + // Get signer information size. + r = CryptMsgGetParam(hMsg, CMSG_SIGNER_INFO_PARAM, 0, NULL, &dwSignerInfo); + if (!r) { + uprintf("PKI: Failed to get signer size: %s", WindowsErrorString); + goto out; + } + + // Allocate memory for signer information. + pSignerInfo = (PCMSG_SIGNER_INFO)calloc(dwSignerInfo, 1); + if (!pSignerInfo) { + uprintf("PKI: Could not allocate memory for signer information"); + r = FALSE; + goto out; + } + + // Get Signer Information. + r = CryptMsgGetParam(hMsg, CMSG_SIGNER_INFO_PARAM, 0, (PVOID)pSignerInfo, &dwSignerInfo); + if (!r) { + uprintf("PKI: Failed to get signer information: %s", WindowsErrorString()); + goto out; + } + + // Search for the signer certificate in the temporary certificate store. + CertInfo.Issuer = pSignerInfo->Issuer; + CertInfo.SerialNumber = pSignerInfo->SerialNumber; + + pCertContext = CertFindCertificateInStore(hStore, ENCODING, 0, CERT_FIND_SUBJECT_CERT, (PVOID)&CertInfo, NULL); + if (!pCertContext) { + uprintf("PKI: Failed to locate signer certificate in temporary store: %s", WindowsErrorString()); + r = FALSE; + goto out; + } + + // Isolate the signing certificate subject name + dwSubjectSize = CertGetNameStringA(pCertContext, CERT_NAME_SIMPLE_DISPLAY_TYPE, 0, NULL, + szSubjectName, sizeof(szSubjectName)); + if (dwSubjectSize <= 1) { + uprintf("PKI: Failed to get Subject Name"); + r = FALSE; + goto out; + } + + uprintf("Executable is signed by '%s'", szSubjectName); + + // Now check the signature name. Make it specific enough (i.e. don't simply check for "Akeo") + // so that, besides hacking our server, it'll place an extra hurdle on any malicious entity + // into also fooling a C.A. to issue a certificate that passes our test. + for (i = 0; i < ARRAYSIZE(valid_cert_names); i++) { + r = (strncmp(szSubjectName, valid_cert_names[i], strlen(valid_cert_names[i])) == 0); + if (r) + break; + } + +out: + safe_free(szFileName); + safe_free(ProgPubInfo.lpszProgramName); + safe_free(ProgPubInfo.lpszPublisherLink); + safe_free(ProgPubInfo.lpszMoreInfoLink); + safe_free(pSignerInfo); + safe_free(pCounterSignerInfo); + if (pCertContext != NULL) + CertFreeCertificateContext(pCertContext); + if (hStore != NULL) + CertCloseStore(hStore, 0); + if (hMsg != NULL) + CryptMsgClose(hMsg); + return r; +} + +// From https://msdn.microsoft.com/en-us/library/windows/desktop/aa382384.aspx +LONG ValidateSignature(HWND hDlg, const char* path) +{ + LONG r; + WINTRUST_DATA trust_data = { 0 }; + WINTRUST_FILE_INFO trust_file = { 0 }; + GUID guid_generic_verify = // WINTRUST_ACTION_GENERIC_VERIFY_V2 + { 0xaac56b, 0xcd44, 0x11d0,{ 0x8c, 0xc2, 0x0, 0xc0, 0x4f, 0xc2, 0x95, 0xee } }; + wchar_t *szFileName = utf8_to_wchar(path); + + trust_file.cbStruct = sizeof(trust_file); + trust_file.pcwszFilePath = szFileName; + + trust_data.cbStruct = sizeof(trust_data); + trust_data.dwUIChoice = WTD_UI_ALL; + trust_data.fdwRevocationChecks = WTD_REVOKE_WHOLECHAIN; + trust_data.dwUnionChoice = WTD_CHOICE_FILE; + trust_data.pFile = &trust_file; + // 0x400 = WTD_MOTW for Windows 8.1 or later + trust_data.dwProvFlags = WTD_REVOCATION_CHECK_CHAIN | 0x400; + + r = WinVerifyTrust(NULL, &guid_generic_verify, &trust_data); + safe_free(szFileName); + return r; +} diff --git a/src/rufus.c b/src/rufus.c index 64a2db98..a21181d3 100644 --- a/src/rufus.c +++ b/src/rufus.c @@ -2054,6 +2054,8 @@ void SaveVHD(void) /* * Main dialog callback */ +extern BOOL CheckSignatureAttributes(const char* path); +extern LONG ValidateSignature(HWND hDlg, const char* path); static INT_PTR CALLBACK MainCallback(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) { static DWORD DeviceNum = 0, LastRefresh = 0; @@ -2272,6 +2274,9 @@ static INT_PTR CALLBACK MainCallback(HWND hDlg, UINT message, WPARAM wParam, LPA break; #ifdef RUFUS_TEST case IDC_TEST: + uprintf("CHK: %d", CheckSignatureAttributes("C:\\Downloads\\rufus-2.4.exe")); + SetLastError(ValidateSignature(hDlg, "C:\\Downloads\\rufus-2.4.exe")); + uprintf("VAL: %s", WindowsErrorString()); break; #endif case IDC_ADVANCED: diff --git a/src/rufus.h b/src/rufus.h index 8821227a..bb5f40cd 100644 --- a/src/rufus.h +++ b/src/rufus.h @@ -32,7 +32,7 @@ /* Program options */ #define RUFUS_DEBUG // print debug info to Debug facility /* Features not ready for prime time and that may *DESTROY* your data - USE AT YOUR OWN RISKS! */ -// #define RUFUS_TEST +#define RUFUS_TEST /* Languages for which translators are M.I.A. and that we could use help with */ #define LOST_TRANSLATORS { "ms-MY" } // NB: locales MUST be <= 5 chars /* Probability of getting the M.I.A. translator message. For more on this, see LostTranslatorCheck() */ diff --git a/src/rufus.rc b/src/rufus.rc index 12b658e8..73a4e0f5 100644 --- a/src/rufus.rc +++ b/src/rufus.rc @@ -32,7 +32,7 @@ LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL IDD_DIALOG DIALOGEX 12, 12, 242, 376 STYLE DS_SETFONT | DS_MODALFRAME | DS_CENTER | WS_MINIMIZEBOX | WS_POPUP | WS_CAPTION | WS_SYSMENU -CAPTION "Rufus 2.5.765" +CAPTION "Rufus 2.5.766" FONT 8, "Segoe UI Symbol", 400, 0, 0x0 BEGIN LTEXT "Device",IDS_DEVICE_TXT,9,6,200,8 @@ -319,8 +319,8 @@ END // VS_VERSION_INFO VERSIONINFO - FILEVERSION 2,5,765,0 - PRODUCTVERSION 2,5,765,0 + FILEVERSION 2,5,766,0 + PRODUCTVERSION 2,5,766,0 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L @@ -337,13 +337,13 @@ BEGIN BEGIN VALUE "CompanyName", "Akeo Consulting (http://akeo.ie)" VALUE "FileDescription", "Rufus" - VALUE "FileVersion", "2.5.765" + VALUE "FileVersion", "2.5.766" VALUE "InternalName", "Rufus" VALUE "LegalCopyright", "© 2011-2015 Pete Batard (GPL v3)" VALUE "LegalTrademarks", "http://www.gnu.org/copyleft/gpl.html" VALUE "OriginalFilename", "rufus.exe" VALUE "ProductName", "Rufus" - VALUE "ProductVersion", "2.5.765" + VALUE "ProductVersion", "2.5.766" END END BLOCK "VarFileInfo"