views:

530

answers:

2

Hello all, hopefully someone here can shed some light on my issue :D

I've been creating a Windows XP service in python that is designed to monitor/repair selected Windows/Application/Service settings, atm I have been focusing on default DCOM settings.

The idea is to backup our default configuration within another registry key for reference. Every 30 minutes (currently every 30 seconds for testing) I would like the service to query the current windows default DCOM settings from the registry and compare the results to the default configuration. If discrepancies are found, the service will replace the current windows settings with the custom configuration settings.

I have already created/tested my class to handle the registry checking/repairing and so far it runs flawlessly.. Until I compile it to an exe and run it as a service.

The service itself starts up just fine and it seems to loop every 30 seconds as defined, but my module to handle the registry checking/repairing does not seem to get run as specified.

I created a log file and was able to obtain the following error:

Traceback (most recent call last):
File "DCOMMon.pyc", line 52, in RepairDCOM
File "DCOMMon.pyc", line 97, in GetDefaultDCOM
File "pywmi.pyc", line 396, in call
File "pywmi.pyc", line 189, in handle_com_error
x_wmi: -0x7ffdfff7 - Exception occurred.
Error in: SWbemObjectEx
-0x7ffbfe10 -

When I stop the service and run the exe manually, specifying the debug argument: DCOMMon.exe debug, the service starts up and runs just fine, performing all tasks as expected. The only differences that I can see is that the service starts the process as the SYSTEM user instead of the logged on user which leads me to believe (just guessing here) that it might be some sort of missed permission/policy for the SYSTEM user? I have tested running the service as another user but there was no difference there either.

Other thoughts were to add the wmi service to the dependencies of my service but truthfully I have no idea what that would do :P This is the first time I've attempted to create a windows service in python, without using something like srvany.exe.

I have spent the better part of last night and today trying to google around and find some information regarding py2exe and wmi compatibility but so far the suggestions I have found have not helped solve the above issue.

Any suggestions would be appreciated.

PS: Don't hate me for the poor logging, I cut/pasted my logger from a different scripts and I have not made the appropriate changes, it might double up each line :P. The log file can be found here: "%WINDIR%\system32\DCOMMon.log"

UPDATE

