#include "filediskrep.h"
#include "bundlediskrep.h"
#include "cfmdiskrep.h"
-#include "foreigndiskrep.h"
+#include "slcrep.h"
namespace Security {
}
DiskRep::~DiskRep()
-{ /* virtual */ }
+{
+ CODESIGN_DISKREP_DESTROY(this);
+}
//
//
DiskRep::Writer *DiskRep::writer()
{
- MacOSError::throwMe(errSecCSBadObjectFormat);
+ MacOSError::throwMe(errSecCSUnimplemented);
+}
+
+
+void DiskRep::Writer::addDiscretionary(CodeDirectory::Builder &)
+{
+ // do nothing
}
// fine in ordinary use. If you happen to know what you're looking at
// (say, a bundle), then just create the suitable subclass of DiskRep directly.
// That's quite legal.
+// The optional context argument can provide additional information that guides the guess.
//
-DiskRep *DiskRep::bestGuess(const char *path)
+DiskRep *DiskRep::bestGuess(const char *path, const Context *ctx)
{
try {
- struct stat st;
- if (::stat(path, &st))
- UnixError::throwMe();
+ if (!(ctx && ctx->fileOnly)) {
+ struct stat st;
+ if (::stat(path, &st))
+ UnixError::throwMe();
+
+ // if it's a directory, assume it's a bundle
+ if ((st.st_mode & S_IFMT) == S_IFDIR) // directory - assume bundle
+ return new BundleDiskRep(path, ctx);
+
+ // see if it's the main executable of a recognized bundle
+ if (CFRef<CFURLRef> pathURL = makeCFURL(path))
+ if (CFRef<CFBundleRef> bundle = _CFBundleCreateWithExecutableURLIfMightBeBundle(NULL, pathURL))
+ return new BundleDiskRep(bundle, ctx);
+ }
- // if it's a directory, assume it's a bundle
- if ((st.st_mode & S_IFMT) == S_IFDIR) // directory - assume bundle
- return new BundleDiskRep(path);
-
- // see if it's the main executable of a recognized bundle
- if (CFRef<CFURLRef> pathURL = makeCFURL(path))
- if (CFRef<CFBundleRef> bundle = _CFBundleCreateWithExecutableURLIfMightBeBundle(NULL, pathURL))
- return new BundleDiskRep(bundle);
-
- // follow the file choosing rules
- return bestFileGuess(path);
+ // try the various single-file representations
+ AutoFileDesc fd(path, O_RDONLY);
+ if (MachORep::candidate(fd))
+ return new MachORep(path, ctx);
+ if (CFMDiskRep::candidate(fd))
+ return new CFMDiskRep(path);
+ if (DYLDCacheRep::candidate(fd))
+ return new DYLDCacheRep(path);
+
+ // ultimate fallback - the generic file representation
+ return new FileDiskRep(path);
+
} catch (const CommonError &error) {
switch (error.unixError()) {
case ENOENT:
}
-DiskRep *DiskRep::bestFileGuess(const char *path)
+DiskRep *DiskRep::bestFileGuess(const char *path, const Context *ctx)
{
- AutoFileDesc fd(path, O_RDONLY);
- if (MachORep::candidiate(fd))
- return new MachORep(path);
- if (CFMDiskRep::candidiate(fd))
- return new CFMDiskRep(path);
- if (ForeignDiskRep::candidate(fd))
- return new ForeignDiskRep(path);
+ Context dctx;
+ if (ctx)
+ dctx = *ctx;
+ dctx.fileOnly = true;
+ return bestGuess(path, &dctx);
+}
- return new FileDiskRep(path);
+
+//
+// Given a main executable known to be a Mach-O binary, and an offset into
+// the file of the actual architecture desired (of a Universal file),
+// produce a suitable MachORep.
+// This function does not consider non-MachO binaries. It does however handle
+// bundles with Mach-O main executables correctly.
+//
+DiskRep *DiskRep::bestGuess(const char *path, size_t archOffset)
+{
+ try {
+ // is it the main executable of a bundle?
+ if (CFRef<CFURLRef> pathURL = makeCFURL(path))
+ if (CFRef<CFBundleRef> bundle = _CFBundleCreateWithExecutableURLIfMightBeBundle(NULL, pathURL)) {
+ Context ctx; ctx.offset = archOffset;
+ return new BundleDiskRep(bundle, &ctx); // ask bundle to make bundle-with-MachO-at-offset
+ }
+ // else, must be a Mach-O binary
+ Context ctx; ctx.offset = archOffset;
+ return new MachORep(path, &ctx);
+ } catch (const CommonError &error) {
+ switch (error.unixError()) {
+ case ENOENT:
+ MacOSError::throwMe(errSecCSStaticCodeNotFound);
+ default:
+ throw;
+ }
+ }
}
return ""; // has no resources directory
}
-CFDictionaryRef DiskRep::defaultResourceRules()
-{
- return NULL; // none
-}
-
void DiskRep::adjustResources(ResourceBuilder &builder)
{
// do nothing
}
-const Requirements *DiskRep::defaultRequirements(const Architecture *)
-{
- return NULL; // none
-}
-
Universal *DiskRep::mainExecutableImage()
{
return NULL; // no Mach-O executable
}
-size_t DiskRep::pageSize()
-{
- return monolithicPageSize; // unpaged (monolithic)
-}
-
size_t DiskRep::signingBase()
{
return 0; // whole file (start at beginning)
}
+CFDictionaryRef DiskRep::defaultResourceRules(const SigningContext &)
+{
+ return NULL; // none
+}
+
+const Requirements *DiskRep::defaultRequirements(const Architecture *, const SigningContext &)
+{
+ return NULL; // none
+}
+
+size_t DiskRep::pageSize(const SigningContext &)
+{
+ return monolithicPageSize; // unpaged (monolithic)
+}
+
+
+//
+// Given some string (usually a pathname), derive a suggested signing identifier
+// in a canonical way (so there's some consistency).
+//
+// This is a heuristic. First we lop off any leading directories and final (non-numeric)
+// extension. Then we walk backwards, eliminating numeric extensions except the first one.
+// Thus, libfrotz7.3.5.dylib becomes libfrotz7, mumble.77.plugin becomes mumble.77,
+// and rumble.rb becomes rumble. This isn't perfect, but it ought to handle 98%+ of
+// the common varieties out there. Specify an explicit identifier for the oddballs.
+//
+// This is called by the various recommendedIdentifier() methods, who are
+// free to modify or override it.
+//
+// Note: We use strchr("...") instead of is*() here because we do not
+// wish to be influenced by locale settings.
+//
+std::string DiskRep::canonicalIdentifier(const std::string &name)
+{
+ string s = name;
+ string::size_type p;
+
+ // lop off any directory prefixes
+ if ((p = s.rfind('/')) != string::npos)
+ s = s.substr(p+1);
+
+ // remove any final extension (last dot) unless it's numeric
+ if ((p = s.rfind('.')) != string::npos && !strchr("0123456789", s[p+1]))
+ s = s.substr(0, p);
+
+ // eat numeric suffixes except the first one; roughly:
+ // foo.2.3.4 => foo.2, foo2.3 => foo2, foo.9 => foo.9, foo => foo
+ if (strchr("0123456789.", s[0])) // starts with digit or .
+ return s; // ... so don't mess with it
+ p = s.size()-1;
+ // foo3.5^, foo.3.5^, foo3^, foo.3^, foo^
+ while (strchr("0123456789.", s[p]))
+ p--;
+ // fo^o3.5, fo^o.3.5, fo^o3, fo^o.3, fo^o
+ p++;
+ // foo^3.5, foo^.3.5, foo^3, foo^.3, foo^
+ if (s[p] == '.')
+ p++;
+ // foo^3.5, foo.^3.5, foo^3, foo.^3, foo^
+ while (p < s.size() && strchr("0123456789", s[p]))
+ p++;
+ // foo3^.5, foo.3^.5, foo3^, foo.3^, foo^
+ return s.substr(0, p);
+}
+
+
//
// Writers
//
void DiskRep::Writer::flush()
{ /* do nothing */ }
+void DiskRep::Writer::remove()
+{
+ MacOSError::throwMe(errSecCSNotSupported);
+}
+
} // end namespace CodeSigning
} // end namespace Security