def handler(dn, new, old):
    # type: (str, dict, dict) -> None
    if new and b'Software Monitor' in new.get('univentionService', ()):
        with SetUID(0):
            ucr.handler_set(('pkgdb/scan=yes', ))
    elif old and b'Software Monitor' in old.get('univentionService', ()):
        if not ldap_info['lo']:
            ldap_reconnect()
        if ldap_info['lo'] and not ldap_info['lo'].search(
                filter='(&%s(univentionService=Software Monitor))' % filter,
                attr=['univentionService']):
            with SetUID(0):
                ucr.handler_set(('pkgdb/scan=no', ))
def removePrivileges(sambaSID, privileges):
    # type: (bytes, list) -> None
    with SetUID(0):
        tdbKey = b'PRIV_%s\x00' % (sambaSID, )
        tdbFile = tdb.Tdb(SAMBA_POLICY_TDB)
        tdbFile.lock_all()
        privs = tdbFile.get(tdbKey)

        if privs:
            for privilege in privileges:
                if SAMBA_PRIVILEGES.get(privilege):
                    index = SAMBA_PRIVILEGES[privilege]["index"]
                    number = SAMBA_PRIVILEGES[privilege]["number"]
                    if privs[index] & number:
                        new = chr(privs[index] - number).encode('ISO8859-1')
                        privs = privs[0:index] + new + privs[(index +
                                                              1):len(privs)]
                        tdbFile[tdbKey] = privs

            # delete key if no privileges are assigned
            if privs == b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00':
                tdbFile.delete(tdbKey)

        tdbFile.unlock_all()
        tdbFile.close()
 def initialize(self):
     try:
         ent = getpwnam(self.USER)
         with SetUID():
             with open(self.LOG, "wb"):
                 pass
             os.chown(self.LOG, ent.pw_uid, -1)
     except OSError as ex:
         ud.debug(ud.LISTENER, ud.ERROR, str(ex))
Пример #4
0
def exec_pkgdb(args):
    # type: (list) -> int
    ud.debug(ud.LISTENER, ud.INFO, "exec_pkgdb args=%s" % args)

    with SetUID(0):
        cmd = [
            'univention-pkgdb-scan',
            '--db-server=%(hostname)s.%(domainname)s' % configRegistry
        ]
        cmd += args
        retcode = subprocess.call(cmd)

    ud.debug(ud.LISTENER, ud.INFO, "pkgdb: return code %d" % retcode)
    return retcode
Пример #5
0
def handler(dn, new, old):
    # type: (str, dict, dict) -> None
    ud.debug(ud.LISTENER, ud.INFO, "pkgdb handler dn=%s" % (dn))

    with SetUID(0):
        if old and not new:
            if 'uid' in old:
                uid = old['uid'][0].decode('UTF-8')
                if del_system(uid) != 0:
                    with open(os.path.join(DELETE_DIR, uid), 'w') as fd:
                        fd.write(uid + '\n')

        elif new and not old:
            if 'uid' in new:
                uid = new['uid'][0].decode('UTF-8')
                if add_system(uid) != 0:
                    with open(os.path.join(ADD_DIR, uid), 'w') as fd:
                        fd.write(uid + '\n')
Пример #6
0
def initialize():
    # type: () -> None
    timestamp = time.strftime(timestampfmt, time.gmtime())
    univention.debug.debug(univention.debug.LISTENER, univention.debug.INFO,
                           'init %s' % name)

    with SetUID(0):
        if not os.path.exists(logname):
            createFile(logname)

        if not os.path.exists(cachename):
            createFile(cachename)
        size = os.path.getsize(cachename)
        cachefile = open(cachename, 'r+')

        # generate log record
        if size == 0:
            action = 'Initialize'
            record = 'START\nTimestamp: %s\nAction: %s %s\n' % (timestamp,
                                                                action, name)
        else:
            # read previous hash
            previoushash = cachefile.read()
            action = 'Reinitialize'
            record = 'START\nOld Hash: %s\nTimestamp: %s\nAction: %s %s\n' % (
                previoushash, timestamp, action, name)
        record += endtag

        # 3. write log file record
        with open(logname, 'a') as logfile:  # append
            logfile.write(prefix_record(record, 0))
        # 4. calculate initial hash
        nexthash = hashlib.new(digest, record.encode('UTF-8')).hexdigest()
        # 5. cache nexthash (the actual logfile might be logrotated away..)
        cachefile.seek(0)
        cachefile.write(nexthash)
        cachefile.close()
        # 6. send log message including nexthash
        syslog.openlog(name, 0, syslog.LOG_DAEMON)
        syslog.syslog(
            syslog.LOG_INFO,
            '%s\nTimestamp=%s\nNew Hash=%s' % (action, timestamp, nexthash))
        syslog.closelog()