I have tried to split this project up into two exe files instead of one. Let the service make and external call to the other exe to run the wmi registry portion. Again, when running with the debug arg it works just fine, but when I start it as a service it logs the same error message. More and more this is starting to look like a permission issue an not a program issue :(

UPDATE

DCOMMon.py - Requires pywin32, wmi (renamed to pywmi),

# DCOMMon.py


import win32api, win32service, win32serviceutil, win32event, win32evtlogutil, win32traceutil
import logging, logging.handlers, os, re, sys, thread, time, traceback, pywmi # pywmi == wmi module renamed as suggested in online post
import _winreg as reg


DCOM_DEFAULT_CONFIGURATION      = ["EnableDCOM", "EnableRemoteConnect", "LegacyAuthenticationLevel", "LegacyImpersonationLevel", "DefaultAccessPermission",
                                   "DefaultLaunchPermission", "MachineAccessRestriction", "MachineLaunchRestriction"]

DCOM_DEFAULT_ACCESS_PERMISSION  = [1, 0, 4, 128, 92, 0, 0, 0, 108, 0, 0, 0, 0, 0, 0, 0, 20, 0, 0, 0, 2, 0, 72, 0, 3, 0, 0, 0, 0, 0, 24, 0, 7, 0, 0, 0, 1, 2,
                                   0, 0, 0, 0, 0, 5, 32, 0, 0, 0, 32, 2, 0, 0, 0, 0, 20, 0, 7, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 5, 7, 0, 0, 0, 0, 0, 20, 0, 7,
                                   0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 2, 0, 0, 0, 0, 0, 5, 32, 0, 0, 0, 32, 2, 0, 0, 1, 2, 0, 0, 0, 0, 0, 5, 32,
                                   0, 0, 0, 32, 2, 0, 0]

DCOM_DEFAULT_LAUNCH_PERMISSION  = [1, 0, 4, 128, 132, 0, 0, 0, 148, 0, 0, 0, 0, 0, 0, 0, 20, 0, 0, 0, 2, 0, 112, 0, 5, 0, 0, 0, 0, 0, 24, 0, 31, 0, 0, 0, 1,
                                   2, 0, 0, 0, 0, 0, 5, 32, 0, 0, 0, 32, 2, 0, 0, 0, 0, 20, 0, 31, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 5, 7, 0, 0, 0, 0, 0, 20, 0,
                                   31, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 20, 0, 31, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 5, 4, 0, 0, 0, 0, 0, 20, 0,
                                   31, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 5, 18, 0, 0, 0, 1, 2, 0, 0, 0, 0, 0, 5, 32, 0, 0, 0, 32, 2, 0, 0, 1, 2, 0, 0, 0, 0, 0, 5,
                                   32, 0, 0, 0, 32, 2, 0, 0]

DCOM_MACHINE_ACCESS_RESTRICTION = [1, 0, 4, 128, 68, 0, 0, 0, 84, 0, 0, 0, 0, 0, 0, 0, 20, 0, 0, 0, 2, 0, 48, 0, 2, 0, 0, 0, 0, 0, 20, 0, 3, 0, 0, 0, 1, 1,
                                   0, 0, 0, 0, 0, 5, 7, 0, 0, 0, 0, 0, 20, 0, 7, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 2, 0, 0, 0, 0, 0, 5, 32, 0,
                                   0, 0, 32, 2, 0, 0, 1, 2, 0, 0, 0, 0, 0, 5, 32, 0, 0, 0, 32, 2, 0, 0]

DCOM_MACHINE_LAUNCH_RESTRICTION = [1, 0, 4, 128, 72, 0, 0, 0, 88, 0, 0, 0, 0, 0, 0, 0, 20, 0, 0, 0, 2, 0, 52, 0, 2, 0, 0, 0, 0, 0, 24, 0, 31, 0, 0, 0, 1,
                                   2, 0, 0, 0, 0, 0, 5, 32, 0, 0, 0, 32, 2, 0, 0, 0, 0, 20, 0, 31, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 2, 0, 0,
                                   0, 0, 0, 5, 32, 0, 0, 0, 32, 2, 0, 0, 1, 2, 0, 0, 0, 0, 0, 5, 32, 0, 0, 0, 32, 2, 0, 0]

COMPUTER  = os.environ["COMPUTERNAME"]
REGISTRY  = pywmi.WMI(COMPUTER, namespace="root/default").StdRegProv
LOGFILE   = os.getcwd() + "\\DCOMMon.log"


def Logger(title, filename):
    logger = logging.getLogger(title)
    logger.setLevel(logging.DEBUG)
    handler = logging.handlers.RotatingFileHandler(filename, maxBytes=0, backupCount=0)
    handler.setLevel(logging.DEBUG)
    formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s")
    handler.setFormatter(formatter)
    logger.addHandler(handler)
    return logger


def LogIt(filename=LOGFILE):
    #try:
    #    if os.path.exists(filename):
    #        os.remove(filename)
    #except:
    #    pass
    log = Logger("DCOMMon", filename)
    tb  = str(traceback.format_exc()).split("\n")
    log.error("")
    for i, a in enumerate(tb):
        if a.strip() != "":
            log.error(a)

class Monitor:

    def RepairDCOM(self):
        try:
            repaired = {}
            dict1    = self.GetDefaultDCOM()
            dict2    = self.GetCurrentDCOM()
            compared = self.CompareDCOM(dict1, dict2)

            for dobj in DCOM_DEFAULT_CONFIGURATION:
                try:
                    compared[dobj]
                    if dobj == "LegacyAuthenticationLevel" or dobj == "LegacyImpersonationLevel":
                        REGISTRY.SetDWORDValue(hDefKey=reg.HKEY_LOCAL_MACHINE, sSubKeyName="SOFTWARE\\Microsoft\\Ole", sValueName=dobj, uValue=dict1[dobj])
                    elif dobj == "DefaultAccessPermission" or dobj == "DefaultLaunchPermission" or \
                         dobj == "MachineAccessRestriction" or dobj == "MachineLaunchRestriction":
                        REGISTRY.SetBinaryValue(hDefKey=reg.HKEY_LOCAL_MACHINE, sSubKeyName="SOFTWARE\\Microsoft\\Ole", sValueName=dobj, uValue=dict1[dobj])
                    elif dobj == "EnableDCOM" or dobj == "EnableRemoteConnect":
                        REGISTRY.SetStringValue(hDefKey=reg.HKEY_LOCAL_MACHINE, sSubKeyName="SOFTWARE\\Microsoft\\Ole", sValueName=dobj, sValue=dict1[dobj])
                except KeyError:
                    pass
        except:
            LogIt(LOGFILE)

    def CompareDCOM(self, dict1, dict2):
        compare = {}
        for (key, value) in dict2.iteritems():
            try:
                if dict1[key] != value:
                    compare[key] = value
            except KeyError:
                compare[key] = value
        return compare

    def GetCurrentDCOM(self):
        current = {}
        for name in REGISTRY.EnumValues(hDefKey=reg.HKEY_LOCAL_MACHINE, sSubKeyName="SOFTWARE\\Microsoft\\Ole")[1]:
            value = REGISTRY.GetStringValue(hDefKey=reg.HKEY_LOCAL_MACHINE, sSubKeyName="SOFTWARE\\Microsoft\\Ole", sValueName=str(name))[1]
            if value:
                current[str(name)] = str(value)
            else:
                value = REGISTRY.GetDWORDValue(hDefKey=reg.HKEY_LOCAL_MACHINE, sSubKeyName="SOFTWARE\\Microsoft\\Ole", sValueName=str(name))[1]
                if not value:
                    value = REGISTRY.GetBinaryValue(hDefKey=reg.HKEY_LOCAL_MACHINE, sSubKeyName="SOFTWARE\\Microsoft\\Ole", sValueName=str(name))[1]
            current[str(name)] = value
        return current

    def GetDefaultDCOM(self):
        default = {}
        # Get Default DCOM Settings
        for name in REGISTRY.EnumValues(hDefKey=reg.HKEY_CURRENT_USER, sSubKeyName="Software\\DCOMMon")[1]:
            value = REGISTRY.GetStringValue(hDefKey=reg.HKEY_CURRENT_USER, sSubKeyName="Software\\DCOMMon", sValueName=str(name))[1]
            if value:
                default[str(name)] = str(value)
            else:
                value = REGISTRY.GetDWORDValue(hDefKey=reg.HKEY_CURRENT_USER, sSubKeyName="Software\\DCOMMon", sValueName=str(name))[1]
                if not value:
                    value = REGISTRY.GetBinaryValue(hDefKey=reg.HKEY_CURRENT_USER, sSubKeyName="Software\\DCOMMon", sValueName=str(name))[1]
            default[str(name)] = value
        return default

class DCOMMon(win32serviceutil.ServiceFramework):
    _svc_name_         = "DCOMMon"
    _svc_display_name_ = "DCOM Monitoring Service"
    _svc_description_  = "DCOM Monitoring Service"
    _svc_deps_         = ["EventLog"]

    def __init__(self, args):
        win32serviceutil.ServiceFramework.__init__(self, args)
        self.hWaitStop = win32event.CreateEvent(None, 0, 0, None)
        self.isAlive   = True

    def SvcDoRun(self):
        import servicemanager
        servicemanager.LogMsg(servicemanager.EVENTLOG_INFORMATION_TYPE, servicemanager.PYS_SERVICE_STARTED,
                              (self._svc_name_, ': DCOM Monitoring Service - Service Started'))
        self.timeout=30000  # In milliseconds
        while self.isAlive:
            rc = win32event.WaitForSingleObject(self.hWaitStop, self.timeout)
            if rc == win32event.WAIT_OBJECT_0:
                 break
            else:
                servicemanager.LogMsg(servicemanager.EVENTLOG_INFORMATION_TYPE, servicemanager.PYS_SERVICE_STARTED,
                                      (self._svc_name_, ': DCOM Monitoring Service - Examining DCOM Configuration'))
                Monitor().RepairDCOM()
        servicemanager.LogMsg(servicemanager.EVENTLOG_INFORMATION_TYPE, servicemanager.PYS_SERVICE_STOPPED,
                              (self._svc_name_, ': DCOM Monitoring Service - Service Stopped'))
        return

    def SvcStop(self):
        self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING)
        win32event.SetEvent(self.hWaitStop)
        LOG.close()
        self.isAlive = False
        return

