#!/usr/bin/python
import os
import time
import socket # for gethostbyaddr
files = {}
logs = []
def logbase(s):
files["base"] = s
# realm support is a little odd, since it isn't mainline:
# just use class@realm
def log(zclass, zinst="*", zfile=None):
if not zfile:
zfile = zclass
recip = "*"
atsign = zclass.rfind("@")
if atsign > -1:
recip += zclass[atsign:]
zclass = zclass[:atsign]
zpath = os.path.join(files["base"], zfile)
logset = [recip, zclass.lower(), zinst.lower(), zpath]
print "Z:", zpath, logset
if zpath in files:
# consider, later, just aliasing the right filehandle, so this isn't an error
raise Exception("duplicate path %s vs. %s" % (files[zpath], logset))
logs.append(logset)
files[zpath] = logset
def format_notice(n):
msgs = n.message.split("\0")
if len(msgs) == 1:
msgsig = ""
msgbody = msgs[0]
elif len(msgs) == 2:
msgsig = msgs[0]
msgbody = msgs[1]
else:
msgsig = "mangled/multifield message"
msgbody = "|\n".join(msgs)
# if n.opcode != "": include something
realm_suffix = "@" + zzz.zgetrealm()
sender = n.sender
if sender.endswith(realm_suffix):
sender = sender.replace(realm_suffix, "")
try:
sender_host = socket.gethostbyaddr(n.sender_addr)[0]
# consider paranoia: roll it forward and if they differ, log it
except socket.herror:
sender_host = n.sender_addr
# also - assume auth, but log bad-auth on sender (false-from?)
return "\n".join(["Instance: %(inst)s Time: %(time)s Host: %(host)s" %
{"inst":n.zinst, "time":time.ctime(n.time), "host":sender_host},
"From: %(sig)s <%(sender)s>" % {"sig": msgsig, "sender": sender},
"",
msgbody,
])
import zzz, zzz2, select
def logsub(zport, classfile, instfile):
for r,c,i,f in logs:
if i == "*":
classfile[c] = open(f, "a")
else:
instfile[c,i] = open(f, "a")
zzz2.Subscription(r, c, i).sub(zport)
def run(helpertime=None, helper=None):
n = zzz.Notice("", "", "", "", "", "")
zport = zzz.openport()
classfile = {}
instfile = {}
logsub(zport, classfile, instfile)
zfd = zzz.zgetfd()
scantime = None
if helpertime:
scantime = helpertime/4
last_help_time = time.time()
while 1:
rr,ww,xx = select.select([zfd], [], [], scantime) # timeout *not* mutable
if rr:
while n.pending():
n.receive()
try:
dstf = instfile[n.zclass.lower(),n.zinst.lower()]
except KeyError:
dstf = classfile[n.zclass.lower()]
print "sending to", dstf
print >>dstf, format_notice(n)
dstf.flush()
else: # must be a timeout...
if helpertime:
if time.time() - last_help_time >= helpertime:
helper()
logsub(zport, classfile, instfile)
last_help_time = time.time()
# innovations to try:
#
# sub to logins for every user you see; stash them (shelf?) and note them on the run
# (per class, that is.) also zlocate the users after they show up and see if they change,
# and log that too.
#
# stat the config file and reread it? (clearly a reason not to use the python syntax.)
# maybe take some authenticated (or local) control-messages, but that means
# writing out the config too.
#
# support explicit reauthentication
def mk_kinit_helper(princ):
def kinit_helper():
os.spawnlp(os.P_WAIT, "kinit",
"kinit", "-k", "-t", "ztab", princ)
os.spawnlp(os.P_WAIT, "krb524init",
"krb524init")
return kinit_helper
if __name__ == "__main__":
import sys
try:
prog, base, subsfile, princ = sys.argv
except ValueError:
sys.exit("Usage: " + sys.argv[0] + " basedir subsfile principal")
logbase(base)
for subline in file(subsfile):
if subline.startswith("#"):
continue
zclass, zinst, zrecip = subline.rstrip().split(",")
if zrecip.startswith("*@"):
atsign = zrecip.rfind("@")
zrealm = zrecip[atsign:]
# not actually necessary, and forces a zinit before we have tickets
# if zrealm[1:] != zzz.zgetrealm():
zclass += zrealm
zrecip = zrecip[:atsign]
if zrecip == "%me%": zrecip = zzz.sender()
if zrecip != "*": raise Exception("don't log personals yet (%s,%s)" % (zclass,zinst))
print (zrecip, zclass, zinst)
log(zclass, zinst)
helper = mk_kinit_helper(princ)
os.putenv("KRB5CCNAME", "FILE:ztab.k5")
os.putenv("KRBTKFILE", "ztab.k4")
helper()
run(60*60, helper)
# later, fix the case where a class is the same in two realms - how do we even
# noticed which we got from (and is this reportable/exploitable?)