Пример #7
0
def initialize():
    # type: () -> None
    """
	Remove the log file.
	This function is called when the module is forcefully reset.
	"""
    try:
        with SetUID():
            os.remove(USER_LIST)
        ud.debug(ud.LISTENER, ud.INFO,
                 'Successfully deleted "%s"' % (USER_LIST, ))
    except OSError as ex:
        if errno.ENOENT == ex.errno:
            ud.debug(
                ud.LISTENER, ud.INFO,
                'File "%s" does not exist, will be created' % (USER_LIST, ))
        else:
            ud.debug(ud.LISTENER, ud.WARN,
                     'Failed to delete file "%s": %s' % (USER_LIST, ex))
Пример #8
0
def _writeit(rec, comment):
    # type: (_Rec, str) -> None
    """
	Append CommonName, symbolic and numeric User-IDentifier, and comment to file.
	"""
    nuid = u'*****' if rec.uid in ('root', 'spam') else rec.uidNumber
    indent = '\t' if comment is None else ''
    try:
        with SetUID():
            with open(USER_LIST, 'a') as out:
                print(u'%sName: "%s"' % (indent, rec.cn), file=out)
                print(u'%sUser: "******"' % (indent, rec.uid), file=out)
                print(u'%sUID: "%s"' % (indent, nuid), file=out)
                if comment:
                    print(u'%s%s' % (
                        indent,
                        comment,
                    ), file=out)
    except IOError as ex:
        ud.debug(ud.LISTENER, ud.ERROR,
                 'Failed to write "%s": %s' % (USER_LIST, ex))
def addPrivileges(sambaSID, privileges):
    # type: (bytes, list) -> None
    with SetUID(0):
        tdbKey = b'PRIV_%s\x00' % (sambaSID, )
        tdbFile = tdb.Tdb(SAMBA_POLICY_TDB)
        tdbFile.lock_all()
        privs = tdbFile.get(tdbKey)
        if not privs:
            privs = b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'

        for privilege in privileges:
            if SAMBA_PRIVILEGES.get(privilege):
                index = SAMBA_PRIVILEGES[privilege]["index"]
                number = SAMBA_PRIVILEGES[privilege]["number"]
                if (privs[index] & number) == 0:
                    new = chr(privs[index] + number).encode('ISO8859-1')
                    privs = privs[0:index] + new + privs[(index +
                                                          1):len(privs)]

        tdbFile[tdbKey] = privs
        tdbFile.unlock_all()
        tdbFile.close()
 def clean(self):
     try:
         with SetUID():
             os.remove(self.LOG)
     except OSError as ex:
         ud.debug(ud.LISTENER, ud.ERROR, str(ex))
