--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/commands/sudo/sudo.cpp Wed Jun 23 15:52:26 2010 +0100
@@ -0,0 +1,537 @@
+// sudo.cpp
+//
+// Copyright (c) 2008 - 2010 Accenture. All rights reserved.
+// This component and the accompanying materials are made available
+// under the terms of the "Eclipse Public License v1.0"
+// which accompanies this distribution, and is available
+// at the URL "http://www.eclipse.org/legal/epl-v10.html".
+//
+// Initial Contributors:
+// Accenture - Initial contribution
+//
+
+#define __INCLUDE_CAPABILITY_NAMES__
+#include <fshell/ioutils.h>
+#include <f32image.h>
+#include <fshell/memoryaccesscmd.h>
+#include <e32rom.h>
+
+using namespace IoUtils;
+
+class CCmdSudo : public CMemoryAccessCommandBase
+ {
+public:
+ static CCommandBase* NewLC();
+ ~CCmdSudo();
+private:
+ CCmdSudo();
+ static void DeleteModifiedBinary(TAny* aSelf);
+ void DeleteModifiedBinary();
+ TBool FileExists(const TDesC& aFileName);
+ void FixupExeInMemoryL(RProcess& aProcess);
+ void CalculateCaps();
+
+ void FindExeL();
+ void CopyExeLC();
+ void FixupExeL();
+ void RunExeL();
+ void FixupCoreExeLC();
+
+private: // From CCommandBase.
+ virtual const TDesC& Name() const;
+ virtual void DoRunL();
+ virtual void ArgumentsL(RCommandArgumentList& aArguments);
+ virtual void OptionsL(RCommandOptionList& aOptions);
+
+private:
+ HBufC* iCmd;
+ HBufC* iArgs;
+ RPointerArray<HBufC> iAdd;
+ RPointerArray<HBufC> iRemove;
+ TCapabilitySet iCapsToAdd;
+ TCapabilitySet iCapsToRemove;
+ TUint iSid;
+ TUint iVid;
+ TUint iHeapMin;
+ TUint iHeapMax;
+ TUint iStackSize;
+ TInt iProcessPriority;
+
+ TBool iKeep;
+ TBool iWait;
+ TBool iChangeBinaryOnDisk;
+ TBool iFileIsInCore;
+
+ TFileName iPath;
+ TFileName iNewPath;
+ CArrayPtrFlat<HBufC>* iPathsToCleanup;
+ // following 2 are temporaries needed during CopyExeLC
+ TFileName iTempSrc;
+ TFileName iTempDest;
+ };
+
+
+CCommandBase* CCmdSudo::NewLC()
+ {
+ CCmdSudo* self = new(ELeave) CCmdSudo();
+ CleanupStack::PushL(self);
+ self->BaseConstructL();
+ return self;
+ }
+
+CCmdSudo::~CCmdSudo()
+ {
+ delete iCmd;
+ delete iArgs;
+ iAdd.ResetAndDestroy();
+ iRemove.ResetAndDestroy();
+
+ if (iPathsToCleanup)
+ {
+ for (TInt i = 0; i < iPathsToCleanup->Count(); i++)
+ {
+ HBufC* file = (*iPathsToCleanup)[i];
+ //Printf(_L("Deleting file %S (not really)\n"), file);
+ Fs().Delete(*file);
+ delete file;
+ }
+ }
+ delete iPathsToCleanup;
+ }
+
+CCmdSudo::CCmdSudo()
+ {
+ }
+
+TCapability CapabilityFromString(const TDesC& aName)
+ {
+ TBuf<32> cap;
+ for (TInt i = 0; i < ECapability_Limit; i++)
+ {
+ cap.Copy(TPtrC8((TUint8*)CapabilityNames[i]));
+ if (aName.CompareF(cap) == 0)
+ {
+ return (TCapability)i;
+ }
+ }
+ return ECapability_None;
+ }
+
+void CCmdSudo::DoRunL()
+ {
+ iPathsToCleanup = new(ELeave) CArrayPtrFlat<HBufC>(8);
+ CalculateCaps();
+ FindExeL();
+ if (iChangeBinaryOnDisk)
+ {
+#ifndef __WINS__
+ // Don't try and use IsFileInRom on WINSCW, the emulator makes a mess of it and anyway, shadowing isn't supported so it being in the core is irrelevant
+ iFileIsInCore = FsL().IsFileInRom(iPath) != NULL;
+#endif
+ if (iFileIsInCore)
+ {
+ // Things in core have to be handled differently
+ iNewPath = iPath; // We don't call CopyExeLC so something has to set this
+ FixupCoreExeLC();
+ }
+ else
+ {
+ CopyExeLC();
+ FixupExeL();
+ }
+ }
+ else
+ {
+ if (iHeapMin || iHeapMax || iStackSize) LeaveIfErr(KErrArgument, _L("Heap or stack sizes cannot be modified unless you specify --disk"));
+ if (iKeep) LeaveIfErr(KErrArgument, _L("--keep option makes no sense if --disk isn't specified."));
+
+ CleanupStack::PushL((CBase*)NULL); // RunExeL expects a cleanup item for DeleteModifiedBinary, which isn't necessary when --disk isn't specified
+ // Everything else is taken care of from RunExeL
+ }
+ RunExeL();
+ }
+
+void CCmdSudo::FindExeL()
+ {
+ if (!iChangeBinaryOnDisk)
+ {
+ // If we're not changing on disk, it's fine to just use the path as-is and let RProcess::Create sort it out
+ iPath = *iCmd;
+ iNewPath = iPath;
+ return;
+ }
+
+#ifdef __WINS__
+ PrintWarning(_L("On WINS, exe-name must be a complete path to a E32 exe"));
+ iPath = *iCmd;
+#else
+
+ // We can't reliably use RProcess::Create then FileName because the reason we're calling sudo may be because
+ // the capabilities don't allow it to load.
+ _LIT(KExe, ".exe");
+ _LIT(KSysBin, "\\sys\\bin\\");
+ iPath = *iCmd;
+ if (iPath.Right(KExe().Length()).CompareF(KExe) != 0)
+ {
+ iPath.Append(KExe);
+ }
+ TParsePtrC parse(iPath);
+ if (!parse.PathPresent())
+ {
+ TFindFile find(FsL());
+ TInt found = find.FindByDir(iPath, KSysBin);
+ LeaveIfErr(found, _L("Couldn't locate file %S"), &iPath);
+ iPath = find.File();
+ }
+#endif
+ }
+
+void CCmdSudo::CopyExeLC()
+ {
+ iNewPath = iPath;
+ iNewPath[0] = 'c'; // Has to be on C otherwise the loader performs hash checks
+ iNewPath.Append(_L(".sudoed.exe"));
+
+ CleanupStack::PushL(TCleanupItem(&CCmdSudo::DeleteModifiedBinary, this));
+
+ TInt err = FsL().MkDirAll(iNewPath); // In case C:\sys\bin doesn't exist yet
+ if (err && err != KErrAlreadyExists)
+ {
+ LeaveIfErr(err, _L("Couldn't create C:\\sys\\bin"));
+ }
+
+ CFileMan* fm = CFileMan::NewL(Fs());
+ CleanupStack::PushL(fm);
+
+ LeaveIfErr(fm->Copy(iPath, iNewPath), _L("Couldn't copy file from %S to %S"), &iPath, &iNewPath);
+ // Clear the read-only bit in the case where we've copied from Z drive
+ LeaveIfErr(Fs().SetAtt(iNewPath, 0, KEntryAttReadOnly), _L("Couldn't unset read-only flag"));
+
+ /* TODO this code looks like it should work but doesn't. Something in cone or similar is not behaving the way it looks like it should.
+
+ // In case it's an app which will rely on having its main rsc and its mbm file on the same drive as it, copy them over too
+ // (Damn cone for not searching drives or working relative to the reg rsc...)
+ TParsePtrC parse(iPath);
+ TPtrC exename = parse.Name();
+
+ _LIT(KResFmt, "%c:\\Resource\\Apps\\%S.rsc");
+ _LIT(KResDestFmt, "C:\\Resource\\Apps\\%S.sudoed.rsc");
+ iTempSrc.Format(KResFmt, iPath[0], &exename);
+ iTempDest.Format(KResDestFmt, &exename);
+
+ iPathsToCleanup->SetReserveL(iPathsToCleanup->Count() + 2);
+ iPathsToCleanup->AppendL(iTempDest.AllocL());
+ LeaveIfErr(fm->Copy(iTempSrc, iTempDest), _L("Couldn't copy ancillary file %S to %S"), &iTempSrc, &iTempDest);
+ LeaveIfErr(Fs().SetAtt(iTempDest, 0, KEntryAttReadOnly), _L("Couldn't unset read-only flag of %S"), &iTempDest);
+
+ _LIT(KMbmFmt, "%c:\\Resource\\Apps\\%S.mbm");
+ iTempSrc.Format(KMbmFmt, iPath[0], &exename);
+ iTempDest.Format(KMbmFmt, 'c', &exename);
+ if (!FileExists(iTempDest))
+ {
+ iPathsToCleanup->AppendL(iTempDest.AllocL());
+ LeaveIfErr(fm->Copy(iTempSrc, iTempDest), _L("Couldn't copy ancillary file %S to %S"), &iTempSrc, &iTempDest);
+ LeaveIfErr(Fs().SetAtt(iTempDest, 0, KEntryAttReadOnly), _L("Couldn't unset read-only flag of %S"), &iTempDest);
+ }
+ */
+ CleanupStack::PopAndDestroy(fm);
+ }
+
+void CCmdSudo::FixupExeL()
+ {
+ // Now fix up the capabilities or other stuff
+ RFile file;
+ CleanupClosePushL(file);
+ LeaveIfErr(file.Open(Fs(), iNewPath, EFileWrite|EFileStream|EFileShareAny), _L("Couldn't open file"));
+
+ E32ImageHeaderV* imageHeader=new(ELeave)E32ImageHeaderV;
+ CleanupStack::PushL(imageHeader);
+ TPckg<E32ImageHeaderV> ptr(*imageHeader);
+ LeaveIfErr(file.Read(ptr, sizeof(E32ImageHeaderV)), _L("Couldn't read E32ImageHeader"));
+
+ SSecurityInfo& secinfo = imageHeader->iS;
+ for (TInt i = 0; i < ECapability_Limit; i++)
+ {
+ TCapability cap = (TCapability)i;
+ if (iCapsToAdd.HasCapability(cap)) secinfo.iCaps.AddCapability(cap);
+ if (iCapsToRemove.HasCapability(cap)) reinterpret_cast<TCapabilitySet*>(&secinfo.iCaps)->RemoveCapability(cap);
+ }
+
+ if (iOptions.IsPresent(&iSid))
+ {
+ secinfo.iSecureId = iSid;
+ }
+ if (iOptions.IsPresent(&iVid))
+ {
+ secinfo.iVendorId = iVid;
+ }
+ if (iHeapMin)
+ {
+ imageHeader->iHeapSizeMin = iHeapMin;
+ }
+ if (iHeapMax)
+ {
+ imageHeader->iHeapSizeMax = iHeapMax;
+ }
+ if (iStackSize)
+ {
+ imageHeader->iStackSize = iStackSize;
+ }
+ if (iProcessPriority)
+ {
+ imageHeader->iProcessPriority = iProcessPriority;
+ }
+
+ // Update e32 checksum
+ imageHeader->iHeaderCrc = KImageCrcInitialiser;
+ TUint32 crc = 0;
+ Mem::Crc32(crc, imageHeader, imageHeader->TotalSize());
+ imageHeader->iHeaderCrc = crc;
+
+ LeaveIfErr(file.Write(0, ptr), _L("Couldn't write updated header back to file"));
+ CleanupStack::PopAndDestroy(2, &file); // imageHeader, file
+ }
+
+void CCmdSudo::RunExeL()
+ {
+ // Now actually execute it
+#ifdef __WINS__
+ if (iChangeBinaryOnDisk)
+ {
+ PrintWarning(_L("Updated file written to %S. Not actually executing it."), &iNewPath);
+ CleanupStack::Pop(); // DeleteModifiedBinary
+ return;
+ }
+#endif
+
+ RChildProcess childProcess;
+ TRAPL(childProcess.CreateL(iNewPath, iArgs ? *iArgs : KNullDesC(), IoSession(), Stdin(), Stdout(), Stderr(), Env()), _L("Failed to execute %S"), &iNewPath);
+ if (iKeep)
+ {
+ Printf(_L("Executing %S...\r\n"), &iNewPath);
+ CleanupStack::Pop(); // Don't delete if user asked for --keep
+ }
+ else
+ {
+ CleanupStack::PopAndDestroy(); // DeleteModifiedBinary - remove the binary before we actually start running it, so it is guaranteed cleaned up even if the user kills us with ctrl-c
+ }
+ if (!iChangeBinaryOnDisk)
+ {
+ // Time to get memaccess involved
+ TRAPD(err, FixupExeInMemoryL(childProcess.Process()));
+ if (err)
+ {
+ childProcess.Process().Kill(err);
+ childProcess.Close();
+ User::Leave(err);
+ }
+ }
+
+ if (iWait)
+ {
+ Printf(_L("Process is created but not yet resumed. Press a key to continue...\r\n"));
+ Stdin().ReadKey();
+ }
+
+ TRequestStatus stat;
+ childProcess.Run(stat);
+ User::WaitForRequest(stat);
+ TInt err = stat.Int();
+ childProcess.Close();
+ User::LeaveIfError(err); // This gets translated to our exe's return code I hope
+ }
+
+const TDesC& CCmdSudo::Name() const
+ {
+ _LIT(KName, "sudo");
+ return KName;
+ }
+
+void CCmdSudo::ArgumentsL(RCommandArgumentList& aArguments)
+ {
+ aArguments.AppendStringL(iCmd, _L("exe-name"));
+ aArguments.AppendStringL(iArgs, _L("arguments"));
+ }
+
+void CCmdSudo::OptionsL(RCommandOptionList& aOptions)
+ {
+ aOptions.AppendStringL(iAdd, _L("add-cap"));
+ aOptions.AppendStringL(iRemove, _L("remove-cap"));
+ aOptions.AppendUintL(iSid, _L("sid"));
+ aOptions.AppendUintL(iVid, _L("vid"));
+ aOptions.AppendUintL(iHeapMin, _L("heap-min"));
+ aOptions.AppendUintL(iHeapMax, _L("heap-max"));
+ aOptions.AppendUintL(iStackSize, _L("stack-size"));
+ aOptions.AppendIntL(iProcessPriority, _L("process-priority"));
+ aOptions.AppendBoolL(iKeep, _L("keep"));
+ aOptions.AppendBoolL(iChangeBinaryOnDisk, _L("disk"));
+ aOptions.AppendBoolL(iWait, _L("wait"));
+ }
+
+void CCmdSudo::DeleteModifiedBinary(TAny* aSelf)
+ {
+ CCmdSudo* self = static_cast<CCmdSudo*>(aSelf);
+ self->DeleteModifiedBinary();
+ }
+
+void CCmdSudo::DeleteModifiedBinary()
+ {
+ if (iFileIsInCore)
+ {
+#ifdef FSHELL_MEMORY_ACCESS_SUPPORT
+ iMemAccess.FreeShadowMemory((TLinAddr)Fs().IsFileInRom(iPath), sizeof(TRomImageHeader));
+#endif
+ }
+ else
+ {
+ TInt err = Fs().Delete(iNewPath);
+ if (err && err != KErrNotFound && err != KErrPathNotFound) PrintError(err, _L("Couldn't delete file %S"), &iNewPath);
+ }
+ }
+
+EXE_BOILER_PLATE(CCmdSudo)
+
+TBool CCmdSudo::FileExists(const TDesC& aFileName)
+ {
+ TEntry entry;
+ return Fs().Entry(aFileName, entry) == KErrNone;
+ }
+
+void CCmdSudo::FixupExeInMemoryL(RProcess& aProcess)
+ {
+#ifdef FSHELL_MEMORY_ACCESS_SUPPORT
+ LoadMemoryAccessL();
+ TProcessProperties prop;
+ prop.iCapsToAdd = iCapsToAdd;
+ prop.iCapsToRemove = iCapsToRemove;
+ prop.iProcessPriority = iProcessPriority;
+ if (iOptions.IsPresent(&iSid))
+ {
+ prop.iSid = iSid;
+ }
+ if (iOptions.IsPresent(&iVid))
+ {
+ prop.iVid = iVid;
+ }
+ LeaveIfErr(iMemAccess.SetProcessProperties(aProcess, prop), _L("Couldn't set process properties using memoryaccess"));
+#else
+ (void)aProcess;
+ LeaveIfErr(KErrNotSupported, _L("Can't fixup process in memory without MemoryAccess, try the --disk option instead"));
+#endif
+ }
+
+void CCmdSudo::CalculateCaps()
+ {
+ _LIT(KAll, "All");
+ // Add caps
+ iCapsToAdd.SetEmpty();
+ for (TInt i = 0; i < iAdd.Count(); i++)
+ {
+ const TDesC& capName = *iAdd[i];
+ TCapability cap = CapabilityFromString(capName);
+ if (cap == ECapability_None)
+ {
+ if (capName.CompareF(KAll) == 0)
+ {
+ // The pseudo-cap 'All'
+ iCapsToAdd.SetAllSupported();
+ }
+ else
+ {
+ PrintWarning(_L("Couldn't understand capability name %S"), &capName);
+ }
+ }
+ else
+ {
+ iCapsToAdd.AddCapability(cap);
+ }
+ }
+ // Remove caps
+ iCapsToRemove.SetEmpty();
+ for (TInt i = 0; i < iRemove.Count(); i++)
+ {
+ const TDesC& capName = *iRemove[i];
+ TCapability cap = CapabilityFromString(capName);
+ if (cap == ECapability_None)
+ {
+ if (capName.CompareF(KAll) == 0)
+ {
+ // The pseudo-cap 'All'
+ iCapsToRemove.SetAllSupported();
+ }
+ else
+ {
+ PrintWarning(_L("Couldn't understand capability name %S"), &capName);
+ }
+ }
+ else
+ {
+ iCapsToRemove.AddCapability(cap);
+ }
+ }
+
+ TBool noOptions = (iAdd.Count() == 0) && (iRemove.Count() == 0) && !iOptions.IsPresent(&iSid) && !iOptions.IsPresent(&iVid) && (iHeapMin == 0) && (iHeapMax == 0) && (iStackSize == 0) && (iProcessPriority == 0);
+
+ if (noOptions)
+ {
+ // Default to All -TCB
+ iCapsToAdd.SetAllSupported();
+ iCapsToRemove.AddCapability(ECapabilityTCB);
+ }
+ }
+
+void CCmdSudo::FixupCoreExeLC()
+ {
+#ifdef FSHELL_MEMORY_ACCESS_SUPPORT
+ LoadMemoryAccessL();
+
+ const TRomImageHeader* imageHeader = (const TRomImageHeader*)FsL().IsFileInRom(iPath);
+ if (!imageHeader) LeaveIfErr(KErrNotFound, _L("In FixupCoreExeLC but IsFileInRom returned null??"));
+
+ SCapabilitySet caps = imageHeader->iS.iCaps;
+ for (TInt i = 0; i < ECapability_Limit; i++)
+ {
+ TCapability cap = (TCapability)i;
+ if (iCapsToAdd.HasCapability(cap)) caps.AddCapability(cap);
+ if (iCapsToRemove.HasCapability(cap)) reinterpret_cast<TCapabilitySet*>(&caps)->RemoveCapability(cap);
+ }
+ TPckg<SCapabilitySet> pkg(caps);
+ LeaveIfErr(iMemAccess.WriteShadowMemory((TLinAddr)&imageHeader->iS.iCaps, pkg), _L("Couldn't write shadow memory for caps"));
+
+ if (iOptions.IsPresent(&iSid))
+ {
+ TPckg<TUint> pkg(iSid);
+ LeaveIfErr(iMemAccess.WriteShadowMemory((TLinAddr)&imageHeader->iS.iSecureId, pkg), _L("Couldn't write shadow memory for sid"));
+ }
+ if (iOptions.IsPresent(&iVid))
+ {
+ TPckg<TUint> pkg(iVid);
+ LeaveIfErr(iMemAccess.WriteShadowMemory((TLinAddr)&imageHeader->iS.iVendorId, pkg), _L("Couldn't write shadow memory for sid"));
+ }
+ if (iHeapMin)
+ {
+ TPckg<TUint> pkg(iHeapMin);
+ LeaveIfErr(iMemAccess.WriteShadowMemory((TLinAddr)&imageHeader->iHeapSizeMin, pkg), _L("Couldn't write shadow memory for iHeapSizeMin"));
+ }
+ if (iHeapMax)
+ {
+ TPckg<TUint> pkg(iHeapMax);
+ LeaveIfErr(iMemAccess.WriteShadowMemory((TLinAddr)&imageHeader->iHeapSizeMax, pkg), _L("Couldn't write shadow memory for iHeapSizeMax"));
+ }
+ if (iStackSize)
+ {
+ TPckg<TUint> pkg(iStackSize);
+ LeaveIfErr(iMemAccess.WriteShadowMemory((TLinAddr)&imageHeader->iStackSize, pkg), _L("Couldn't write shadow memory for iStackSize"));
+ }
+ if (iProcessPriority)
+ {
+ TPckg<TUint> pkg(iProcessPriority);
+ LeaveIfErr(iMemAccess.WriteShadowMemory((TLinAddr)&imageHeader->iPriority, pkg), _L("Couldn't write shadow memory for iProcessPriority"));
+ }
+
+ // DeleteModifiedBinary does the right thing in the case of shadowing
+ CleanupStack::PushL(TCleanupItem(&CCmdSudo::DeleteModifiedBinary, this));
+#else
+ LeaveIfErr(KErrNotSupported, _L("Can't fixup an exe in Core image without memoryaccess"));
+#endif
+ }