#def ctrlHandler(ctrlType):
#    return True

if __name__ == '__main__':
#    win32api.SetConsoleCtrlHandler(ctrlHandler, True)
    #print Monitor().RepairDCOM()
    win32serviceutil.HandleCommandLine(DCOMMon)

DCOMMon_setup.py - Requires py2exe (self executable, no need for py2exe arg)

# DCOMMon_setup.py (self executable, no need for py2exe arg)

# Usage:
# DCOMMon.exe install
# DCOMMon.exe start
# DCOMMon.exe stop
# DCOMMon.exe remove
# DCOMMon.exe debug

# you can see output of this program running python site-packages\win32\lib\win32traceutil



try:
    # (snippet I found somewhere, searching something??)
    # if this doesn't work, try import modulefinder
    import py2exe.mf as modulefinder
    import win32com, sys
    for p in win32com.__path__[1:]:
        modulefinder.AddPackagePath("win32com", p)
    for extra in ["win32com.shell"]: #,"win32com.mapi"
        __import__(extra)
        m = sys.modules[extra]
        for p in m.__path__[1:]:
            modulefinder.AddPackagePath(extra, p)
except ImportError:
    print "NOT FOUND"


from distutils.core import setup
import py2exe, sys

if len(sys.argv) == 1:
    sys.argv.append("py2exe")
    #sys.argv.append("-q")

