Move verifyFileOnlyWritableByMunkiAndRoot() from munkicommon to managesoftwareupdate, so it can be used without importing munkicommon.py.

This is in prep of a upcoming notification feature that cannot depend on ObjC Py bindings.  Alternatively, if we want to share such code, we can create a common/util module for only Pure-Python code, and doc that it should not contain any ObjC imports.


git-svn-id: http://munki.googlecode.com/svn/trunk@861 a4e17f2e-e282-11dd-95e1-755cbddbdd66
This commit is contained in:
Justin McWilliams
2010-10-26 13:15:13 +00:00
parent 8b36ffe59e
commit 9a566d108e
2 changed files with 55 additions and 51 deletions
+51 -1
View File
@@ -21,6 +21,7 @@ managedsoftwareupdate
import sys
import os
import optparse
import stat
import subprocess
import time
import traceback
@@ -35,6 +36,18 @@ from munkilib import appleupdates
from munkilib import FoundationPlist
class Error(Exception):
"""Class for domain specific exceptions."""
class VerifyFilePermissionsError(Error):
"""There was an error verifying file permissions."""
class InsecureFilePermissionsError(VerifyFilePermissionsError):
"""The permissions of the specified file are insecure."""
def getIdleSeconds():
"""Returns the number of seconds since the last mouse or keyboard event."""
cmd = ['/usr/sbin/ioreg', '-c', 'IOHIDSystem', '-d', '4']
@@ -84,6 +97,43 @@ def clearLastNotifiedDate():
pass
def verifyFileOnlyWritableByMunkiAndRoot(file_path):
"""
Check the permissions on a given file path; fail if owner or group
does not match the munki process (default: root/admin) or the group is not
'wheel', or if other users are able to write to the file. This prevents
escalated execution of arbitrary code.
Args:
file_path: str path of file to verify permissions on.
Raises:
VerifyFilePermissionsError: there was an error verifying file permissions.
InsecureFilePermissionsError: file permissions were found to be insecure.
"""
try:
file_stat = os.stat(file_path)
except OSError, e:
raise VerifyFilePermissionsError(
'%s does not exist. \n %s' % (file_path, str(e)))
try:
# verify the munki process uid matches the file owner uid.
if os.geteuid() != file_stat.st_uid:
raise InsecureFilePermissionsError(
'owner does not match munki process!')
# verify the munki process gid matches the file owner gid, or the file
# owner gid is 80 (which is the admin group root is a member of).
elif os.getegid() != file_stat.st_gid and file_stat.st_gid != 80:
raise InsecureFilePermissionsError(
'group does not match munki process!')
# verify other users cannot write to the file.
elif file_stat.st_mode & stat.S_IWOTH != 0:
raise InsecureFilePermissionsError('world writable!')
except InsecureFilePermissionsError, e:
raise InsecureFilePermissionsError(
'%s is not secure! %s' % (file_path, e.args[0]))
def createDirsIfNeeded(dirlist):
"""Create any missing directories needed by the munki tools.
@@ -307,7 +357,7 @@ def runPreOrPostFlightScript(script, runtype='custom'):
"""
if os.path.exists(script):
try:
munkicommon.verifyFileOnlyWritableByMunkiAndRoot(script)
verifyFileOnlyWritableByMunkiAndRoot(script)
except munkicommon.VerifyFilePermissionsError, e:
msg = ('Skipping execution due to failed file permissions '
'verification: %s\n%s' % (script, str(e)))
+4 -50
View File
@@ -28,7 +28,6 @@ import hashlib
import os
import platform
import shutil
import stat
import struct
import subprocess
import sys
@@ -59,14 +58,6 @@ class PreferencesError(Error):
"""There was an error reading the preferences plist."""
class VerifyFilePermissionsError(Error):
"""There was an error verifying file permissions."""
class InsecureFilePermissionsError(VerifyFilePermissionsError):
"""The permissions of the specified file are insecure."""
def get_version():
"""Returns version of munkitools, reading version.plist
and svnversion"""
@@ -1223,8 +1214,8 @@ def getPackageMetaData(pkgitem):
cataloginfo['receipts'] = receiptinfo
return cataloginfo
def _unsigned(i):
"""Translate a signed int into an unsigned int. Int type returned
is longer than the original since Python has no unsigned int."""
@@ -1423,9 +1414,9 @@ def getLSInstalledApplications():
applist.append(app_path)
return applist
# we save APPDATA in a global to avoid querying LaunchServices more than
# once per session
# once per session
APPDATA = []
def getAppData():
"""Gets info on currently installed apps.
@@ -1455,43 +1446,6 @@ def getAppData():
# some utility functions
def verifyFileOnlyWritableByMunkiAndRoot(file_path):
"""
Check the permissions on a given file path; fail if owner or group
does not match the munki process (default: root/admin) or the group is not
'wheel', or if other users are able to write to the file. This prevents
escalated execution of arbitrary code.
Args:
file_path: str path of file to verify permissions on.
Raises:
VerifyFilePermissionsError: there was an error verifying file permissions.
InsecureFilePermissionsError: file permissions were found to be insecure.
"""
try:
file_stat = os.stat(file_path)
except OSError, e:
raise VerifyFilePermissionsError(
'%s does not exist. \n %s' % (file_path, str(e)))
try:
# verify the munki process uid matches the file owner uid.
if os.geteuid() != file_stat.st_uid:
raise InsecureFilePermissionsError(
'owner does not match munki process!')
# verify the munki process gid matches the file owner gid, or the file
# owner gid is 80 (which is the admin group root is a member of).
elif os.getegid() != file_stat.st_gid and file_stat.st_gid != 80:
raise InsecureFilePermissionsError(
'group does not match munki process!')
# verify other users cannot write to the file.
elif file_stat.st_mode & stat.S_IWOTH != 0:
raise InsecureFilePermissionsError('world writable!')
except InsecureFilePermissionsError, e:
raise InsecureFilePermissionsError(
'%s is not secure! %s' % (file_path, e.args[0]))
def getAvailableDiskSpace(volumepath='/'):
"""Returns available diskspace in KBytes."""
cmd = ['/usr/sbin/diskutil', 'info', '-plist', volumepath]