Пример #11
0
def handler(dn, new_copy, old_copy):
    # type: (str, dict, dict) -> None
    if not configRegistry.is_true('ldap/logging'):
        return

    with SetUID(0):
        # check for exclusion
        if any(value in dn for key, value in configRegistry.items()
               if excludeKeyPattern.match(key)):
            if not new_copy:  # there should be a dellog entry to remove
                process_dellog(dn)
            # important: don't return a thing, otherwise this dn
            # seems to get excluded from future processing by this module
            return

        # Start processing
        # 1. read previous hash
        if not os.path.exists(cachename):
            univention.debug.debug(
                univention.debug.LISTENER, univention.debug.ERROR,
                '%s: %s vanished mid-run, stop.' % (name, cachename))
            return  # really bad, stop it.
        cachefile = open(cachename, 'r+')
        previoushash = cachefile.read()

        # get ID
        with open(notifier_id, 'r') as f:
            nid = int(f.readline()) + 1
        # matches notifier transaction nid. Tested for UCS 1.3-2 and 2.0.
        # Note about 1.3-2:
        # For user removal this matches with ++last_id as seen by the dellog overlay,
        # but for user create dellog sees nid-1, i.e. last_id has already been incremented before
        # we see it here

        # 2. generate log record
        if new_copy:
            try:
                modifier = new_copy['modifiersName'][0].decode('UTF-8')
            except LookupError:
                modifier = '<unknown>'
            try:
                timestamp = ldapTime2string(
                    new_copy['modifyTimestamp'][0].decode('ASCII'))
            except LookupError:
                timestamp = '<unknown>'

            if not old_copy:  # create branch
                record = headerfmt % (previoushash, dn, nid, modifier,
                                      timestamp, 'add')
                record += newtag
                record += ldapEntry2string(new_copy)
            else:  # modify branch
                # filter out unchanged attributes
                filterOutUnchangedAttributes(old_copy, new_copy)
                record = headerfmt % (previoushash, dn, nid, modifier,
                                      timestamp, 'modify')
                record += oldtag
                record += ldapEntry2string(old_copy)
                record += newtag
                record += ldapEntry2string(new_copy)
        else:  # delete branch
            (timestamp, dellog_id, modifier, action) = process_dellog(dn)

            record = headerfmt % (previoushash, dn, nid, modifier, timestamp,
                                  'delete')
            record += oldtag
            record += ldapEntry2string(old_copy)
        record += endtag

        # 3. write log file record
        with open(logname, 'a') as logfile:  # append
            logfile.write(prefix_record(record, nid))
        # 4. calculate nexthash, omitting the final line break to make validation of the
        #    record more intituive
        nexthash = hashlib.new(digest, record[:-1].encode('UTF-8')).hexdigest()
        # 5. cache nexthash (the actual logfile might be logrotated away..)
        cachefile.seek(0)
        cachefile.write(nexthash)
        cachefile.close()
        # 6. send log message including nexthash
        syslog.openlog(name, 0, syslog.LOG_DAEMON)
        syslog.syslog(syslog.LOG_INFO,
                      logmsgfmt % (dn, nid, modifier, timestamp, nexthash))
        syslog.closelog()
Пример #12
0
            record = 'START\nOld Hash: %s\nTimestamp: %s\nAction: %s %s\n' % (
                previoushash, timestamp, action, name)
        record += endtag

        # 3. write log file record
        with open(logname, 'a') as logfile:  # append
            logfile.write(prefix_record(record, 0))
        # 4. calculate initial hash
        nexthash = hashlib.new(digest, record.encode('UTF-8')).hexdigest()
        # 5. cache nexthash (the actual logfile might be logrotated away..)
        cachefile.seek(0)
        cachefile.write(nexthash)
        cachefile.close()
        # 6. send log message including nexthash
        syslog.openlog(name, 0, syslog.LOG_DAEMON)
        syslog.syslog(
            syslog.LOG_INFO,
            '%s\nTimestamp=%s\nNew Hash=%s' % (action, timestamp, nexthash))
        syslog.closelog()


# --- initialize on load:
with SetUID(0):
    if not os.path.exists(logname):
        createFile(logname)
    if not os.path.exists(cachename):
        univention.debug.debug(
            univention.debug.LISTENER, univention.debug.WARN,
            '%s: %s vanished, creating it' % (name, cachename))
        createFile(cachename)