class Target:
    def __init__(self, **kw):
        self.__dict__.update(kw)
        # for the versioninfo resources
        self.version      = "1.0.0.1"
        self.language     = "English (Canada)"
        self.company_name = "Whoever"
        self.copyright    = "Nobody"
        self.name         = "Nobody Home"


myservice = Target(
    description = 'DCOM Monitoring Service',
    modules = ['DCOMMon'],
    cmdline_style='pywin32'
    #dest_base = 'DCOMMon'
)

setup(
    options = {"py2exe": {"compressed": 1, "bundle_files": 1, "ascii": 1, "packages": ["encodings"]} },   
    console=["DCOMMon.py"],
    zipfile = None,
    service=[myservice]
)
A: 

I am not an expert at this, but here are my two cents worth:

This article tells me that you might need to be logged in as someone with the required target system permissions.

However, I find that a little excessive. Have you tried compiling your script from the command line while running the command prompt as the administrator of the computer - so that you can unlock all permissions (on Windows Vista and Windows 7, this is achieved by right clicking on the command prompt icon in the start menu and clicking on "run as administrator").

Hope this helps

inspectorG4dget
Thanks for your reply :D I tried compiling it from an Admin cmd prompt but I still had no luck. The problem doesn't stem from how it was compiled, the service itself works just fine, it's the wmi calls that are being made that seem to fail. My account was already an admin anyhow, and the service runs as SYSTEM not Administrator, but you never know, I'll try anything at this point ;) FYI, this is an XP Service, I probably should have mentioned that before.
AWainb
A: 

So I guess it's time to admit my stupidity.... :P

It turns out this was not a python issue, py2exe issue, nor a WMI issue. :(

This was more or less a simple permission issue. So simple I overlooked it for the better part of a month. :(

Rule of thumb, if you want to create a service that calls to specific registry keys to obtain (in this case) default configuration settings....

maybe... JUST MAYBE....

One should place their default key within the "HKEY_LOCAL_MACHINE", instead of "HKEY_CURRENT_USER"?? :P

Yeah, it's that simple...

I should have remembered this rule from other projects I have worked on in the past. When you are running as the Local System account, simply put, there is no "HKEY_CURRENT_USER" subkey to access. If you absolutely have to access a specific user's "HKEY_CURRENT_USER" subkey, I would guess the only way would be to impersonate the user. Luckily for me this is not necessary for what I am attempting to accomplish.

The below link provided me with what I needed to jog my memory and fix the issue:

http://msdn.microsoft.com/en-us/library/ms684190%28VS.85%29.aspx

So to make a long story short, I migrated all of my default values over to "HKEY_LOCAL_MACHINE\SOFTWARE" subkey and my service is working perfectly. :D

Thanks for your help guys but this problem is solved ;)

AWainb
**UPDATE** I was able to access the user's key by using the HKEY_USERS hive. It is a bit tricky as you must obtain the current user's SID to access the proper subkey: Works from SYSTEM account: "HKEY_USERS\<USER SID STRING>\Software\kMonitor" Does not work from SYSTEM account: "HKEY_CURRENT_USER\Software\kMonitor"
AWainb