Bug 976398 - Check camera permissions directly instead of using Unix groups. r=mwu r=bent

Loosely based on the code used to check PRemoteOpenFile.
This commit is contained in:
Jed Davis 2014-03-12 15:48:15 -07:00
parent 5c75b92a92
commit b756489606
4 changed files with 97 additions and 81 deletions

View File

@ -587,34 +587,6 @@ ContentParent::GetNewOrUsed(bool aForBrowserElement)
return p.forget(); return p.forget();
} }
namespace {
struct SpecialPermission {
const char* perm; // an app permission
ChildPrivileges privs; // the OS privilege it requires
};
}
static ChildPrivileges
PrivilegesForApp(mozIApplication* aApp)
{
const SpecialPermission specialPermissions[] = {
// FIXME/bug 785592: implement a CameraBridge so we don't have
// to hack around with OS permissions
{ "camera", base::PRIVILEGES_CAMERA }
};
for (size_t i = 0; i < ArrayLength(specialPermissions); ++i) {
const char* const permission = specialPermissions[i].perm;
bool hasPermission = false;
if (NS_FAILED(aApp->HasPermission(permission, &hasPermission))) {
NS_WARNING("Unable to check permissions. Breakage may follow.");
break;
} else if (hasPermission) {
return specialPermissions[i].privs;
}
}
return base::PRIVILEGES_DEFAULT;
}
/*static*/ ProcessPriority /*static*/ ProcessPriority
ContentParent::GetInitialProcessPriority(Element* aFrameElement) ContentParent::GetInitialProcessPriority(Element* aFrameElement)
{ {
@ -733,7 +705,7 @@ ContentParent::CreateBrowserOrApp(const TabContext& aContext,
} }
if (!p) { if (!p) {
ChildPrivileges privs = PrivilegesForApp(ownApp); ChildPrivileges privs = base::PRIVILEGES_DEFAULT;
p = MaybeTakePreallocatedAppProcess(manifestURL, privs, p = MaybeTakePreallocatedAppProcess(manifestURL, privs,
initialPriority); initialPriority);
if (!p) { if (!p) {

View File

@ -129,7 +129,6 @@ void CloseSuperfluousFds(const base::InjectiveMultimap& saved_map);
enum ChildPrivileges { enum ChildPrivileges {
PRIVILEGES_DEFAULT, PRIVILEGES_DEFAULT,
PRIVILEGES_UNPRIVILEGED, PRIVILEGES_UNPRIVILEGED,
PRIVILEGES_CAMERA,
PRIVILEGES_INHERIT, PRIVILEGES_INHERIT,
PRIVILEGES_LAST PRIVILEGES_LAST
}; };

View File

@ -302,17 +302,6 @@ void SetCurrentProcessPrivileges(ChildPrivileges privs) {
gid += getpid(); gid += getpid();
uid += getpid(); uid += getpid();
} }
if (privs == PRIVILEGES_CAMERA) {
#if ANDROID_VERSION < 17
gid_t groups[] = { AID_SDCARD_RW };
#else
gid_t groups[] = { AID_SDCARD_R, AID_SDCARD_RW, AID_MEDIA_RW };
#endif
if (setgroups(sizeof(groups) / sizeof(groups[0]), groups) != 0) {
DLOG(ERROR) << "FAILED TO setgroups() CHILD PROCESS";
_exit(127);
}
}
#endif #endif
if (setgid(gid) != 0) { if (setgid(gid) != 0) {
DLOG(ERROR) << "FAILED TO setgid() CHILD PROCESS"; DLOG(ERROR) << "FAILED TO setgid() CHILD PROCESS";

View File

@ -21,6 +21,13 @@
#include <private/android_filesystem_config.h> #include <private/android_filesystem_config.h>
#include "GonkPermission.h" #include "GonkPermission.h"
#include "mozilla/dom/ContentParent.h"
#include "mozilla/dom/TabParent.h"
#include "mozilla/SyncRunnable.h"
#include "nsIAppsService.h"
#include "mozIApplication.h"
#include "nsThreadUtils.h"
#undef LOG #undef LOG
#include <android/log.h> #include <android/log.h>
#define ALOGE(args...) __android_log_print(ANDROID_LOG_ERROR, "gonkperm" , ## args) #define ALOGE(args...) __android_log_print(ANDROID_LOG_ERROR, "gonkperm" , ## args)
@ -28,31 +35,104 @@
using namespace android; using namespace android;
using namespace mozilla; using namespace mozilla;
// Checking permissions needs to happen on the main thread, but the
// binder callback is called on a special binder thread, so we use
// this runnable for that.
class GonkPermissionChecker : public nsRunnable {
int32_t mPid;
bool mCanUseCamera;
explicit GonkPermissionChecker(int32_t pid)
: mPid(pid)
, mCanUseCamera(false)
{
}
public:
static already_AddRefed<GonkPermissionChecker> Inspect(int32_t pid)
{
nsRefPtr<GonkPermissionChecker> that = new GonkPermissionChecker(pid);
nsCOMPtr<nsIThread> mainThread = do_GetMainThread();
MOZ_ASSERT(mainThread);
SyncRunnable::DispatchToThread(mainThread, that);
return that.forget();
}
bool CanUseCamera()
{
return mCanUseCamera;
}
NS_IMETHOD Run();
};
NS_IMETHODIMP
GonkPermissionChecker::Run()
{
MOZ_ASSERT(NS_IsMainThread());
// Find our ContentParent.
dom::ContentParent *contentParent = nullptr;
{
nsTArray<dom::ContentParent*> parents;
dom::ContentParent::GetAll(parents);
for (uint32_t i = 0; i < parents.Length(); ++i) {
if (parents[i]->Pid() == mPid) {
contentParent = parents[i];
break;
}
}
}
if (!contentParent) {
ALOGE("pid=%d denied: can't find ContentParent", mPid);
return NS_OK;
}
// Now iterate its apps...
for (uint32_t i = 0; i < contentParent->ManagedPBrowserParent().Length(); i++) {
dom::TabParent *tabParent =
static_cast<dom::TabParent*>(contentParent->ManagedPBrowserParent()[i]);
nsCOMPtr<mozIApplication> mozApp = tabParent->GetOwnOrContainingApp();
if (!mozApp) {
continue;
}
// ...and check if any of them has camera access.
bool appCanUseCamera;
nsresult rv = mozApp->HasPermission("camera", &appCanUseCamera);
if (NS_SUCCEEDED(rv) && appCanUseCamera) {
mCanUseCamera = true;
return NS_OK;
}
}
return NS_OK;
}
bool bool
GonkPermissionService::checkPermission(const String16& permission, int32_t pid, GonkPermissionService::checkPermission(const String16& permission, int32_t pid,
int32_t uid) int32_t uid)
{ {
if (0 == uid) // root can do anything.
if (0 == uid) {
return true; return true;
}
String8 perm8(permission); String8 perm8(permission);
// Some ril implementations need android.permission.MODIFY_AUDIO_SETTINGS // Some ril implementations need android.permission.MODIFY_AUDIO_SETTINGS
if (uid == AID_RADIO && if (uid == AID_RADIO &&
perm8 == "android.permission.MODIFY_AUDIO_SETTINGS") perm8 == "android.permission.MODIFY_AUDIO_SETTINGS") {
return true; return true;
}
// Camera/audio record permissions are only for apps with the // No other permissions apply to non-app processes.
// "camera" permission. These apps are also the only apps granted
// the AID_SDCARD_RW supplemental group (bug 785592)
if (uid < AID_APP) { if (uid < AID_APP) {
ALOGE("%s for pid=%d,uid=%d denied: not an app", ALOGE("%s for pid=%d,uid=%d denied: not an app",
String8(permission).string(), pid, uid); String8(permission).string(), pid, uid);
return false; return false;
} }
// Only these permissions can be granted to apps through this service.
if (perm8 != "android.permission.CAMERA" && if (perm8 != "android.permission.CAMERA" &&
perm8 != "android.permission.RECORD_AUDIO") { perm8 != "android.permission.RECORD_AUDIO") {
ALOGE("%s for pid=%d,uid=%d denied: unsupported permission", ALOGE("%s for pid=%d,uid=%d denied: unsupported permission",
@ -69,40 +149,16 @@ GonkPermissionService::checkPermission(const String16& permission, int32_t pid,
return true; return true;
} }
char filename[32]; // Camera/audio record permissions are allowed for apps with the
snprintf(filename, sizeof(filename), "/proc/%d/status", pid); // "camera" permission.
FILE *f = fopen(filename, "r"); nsRefPtr<GonkPermissionChecker> checker =
if (!f) { GonkPermissionChecker::Inspect(pid);
ALOGE("%s for pid=%d,uid=%d denied: unable to open %s", bool canUseCamera = checker->CanUseCamera();
String8(permission).string(), pid, uid, filename); if (!canUseCamera) {
return false; ALOGE("%s for pid=%d,uid=%d denied: not granted by user or app manifest",
String8(permission).string(), pid, uid);
} }
return canUseCamera;
char line[80];
while (fgets(line, sizeof(line), f)) {
char *save;
char *name = strtok_r(line, "\t", &save);
if (!name)
continue;
if (strcmp(name, "Groups:"))
continue;
char *group;
while ((group = strtok_r(NULL, " \n", &save))) {
#define _STR(x) #x
#define STR(x) _STR(x)
if (!strcmp(group, STR(AID_SDCARD_RW))) {
fclose(f);
return true;
}
}
break;
}
fclose(f);
ALOGE("%s for pid=%d,uid=%d denied: missing group",
String8(permission).string(), pid, uid);
return false;
} }
static GonkPermissionService* gGonkPermissionService = NULL; static GonkPermissionService* gGonkPermissionService = NULL;