def handler(dn, new, old, command):
    # type: (str, dict, dict, str) -> None
    configRegistry.load()
    interfaces = Interfaces(configRegistry)

    # dymanic module object filter
    current_fqdn = "%(hostname)s.%(domainname)s" % configRegistry
    current_ip = str(interfaces.get_default_ip_address().ip)

    new_univentionShareHost = new.get('univentionShareHost',
                                      [b''])[0].decode('ASCII')
    if new and new_univentionShareHost not in (current_fqdn, current_ip):
        new = {}  # new object is not for this host

    old_univentionShareHost = old.get('univentionShareHost',
                                      [b''])[0].decode('ASCII')
    if old and old_univentionShareHost not in (current_fqdn, current_ip):
        old = {}  # old object is not for this host

    if not (new or old):
        return

    # create tmp dir
    tmpDir = os.path.dirname(tmpFile)
    with SetUID(0):
        try:
            if not os.path.exists(tmpDir):
                os.makedirs(tmpDir)
        except Exception as exc:
            ud.debug(
                ud.LISTENER, ud.ERROR,
                "%s: could not create tmp dir %s (%s)" % (name, tmpDir, exc))
            return

    # modrdn stuff
    # 'r'+'a' -> renamed
    # command='r' and "not new and old"
    # command='a' and "new and not old"

    # write old object to pickle file
    oldObject = {}
    with SetUID(0):
        try:
            # object was renamed -> save old object
            if command == "r" and old:
                with open(tmpFile, "w+") as fd:
                    os.chmod(tmpFile, 0o600)
                    pickle.dump({"dn": dn, "old": old}, fd)
            elif command == "a" and not old and os.path.isfile(tmpFile):
                with open(tmpFile, "r") as fd:
                    p = pickle.load(fd)
                oldObject = p.get("old", {})
                os.remove(tmpFile)
        except Exception as e:
            if os.path.isfile(tmpFile):
                os.remove(tmpFile)
            ud.debug(
                ud.LISTENER, ud.ERROR,
                "%s: could not read/write tmp file %s (%s)" %
                (name, tmpFile, e))

    if old:
        share_name = old.get('univentionShareSambaName',
                             [b''])[0].decode('UTF-8', 'ignore')
        share_name_mapped = quote(share_name, safe='')
        filename = '/etc/samba/shares.conf.d/%s' % (share_name_mapped, )
        with SetUID(0):
            if os.path.exists(filename):
                os.unlink(filename)

    def _quote(arg):
        if ' ' in arg or '"' in arg or '\\' in arg:
            arg = '"%s"' % (arg.replace('\\', '\\\\').replace('"', '\\"'), )
        return arg.replace('\n', '')

    def _simple_quote(arg):
        # type: (str) -> str
        return arg.replace('\n', '')

    def _map_quote(args):
        return (_quote(arg) for arg in args)

    if new:
        share_name = new['univentionShareSambaName'][0].decode(
            'UTF-8', 'ignore')
        if not _validate_smb_share_name(share_name):
            ud.debug(ud.LISTENER, ud.ERROR,
                     "invalid samba share name: %r" % (share_name, ))
            return
        share_name_mapped = quote(share_name, safe='')
        filename = '/etc/samba/shares.conf.d/%s' % (share_name_mapped, )

        # important!: createOrRename() checks if the share path is allowed. this must be done prior to writing any files.
        # try to create directory to share
        if share_name != 'homes':
            # object was renamed
            if not old and oldObject and command == "a":
                old = oldObject
            with SetUID(0):
                ret = univention.lib.listenerSharePath.createOrRename(
                    old, new, configRegistry)
            if ret:
                ud.debug(
                    ud.LISTENER, ud.ERROR,
                    "%s: rename/create of sharePath for %s failed (%s)" %
                    (name, dn, ret))
                return

        with SetUID(0):
            fp = open(filename, 'w')

            print('[%s]' % (share_name, ), file=fp)
            if share_name != 'homes':
                print('path = %s' % _quote(
                    new['univentionSharePath'][0].decode('UTF-8', 'ignore')),
                      file=fp)
            mapping = [
                ('description', 'comment', 'UTF-8'),
                ('univentionShareSambaMSDFS', 'msdfs root', 'ASCII'),
                ('univentionShareSambaWriteable', 'writeable', 'ASCII'),
                ('univentionShareSambaBrowseable', 'browseable', 'ASCII'),
                ('univentionShareSambaPublic', 'public', 'ASCII'),
                ('univentionShareSambaDosFilemode', 'dos filemode', 'ASCII'),
                ('univentionShareSambaHideUnreadable', 'hide unreadable',
                 'ASCII'),
                ('univentionShareSambaCreateMode', 'create mode', 'ASCII'),
                ('univentionShareSambaDirectoryMode', 'directory mode',
                 'ASCII'),
                ('univentionShareSambaForceCreateMode', 'force create mode',
                 'ASCII'),
                ('univentionShareSambaForceDirectoryMode',
                 'force directory mode', 'ASCII'),
                ('univentionShareSambaLocking', 'locking', 'ASCII'),
                ('univentionShareSambaStrictLocking', 'strict locking',
                 'ASCII'),
                ('univentionShareSambaOplocks', 'oplocks', 'ASCII'),
                ('univentionShareSambaLevel2Oplocks', 'level2 oplocks',
                 'ASCII'),
                ('univentionShareSambaFakeOplocks', 'fake oplocks', 'ASCII'),
                ('univentionShareSambaBlockSize', 'block size', 'ASCII'),
                ('univentionShareSambaCscPolicy', 'csc policy', 'UTF-8'),
                ('univentionShareSambaValidUsers', 'valid users', 'UTF-8'),
                ('univentionShareSambaInvalidUsers', 'invalid users', 'UTF-8'),
                ('univentionShareSambaForceUser', 'force user', 'UTF-8'),
                ('univentionShareSambaForceGroup', 'force group', 'UTF-8'),
                ('univentionShareSambaHideFiles', 'hide files', 'UTF-8'),
                ('univentionShareSambaNtAclSupport', 'nt acl support',
                 'ASCII'),
                ('univentionShareSambaInheritAcls', 'inherit acls', 'ASCII'),
                ('univentionShareSambaPostexec', 'postexec', 'ASCII'),
                ('univentionShareSambaPreexec', 'preexec', 'ASCII'),
                ('univentionShareSambaWriteList', 'write list', 'UTF-8'),
                ('univentionShareSambaVFSObjects', 'vfs objects', 'ASCII'),
                ('univentionShareSambaInheritOwner', 'inherit owner', 'ASCII'),
                ('univentionShareSambaInheritPermissions',
                 'inherit permissions', 'ASCII'),
                ('univentionShareSambaHostsAllow', 'hosts allow', 'ASCII'),
                ('univentionShareSambaHostsDeny', 'hosts deny', 'ASCII'),
            ]

            vfs_objects = []
            samba4_ntacl_backend = configRegistry.get('samba4/ntacl/backend',
                                                      'native')
            if samba4_ntacl_backend == 'native':
                vfs_objects.append('acl_xattr')
            elif samba4_ntacl_backend == 'tdb':
                vfs_objects.append('acl_tdb')

            vfs_objects.extend(
                x.decode('ASCII')
                for x in new.get('univentionShareSambaVFSObjects', []))

            if vfs_objects:
                print('vfs objects = %s' %
                      (' '.join(_map_quote(vfs_objects)), ),
                      file=fp)

            for attr, var, encoding in mapping:
                if not new.get(attr):
                    continue
                if attr == 'univentionShareSambaVFSObjects':
                    continue
                if attr == 'univentionShareSambaDirectoryMode' and set(
                        new['univentionSharePath']) & {b'/tmp', b'/tmp/'}:
                    continue
                if attr in ('univentionShareSambaHostsAllow',
                            'univentionShareSambaHostsDeny'):
                    print('%s = %s' % (var, (', '.join(
                        _map_quote(x.decode(encoding) for x in new[attr])))),
                          file=fp)
                elif attr in ('univentionShareSambaValidUsers',
                              'univentionShareSambaInvalidUsers'):
                    print('%s = %s' %
                          (var, _simple_quote(new[attr][0].decode(encoding))),
                          file=fp)
                else:
                    print('%s = %s' %
                          (var, _quote(new[attr][0].decode(encoding))),
                          file=fp)

            for setting in new.get(
                    'univentionShareSambaCustomSetting', []
            ):  # FIXME: vulnerable to injection of further paths and entries
                print(setting.decode('UTF-8').replace('\n', ''), file=fp)

            # implicit settings

            # acl and inherit -> map acl inherit (Bug #47850)
            if '1' in new.get('univentionShareSambaNtAclSupport',
                              []) and '1' in new.get(
                                  'univentionShareSambaInheritAcls', []):
                print('map acl inherit = yes', file=fp)

    if (not (new and old)) or (new['univentionShareSambaName'][0] !=
                               old['univentionShareSambaName'][0]):
        with SetUID(0):
            run_ucs_commit = False
            if not os.path.exists('/etc/samba/shares.conf'):
                run_ucs_commit = True
            fp = open('/etc/samba/shares.conf.temp', 'w')
            print(
                '# Warning: This file is auto-generated and will be overwritten by \n#          univention-directory-listener module. \n#          Please edit the following file instead: \n#          /etc/samba/local.conf \n  \n# Warnung: Diese Datei wurde automatisch generiert und wird durch ein \n#          univention-directory-listener Modul überschrieben werden. \n#          Ergänzungen können an folgende Datei vorgenommen werden: \n# \n#          /etc/samba/local.conf \n#',
                file=fp)

            for f in os.listdir('/etc/samba/shares.conf.d'):
                print('include = %s' %
                      _quote(os.path.join('/etc/samba/shares.conf.d', f)),
                      file=fp)
            fp.close()
            os.rename('/etc/samba/shares.conf.temp', '/etc/samba/shares.conf')
            if run_ucs_commit:
                ucr_handlers.commit(configRegistry, ['/etc/samba/smb.conf'])

    if new and ('univentionShareSambaBaseDirAppendACL' in new
                or 'univentionShareSambaBaseDirAppendACL' in old):
        with SetUID(0):
            share_path = new['univentionSharePath'][0].decode('UTF-8')
            proc = subprocess.Popen(
                ['samba-tool', 'ntacl', 'get', '--as-sddl', share_path],
                stdout=subprocess.PIPE,
                stderr=subprocess.PIPE,
                close_fds=True,
            )
            stdout, _ = proc.communicate()
            stdout = stdout.decode('UTF-8')
            prev_aces = set()
            new_aces = set()
            if 'univentionShareSambaBaseDirAppendACL' in old:
                prev_aces = set(
                    sum([
                        RE_ACE.findall(acl.decode('UTF-8'))
                        for acl in old['univentionShareSambaBaseDirAppendACL']
                    ], []))
            if 'univentionShareSambaBaseDirAppendACL' in new:
                new_aces = set(
                    sum([
                        RE_ACE.findall(acl.decode('UTF-8'))
                        for acl in new['univentionShareSambaBaseDirAppendACL']
                    ], []))

            if (new_aces and new_aces != prev_aces) or (prev_aces
                                                        and not new_aces):
                # if old != new -> delete everything from old!
                for ace in prev_aces:
                    stdout = stdout.replace(ace, '')

                # Aces might be in there from something else (like explicit setting)
                # We don't want duplicates.
                new_aces = [ace for ace in new_aces if ace not in stdout]
                # Deny must be placed before rest. This is not done implicitly.
                # Since deny might be present before, add allow.
                # Be as explicit as possible, because aliases like Du (domain users)
                # are possible.

                res = re.search(r'(O:.+?G:.+?)D:[^\(]*(.+)', stdout)
                if res:
                    # dacl-flags are removed implicitly.
                    owner = res.group(1)
                    old_aces = res.group(2)

                    old_aces = RE_ACE.findall(old_aces)
                    allow_aces = "".join(
                        [ace for ace in old_aces if 'A;' in ace])
                    deny_aces = "".join(
                        [ace for ace in old_aces if 'D;' in ace])
                    allow_aces += "".join(
                        [ace for ace in new_aces if 'A;' in ace])
                    deny_aces += "".join(
                        [ace for ace in new_aces if 'D;' in ace])

                    dacl_flags = ""
                    if new_aces:
                        dacl_flags = "PAI"
                    sddl = "{}D:{}{}{}".format(owner, dacl_flags,
                                               deny_aces.strip(),
                                               allow_aces.strip())
                    ud.debug(
                        ud.LISTENER, ud.PROCESS,
                        "Set new nt %s acl for dir %s" % (sddl, share_path))
                    proc = subprocess.Popen(
                        ['samba-tool', 'ntacl', 'set', sddl, share_path],
                        stdout=subprocess.PIPE,
                        stderr=subprocess.PIPE,
                        close_fds=True)
                    _, stderr = proc.communicate()
                    stderr = stderr.decode('UTF-8')
                    if stderr:
                        ud.debug(
                            ud.LISTENER, ud.ERROR,
                            "could not set nt acl for dir %s (%s)" %
                            (share_path, stderr))