# _*_ coding:utf-8 _*_ ''' Antivir library for Sagator (c) 2003-2022 Jan ONDREJ (SAL) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. ''' from __future__ import absolute_import from __future__ import print_function import os, sys, re, time, random, gettext import traceback, signal, resource, struct, socket import pwd, grp import base64 from threading import Thread if sys.version_info[0]>2: from urllib.parse import quote_plus, unquote_plus from email import message_from_bytes as message_from_string from io import StringIO, BytesIO unicode = str empty_string = b"" b64decode = base64.decodebytes else: from urllib import quote_plus, unquote_plus from email import message_from_string from cStringIO import StringIO BytesIO = StringIO empty_string = "" b64decode = base64.decodestring import version KB = 1024 MB = KB*KB MINUTE = 60 HOUR = MINUTE*60 DAY = HOUR*24 WEEK = DAY*7 MONTH = DAY*31 YEAR = DAY*365 SG_VER_REL = version.VERSION+'-'+version.RELEASE BUFSIZE = 10240 AZaz09 = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'\ 'abcdefghijklmnopqrstuvwxyz'\ '0123456789' allowed_chars = AZaz09+'._-' # python-2.3 socket has no SHUT_* constants if not hasattr(socket, 'SHUT_WR'): socket.SHUT_RD = 0 socket.SHUT_WR = 1 socket.SHUT_RDWR = 2 ######################################################################### ### Translations def tostr(s, enc="latin1"): if hasattr(s, "decode") and type(s)!=str: try: # decode with error replacing return s.decode(enc, "replace") except UnicodeDecodeError: return repr(s)[1:-1] return s def tobytes(s, enc="latin1"): if type(s)==bytes: return s # encode with error replacing return s.encode(enc, "replace") def tostr_list(a, enc="latin1"): def conv(x): if type(x)==str: return x elif type(x)==bytes: return x.decode(enc) return x return [conv(x) for x in a] def tostr_dict(d, enc="latin1"): def conv(x): if type(x)==str: return x elif type(x)==bytes: return x.decode(enc) return x return dict([(conv(x), conv(y)) for x,y in d.items()]) class trans_class: def __init__(self, domain='sagator', locale_dir='/usr/share/locale'): self.TR = {} self.LANG = 'en' self.DOMAIN = domain self.LOCALE_DIR = locale_dir def __call__(self, msg): return self.gettext(msg) def gettext(self, msg): return msg def lgettext(self, msg): t = self.TR[self.LANG].gettext(msg) if sys.version_info[0]<=2: return t.decode("UTF-8") return t def set_lang(self, lang): self.LANG = lang if self.LANG not in self.TR: self.TR[self.LANG] = gettext.translation( self.DOMAIN, self.LOCALE_DIR, [self.LANG], fallback=True, codeset='UTF-8') self.gettext = self.lgettext _ = trans_class() ######################################################################### ### Debuging support class debug_class: ''' Debug class. Used for debugging informations. ''' debug_level = 2 trace_level = 9 fd = 1 def __init__(self): self.set_logfile('-') def dup(self): if self.fd>2: os.dup2(self.fd, 1) os.dup2(self.fd, 2) def set_logfile(self, logfile, silent=False): self.logfile = logfile if logfile=='-': #try: # self.fd = os.dup(1) #except OSError: # self.fd = 1 self.logfd = sys.stdout self.fd = 1 else: if self.fd>2: # try to close an old file descriptor opened by sagator self.logfd.close() try: # open a new logfile self.fd = os.open(logfile, os.O_CREAT|os.O_WRONLY|os.O_APPEND, 0o640) except OSError as err: (ec, es) = err.args self.fd = 1 if not silent: self.echo(2, "WARNING: Can't open logfile: ", logfile, ": ", es) self.logfd = os.fdopen(self.fd, 'a') sys.stdout = self.logfd # redirect stderr only for non-standard logging sys.stderr = self.logfd def reopen(self): if (self.logfile!='-') and \ (self.logfile[0:len(safe.ROOT_PATH)]==safe.ROOT_PATH): lfn = self.logfile[len(safe.ROOT_PATH):] if lfn[0]!="/": lfn = "/"+lfn self.echo(7, "Reopening log %s ..." % lfn) try: oldumask = os.umask(0o002) self.fd = os.open(lfn, os.O_CREAT|os.O_WRONLY|os.O_APPEND, 0o660) os.umask(oldumask) self.logfd = os.fdopen(self.fd, 'a') self.dup() except Exception as es: self.echo(2, "WARNING: Log is not reopened! [%s]" % es) debug.traceback(4, 'debug.reopen()') # try to fix ownership try: os.chown(lfn, globals.UID, globals.GID) except OSError: pass else: self.echo(1, "ERROR: Logrotation not possible! (log isn't in chroot?)") def set_level(self, level): self.debug_level = level def echo(self, level, *s): if self.debug_level>=level: if self.debug_level>=9: o = time.strftime("%c")+': ' else: o = '' for i in s: if type(i)==type([]): o += ''.join([str(x) for x in i]) elif hasattr(i, "decode") and type(i)==bytes: try: o += i.decode("latin1") except UnicodeEncodeError: o += i else: try: o += str(i) except UnicodeEncodeError: o += i p = os.getpid() while 1: try: self.logfd.write("%5d: %s\n" % (p, o.rstrip('\r\n'))) self.logfd.flush() break except IOError as err: (ec, es) = err.args if ec!=4: break except UnicodeEncodeError: o = re.sub('[^\x00-\x7F]', '_', o) # leave only ascii chars continue except RuntimeError: break def stack(self, level, arg): self.echo(level, arg, traceback.format_stack()) def traceback(self, level, arg=''): e_type, e_value, e_tb = sys.exc_info() self.echo(level, arg, traceback.format_exception(e_type, e_value, e_tb)) def traceback_value_str(self): return ', '.join(traceback.format_exception_only( sys.exc_info()[0], sys.exc_info()[1])) debug = debug_class() ######################################################################### ### Useful functions def is_infected(level, detected=''): if level>=1.0: return True else: return False def iret(level, vname, ret): if vname: return level, vname, ret else: return 0.0, vname, ret class safe_class: ROOT_PATH = '/' def fn(self, f): if (f[0]=='/') and (self.ROOT_PATH): return os.path.join(self.ROOT_PATH, f.lstrip('/')) else: return f def open(self, name, mode='', buffering=-1): if buffering>=0: return open(self.fn(name), mode, buffering) if mode: return open(self.fn(name), mode) return open(self.fn(name)) def osopen(self, filename, flag, mode=0o777): return os.open(self.fn(filename), flag, mode) safe = safe_class() def normalize_filename(sfn): '''replace !allowed_chars from filename with _''' ofn = '' for chp in range(len(sfn)): if not sfn[chp] in allowed_chars: ofn = ofn+'_' else: ofn = ofn+sfn[chp] return str(ofn) def quote(s): if type(s)==bytes: return re.sub(b"([\\\\'])", b"\\\\\\1", s).decode("latin1") return re.sub("([\\\\'])", "\\\\\\1", s) class core_count(object): ''' Return number of cores on this system. You can define minimal value as first argument. ''' def __init__(self): self.detected = None def __call__(self, minimum=1): if self.detected is None: try: self.detected = len([ x for x in open('/proc/cpuinfo').read().split('\n') if x.startswith('processor\t:') ]) except IOError: self.detected = 0 n = self.detected # minimum number of cores is 1 if n=self.delta: # count averages t = ctime-self.last self.avg = [count*self.delta/t for count in self.data] # zero current data and time self.data = [0]*self.count self.last = ctime def get(self): ''' return countet averages ''' return self.avg def socket_settimeout(sock, seconds): ''' Set a timeout for a socket. Timeout is in seconds. ''' try: # try to use settimeout sock.settimeout(seconds) except AttributeError: # use this for python < 2.3 sock.setsockopt(socket.SOL_SOCKET, socket.SO_RCVTIMEO, struct.pack('ll', seconds, 0)) ######################################################################### ### SMTP and mail classes S_OK = _('SENT') S_TEMPFAIL = _('TEMP-FAILED') S_REJECT = _('REJECTED') S_DROP = _('DROPPED') S_CUSTOM = _('CUSTOM') S_FORCE_SEND = _('SEND-FORCED') RECV_SMTP = 0 RECV_HEADER = 1 # obsolete RECV_BODY = 2 RECV_QUIT = 3 # waiting for quit class SmtpcError(Exception): """SMTP Client Error""" ConnectError = 'ConnectError' WrongReturnCode = 'WrongReturnCode' SendmailError = 'SendmailError' class smtp: ''' Basic SMTP class. All SMTP clases are descended from this class. ''' f = False bufsize = BUFSIZE SMTP_SERVER = ('127.0.0.1', 25) reg_cmd = re.compile(br"^([A-Z]+) ", re.IGNORECASE) reg_smtp_reply = re.compile(br"^([0-9]{3}) ", re.M) reg_smtp_reply_ok = re.compile(br"^[23][0-9][0-9] ", re.M) reg_mailfrom = re.compile(br"^MAIL +FROM: *(.*?)\r?\n?$", re.IGNORECASE) reg_rcptto = re.compile(br"^RCPT +TO: *(.*?)\r?\n?$", re.IGNORECASE) reg_data = re.compile(br"^DATA", re.IGNORECASE) reg_quit = re.compile(br"^QUIT", re.IGNORECASE) reg_helo_ehlo = re.compile(br'^(?:HELO|EHLO) +(.*?)[ \r\n]', re.I|re.M) reg_xforward_addr = re.compile(br"^XFORWARD.* ADDR=\(?'?([0-9.]+)'?[, \r\n]", re.I|re.M) reg_xforward_name = re.compile(br"^XFORWARD.* NAME=([^ ]+)[ \r\n]", re.I|re.M) reg_xforward = re.compile(br"^XFORWARD ", re.IGNORECASE) reg_rset = re.compile(br"^RSET", re.IGNORECASE) class smtpc(smtp): ''' SMTP client class. ''' def __init__(self, mail_from=''): try: self.conn = socket.socket(socket.AF_INET, socket.SOCK_STREAM) socket_settimeout(self.conn, 120) self.conn.connect(self.SMTP_SERVER) except socket.error as err: (ec, es) = err.args debug.echo(0, "SMTPC: ERROR: connect: ", es) raise SmtpcError(SmtpcError.ConnectError, "451 SMTP connection refused\r\n") self.f = self.conn.makefile('rwb', self.bufsize) if mail_from: mail_from = tobytes(mail_from) ret = self.send(b'', b'2..') ret = self.send(b'EHLO %s' % socket.gethostname().encode(), b'2..') ret = self.send(b'MAIL FROM:%s' % mail_from, b'2..') def __del__(self): self.close() def send(self, data, rc=b'...'): if data!="" and data!=b"": debug.echo(5, "SMTPC>: ", data) self.conn.sendall(data+b"\r\n") ret = b'' while 1: r = self.readline() ret += r smtp_reply = self.reg_smtp_reply.search(r) if smtp_reply: break debug.echo(5, "SMTPC<: ", tostr(ret)) if not re.compile(br"^%s$" % rc).search(smtp_reply.group(1)): raise SmtpcError(SmtpcError.WrongReturnCode, b"%s!=%s" % (smtp_reply.group(1), rc)) return ret def readline(self): while True: try: return self.f.readline() except IOError as err: (ec, es) = err.args if ec!=11: # Try again raise return '' # required by pychecker def close(self): try: self.conn.shutdown(socket.SHUT_RDWR) except: pass try: self.f.close() self.conn.close() except: pass def sendmail(self, sender, recipients, ldata=b'', ret_code=""): sender = tobytes(sender) ret = b"451 SMTP connection error\r\n" try: self.send(b'', b'220') self.send(b"HELO "+socket.gethostname().encode(), b'250') self.send(b"MAIL FROM:"+(sender or b'<>'), b'250') for rec in recipients: self.send(b"RCPT TO:"+tobytes(rec), b'250') self.send(b"DATA", b'354') self.conn.sendall(tobytes(ldata)) debug.echo(5, "SMTPC>: DATA SENT") ret = self.send(b".", ret_code or b"250") except SmtpcError as ret: raise SmtpcError(SmtpcError.SendmailError, ret) try: if ret[0:3]==b"250": self.send(b"QUIT", b"221") except SmtpcError as ret: debug.echo(5, "SMTPC: quit error", ret) self.close() return ret class mail_class: ''' EMail structure with some functions. ''' reg_header = re.compile(br'^[!-9;-~]+:').search reg_header_cont = re.compile(br'^[ \t]+').search reg_recv = re.compile( br'^Received: +from +([^ ]+) +\(([^ ]+) +\[([0-9.]+)\]\)', re.M|re.I ) reg_xforward = smtp.reg_xforward_addr def __init__(self): self.addr = (b'0.0.0.0', 0, b'') # Connection address, port, name self.comm = b'' # SMTP communication self.data = b'' # Email Data (with header too) self.bodypos = 0 # Body position self.xheader = b'' # Extended header self.xhdra = {} # Extended header array self.headers = {} # parsed message headers self.recip = [] # recipient emails self.sender = '' # sender self.policy_request = {} # smtpd_policy request self.saved = () # not saved yet self.df = BytesIO() def close(self): self.df.flush() self.data = self.df.getvalue() self.df.close() del self.df # not required now self.findbody() def store(self): self.saved = (self.comm, self.data, self.bodypos, self.xheader, self.xhdra, self.headers, self.sender, self.recip) def restore(self): if self.saved: (self.comm, self.data, self.bodypos, self.xheader, self.xhdra, self.headers, self.sender, self.recip) = self.saved def findbody(self): # find end of header (beginnig of email body) df = BytesIO(self.data) lineno = 0 while True: lineno += 1 l = df.readline() if (not l) | (l=='\r\n') | (l=='\n'): break if self.reg_header(l): continue if lineno==1: break if not self.reg_header_cont(l): break self.bodypos = df.tell() df.close() # also create an headers attribute try: self.headers = message_from_string(self.data[:self.bodypos]) except: # unable to parse headers pass def normalize_header(self, lines, value=None): ''' normalize to <80 chars per line ''' if value!=None: lines += b': '+value+globals.EOL SPACE8 = b' '*8 MAXLINELEN2 = 78 MAXLINELEN = MAXLINELEN2-len(SPACE8) sublines_semi = [] for line in lines.splitlines(): if len(line.replace(b"\t", SPACE8)) <= MAXLINELEN2: sublines_semi.append(line.rstrip()) else: # split line by semicolons while len(line) > MAXLINELEN: i = line.rfind(b';', 0, MAXLINELEN) if i<0: break sublines_semi.append(line[:i+1].lstrip()) line = line[i+1:] sublines_semi.append(line.lstrip()) # now split sublines by spaces sublines_space = [] for line in sublines_semi: if len(line.replace(b"\t", SPACE8)) <= MAXLINELEN2: sublines_space.append(line) else: while len(line) > MAXLINELEN: i = line.rfind(b' ', 0, MAXLINELEN) if i<0: break sublines_space.append(line[:i]) line = line[i+1:] sublines_space.append(line) # and now join it sublines = b'' current = sublines_space.pop(0) while sublines_space: line = sublines_space.pop(0) nline = current+b' '+line if len(nline.replace(b"\t", SPACE8)) <= MAXLINELEN2: current = nline else: sublines = sublines+current+globals.EOL current = b"\t"+line sublines = sublines+current+globals.EOL return sublines def addheader(self, desc, value=None): if value==None: hdr = tobytes(desc) desc, value = desc.split(b":", 1) value = value.strip() else: hdr = tobytes(desc)+b': '+tobytes(value)+globals.EOL self.xhdra[desc] = value self.xheader += self.normalize_header(hdr) def modheader(self, frm, to): ''' Modifies header by regular expression. Returns number of modifications (max. 1). ''' h = re.compile(frm, re.MULTILINE).subn(to, self.data[:self.bodypos], 1) if h[1]>0: self.data = h[0]+self.data[self.bodypos:] return h[1] def delheader(self, hdr): reg_hdr = re.compile(br'^%s: .*?(^[!-9;-~])' % hdr, re.I|re.M|re.DOTALL) while True: reg1 = reg_hdr.search(self.data) if reg1: if reg1.end()': if l1>l2: return 1.0, self._join(d1,d2), v1+v2 else: return 0.0, b'', [] elif op=='=': if l1==l2: return 1.0, self._join(d1,d2), v1+v2 else: return 0.0, b'', [] elif op=='<=': if l1<=l2: return 1.0, self._join(d1,d2), v1+v2 else: return 0.0, b'', [] elif op=='>=': if l1>=l2: return 1.0, self._join(d1,d2), v1+v2 else: return 0.0, b'', [] elif op=='!=': if l1!=l2: return 1.0, self._join(d1,d2), v1+v2 else: return 0.0, b'', [] else: raise ScannerError("Unknown operator: %s" % tostr(self._oper[0])) def param(self, key, value=None): if self.scanner.param(key, value): return self.scannerb.param(key, value) return '' def help(self): h = self.scanner.help() h.update(self.scannerb.help()) return h def rcpt_signature(self, rcpt): siga = self.scanner.rcpt_signature(rcpt) if self.scannerb: sigb = self.scannerb.rcpt_signature(rcpt) if siga and sigb: return siga+self._oper[0]+sigb else: if siga: return self._oper[0]+siga return '' def scanbuffer(self, buffer, args={}): l, d, v = {}, {}, {} if self._oper[0] in ('+','-','*','/','<','>','=','<=','>=','!='): for i in [1, 2]: self._oper[i].prescan() l[i], d[i], v[i] = self._oper[i].scanbuffer(buffer, args) self._oper[i].postscan(l[i], d[i], v[i]) return self._eval(l[1], d[1], v[1], l[2], d[2], v[2]) elif self._oper[0]=='|': try: self._oper[1].prescan() l, d, v = self._oper[1].scanbuffer(buffer, args) self._oper[1].postscan(l, d, v) return l, d, v except: self._oper[2].prescan() l, d, v = self._oper[2].scanbuffer(buffer, args) self._oper[2].postscan(l, d, v) return l, d, v elif self._oper[0]=='&': self._oper[1].prescan() l1, d1, v1 = self._oper[1].scanbuffer(buffer, args) self._oper[1].postscan(l1, d1, v1) if is_infected(l1): self._oper[2].prescan() l2, d2, v2 = self._oper[2].scanbuffer(buffer, args) self._oper[2].postscan(l2, d2, v2) return l1*l2, self._join(d1, d2, b'&'), v1+[b'&']+v2 else: return 0.0, b'', [] elif self._oper[0]=='~': try: self._oper[1].prescan() l, d, v = self._oper[1].scanbuffer(buffer, args) except: debug.echo(4, "Scanner %s failed, recovering..." % self._oper[1].name) if self._oper[1].is_interscanner: l, d, v = self._oper[1].get('child_status') else: return 0.0, b'', [] self._oper[1].postscan(l, d, v) return l, d, v else: raise ScannerError("Unknown operator: %s" % tostr(self._oper[0])) def scanfile(self, files, dirname='', args={}): l, d, v = {}, {}, {} if self._oper[0] in ('+','-','*','/','<','>','=','<=','>=','!='): for i in [1, 2]: self._oper[i].prescan() l[i], d[i], v[i] = self._oper[i].scanfile(files, dirname) self._oper[i].postscan(l[i], d[i], v[i]) return self._eval(l[1], d[1], v[1], l[2], d[2], v[2]) elif self._oper[0]=='|': try: self._oper[1].prescan() l, d, v = self._oper[1].scanfile(files, dirname) self._oper[1].postscan(l, d, v) return l, d, v except: self._oper[2].prescan() l, d, v = self._oper[2].scanfile(files, dirname) self._oper[2].postscan(l, d, v) return l, d, v elif self._oper[0]=='&': self._oper[1].prescan() l1, d1, v1 = self._oper[1].scanfile(files, dirname) self._oper[1].postscan(l1, d1, v1) if is_infected(l1): self._oper[2].prescan() l2, d2, v2 = self._oper[2].scanfile(files, dirname) self._oper[2].postscan(l2, d2, v2) return l1*l2, self._join(d1, d2, b'&'), v1+[b'&']+v2 else: return 0.0, b'', [] elif self._oper[0]=='~': try: self._oper[1].prescan() l, d, v = self._oper[1].scanfile(files, dirname) except: debug.echo(4, "Scanner %s failed, recovering..." % self._oper[1].name) if self._oper[1].is_interscanner: l, d, v = self._oper[1].get('child_status') else: return 0.0, b'', [] self._oper[1].postscan(l, d, v) return l, d, v else: raise ScannerError("Unknown operator: %s" % tostr(self._oper[0])) def prescan(self): pass def postscan(self, level, vir, ret): pass def destroy(self): self._oper[1].destroy() if self._oper[2]: self._oper[2].destroy() def reinit(self): self._oper[1].reinit() if self._oper[2]: self._oper[2].reinit() def get(self, var): try: return getattr(self._oper[1], var) except AttributeError: try: return eval(self._oper[2], var) except AttributeError: return "UNKNOWN" def __add__(self, second): return scanoper('+', self, second) def __sub__(self, second): return scanoper('-', self, second) def __mul__(self, second): return scanoper('*', self, second) def __div__(self, second): return scanoper('/', self, second) def __or__(self, second): return scanoper('|', self, second) def __and__(self, second): return scanoper('&', self, second) def __invert__(self): return scanoper('~', self) def __lt__(self, second): return scanoper('<', self, second) def __gt__(self, second): return scanoper('>', self, second) def __eq__(self, second): return scanoper('=', self, second) def __le__(self, second): return scanoper('<=', self, second) def __ge__(self, second): return scanoper('>=', self, second) def __ne__(self, second): return scanoper('!=', self, second) class ascanner(scanoper): ''' Default scanner user for building all other realscanners. ''' name = 'AScanner()' is_spamscan = False is_policy_scanner = False is_interscanner = False ignore_name = False scanner = 0 def __init__(self): pass def destroy(self): pass def help(self): return {} def param(self, key, value=None): return 'Unknown parameter: %s' % key def prescan(self): '''This function is called before running a scanner''' debug.echo(5, "Running: ", self.name) def postscan(self, level, vir, ret): '''This function is called after running a scanner''' self.child_status = (level, vir, ret) debug.echo(4, "Values: %f, '%s', %s" % (level, tostr(vir), tostr_list(ret))) if is_infected(level): if not self.ignore_name: globals.found_by = self debug.echo(5, "Found %s by %s, level %f" % (tostr(vir), self.name, level)) def rcpt_signature(self, rcpt): return self.name def scanbuffer(self, buffer, args={}): raise ScannerError('Not implemented') def scanfile(self, files, dirname='', args={}): raise ScannerError('Not implemented') def reinit(self): pass def get(self, var): try: return getattr(self, var) except AttributeError: return "UNKNOWN" class interscanner(ascanner): ''' Default scanner used for building all other interscanners. ''' name = 'AInterScanner()' is_interscanner = True scanners = [] def rename(self, scanners): n = [s.name for s in scanners] self.name = self.name.replace("()", "("+', '.join(n)+")") if self.name=="": self.name = ','.join(n) def destroy(self): self.scanner.destroy() def prescan(self): '''This function is called before running a scanner''' debug.echo(5, "Running: ", self.name) def postscan(self, level, vir, ret): '''This function is called after running a scanner''' self.child_status = (level, vir, ret) def rcpt_signature(self, rcpt): return "%s(%s)" % ( self.name.split('(', 1)[0], # only it's name self.scanner.rcpt_signature(rcpt) ) def reinit(self): self.scanner.reinit() def param(self, key, value=None): return self.scanner.param(key, value) def help(self): return self.scanner.help() def get(self, var): try: r = getattr(self, var) if not r: r = self.scanner.get(var) return r except AttributeError: return self.scanner.get(var) class globals_class: QFNAME = '' # quarantine filename (generated) USER = '' GROUP = '' UID = 0 GID = 0 SRV = [] EOL = b'\r\n' DBC = None # a policy database connection id = None # sagator's message ID scan_only = False # only scan or also send reports? daemon = True # daemonize or not? fork_id = 0 # Fork internal ID pid_file = None # PID filename pidf = None # PID file (as python file object) # policy specifications sender_policy = [] recipient_policy = [] def __init__(self): self.reset() def reset(self, action=S_REJECT): self.found_by = ascanner self.QFNAME = '' self.ACTION = action self.PREPEND = '' self.RCPT_MATCH = {} def action(self, level, detected=''): if is_infected(level): return self.ACTION else: return S_OK def gen_id(self, time1, time2): self.id = "%s-%04d-%05d-%s@%s" \ % (time1, time2, os.getpid(), randomchars(6), socket.gethostname()) def setuidgid(self, user, group): if user: try: globals.UID = pwd.getpwnam(user)[2] globals.USER = user except KeyError as err_str: debug.echo(0, "ERROR, getpwnam: %s %s, using current user" % ([user], err_str)) globals.UID = os.getuid() globals.USER = pwd.getpwuid(globals.UID)[0] if group: try: globals.GID = grp.getgrnam(group)[2] globals.GROUP = group except KeyError as err_str: debug.echo(0, "ERROR, getgrnam: %s %s, using current group" % ([group], err_str)) globals.GID = os.getgid() globals.GROUP = grp.getgrgid(globals.GID)[0] globals = globals_class()