root/trunk/spidentd.py

Revision 156, 14.5 kB (checked in by jajcus, 3 years ago)

- fixed uninitialized variable after exception is cought

  • Property svn:executable set to *
Line 
1 #!/usr/bin/python
2
3 """ Simple Programmable Ident (RFC1413) Daemon """
4
5 import sys
6 sys.path=sys.path[1:]
7
8 import signal
9 import socket
10 import select
11 import threading
12 import os
13 import errno
14 import pwd
15 import grp
16 import getopt
17 import re
18
19 class Driver:
20         def __init__(self):
21                 pass
22         def lookup(self,localport,localip,remoteport,remoteip):
23                 return None
24
25 class NoUserDriver(Driver):
26         def lookup(self,localport,localip,remoteport,remoteip):
27                 return "ERROR:NO-USER"
28
29 class HiddenUserDriver(Driver):
30         def lookup(self,localport,localip,remoteport,remoteip):
31                 return "ERROR:HIDDEN-USER"
32
33 class FailDriver(Driver):
34         def lookup(self,localport,localip,remoteport,remoteip):
35                 return "ERROR:UNKNOWN-ERROR"
36
37 class StaticDriver(Driver):
38         def __init__(self,reply):
39                 self.reply=reply
40         def lookup(self,localport,localip,remoteport,remoteip):
41                 return self.reply
42
43 class Mapping:
44         def __init__(self):
45                 pass
46         def lookup(self,user):
47                 return user
48
49 class FileMapping(Mapping):
50         def __init__(self,path):
51                 self.mapping={}
52                 for l in file(path).xreadlines():
53                         l=l.strip()
54                         if not l:
55                                 continue
56                         key,val=l.split(":")
57                         self.mapping[key]=val
58         def lookup(self,user):
59                 return self.mapping.get(user,user)
60
61 class RealDriver(Driver):
62         def lookup(self,localport,localip,remoteport,remoteip):
63                 try:
64                         f=file("/proc/net/tcp","r")
65                 except IOError:
66                         print >>sys.stderr,"Couldn't open /proc/net/tcp"
67                         return None
68                 f.readline()
69                 li=[long(i) for i in localip.split(".")]
70                 localip=(li[0]<<24)+(li[1]<<16)+(li[2]<<8)+li[3]
71                 ri=[long(i) for i in remoteip.split(".")]
72                 remoteip=(ri[0]<<24)+(ri[1]<<16)+(ri[2]<<8)+ri[3]
73                 for l in f.xreadlines():
74                         sp=l.split()
75                         if len(sp)<8:
76                                 continue
77                         locip,locport=sp[1].split(":")
78                         locip=socket.htonl(long(locip,16))&0xffffffffL
79                         locport=int(locport,16)
80                         remip,remport=sp[2].split(":")
81                         remip=socket.htonl(long(remip,16))&0xffffffffL
82                         remport=int(remport,16)
83                         if (localip,localport,remoteip,remoteport)!=(locip,locport,remip,remport):
84                                 continue
85                         uid=int(sp[7])
86                         try:
87                                 pw=pwd.getpwuid(uid)
88                                 return pw[0]
89                         except:
90                                 return "ERROR:NO-USER"
91                 return None
92
93 class SocketDriverClient:
94         add_re=re.compile(r"add (\d+\.\d+\.\d+\.\d+):(\d+) (\d+\.\d+\.\d+\.\d+):(\d+) (.*)")
95         remove_re=re.compile(r"remove (\d+\.\d+\.\d+\.\d+):(\d+) (\d+\.\d+\.\d+\.\d+):(\d+)")
96         def __init__(self,driver,sock):
97                 self.socket=sock
98                 self.driver=driver
99                 self.thread=threading.Thread(target=self.run_thread)
100                 self.thread.setDaemon(1)
101                 self.conn_users={}
102                 self.buf=""
103                 self.thread.start()
104
105         def run_thread(self):
106                 try:
107                         self._run_thread()
108                 finally:
109                         self.conn_users={}
110                         try:
111                                 self.driver.clients.remove(self)
112                         except ValueError:
113                                 pass
114                         try:
115                                 self.sock.close()
116                         except:
117                                 pass
118
119         def _run_thread(self):
120                 global exit
121                 while not exit:
122                         l=self.read_line()
123                         if not l:
124                                 break
125                         print >>sys.stderr,"Got %r on programming socket" % (l,)
126                         m=self.add_re.match(l)
127                         if m:
128                                 localip,localport,remoteip,remoteport,user=m.groups()
129                                 self.conn_users[(int(localport),localip,
130                                                 int(remoteport),remoteip)]=user
131                                 continue
132                         m=self.remove_re.match(l)
133                         if m:
134                                 localip,localport,remoteip,remoteport=m.groups()
135                                 try:
136                                         del self.conn_users[(int(localport),localip,
137                                                         int(remoteport),remoteip)]
138                                 except KeyError:
139                                         pass
140                                 continue
141                         print >>sys.stderr,"Bad protocol on programming socket"
142
143         def read_line(self):
144                 while 1:
145                         if "\n" in self.buf:
146                                 line,self.buf=self.buf.split("\n",1)
147                                 return line
148                         if len(self.buf)>1024:
149                                 print >>sys.stderr,"Input line too long: %r" % (self.buf,)
150                                 return None
151                         r=self.socket.recv(1024)
152                         if not r:
153                                 return None
154                         self.buf+=r
155
156
157 class SocketDriver(Driver):
158         def __init__(self,path):
159                 global socketmode,socketgroup,user,group
160                 self.socket=socket.socket(socket.AF_UNIX)
161                 try:
162                         os.unlink(path)
163                 except:
164                         pass
165                 self.socket.bind(path)
166                 if socketmode is not None:
167                         os.chmod(path,socketmode)
168                 if os.getuid()==0:
169                         gid=None
170                         if socketgroup is not None:
171                                 gid=grp.getgrnam(socketgroup)[2]
172                         elif group is not None:
173                                 gid=grp.getgrnam(group)[2]
174                         elif user is not None:
175                                 gid=pwd.getpwnam(user)[3]
176                         if gid:
177                                 os.chown(path,0,gid)
178                 self.socket.listen(1)
179                 self.clients=[]
180                 register_listening_socket(self.socket,self.accept_connection)
181         def lookup(self,localport,localip,remoteport,remoteip):
182                 for c in self.clients:
183                         connuser=c.conn_users.get((localport,localip,remoteport,remoteip))
184                         if connuser:
185                                 return connuser
186                 return None
187         def accept_connection(self,sock):
188                 sock,addr=self.socket.accept()
189                 print >>sys.stderr,"Client connection from: %r" % (addr,)
190                 self.clients.append(SocketDriverClient(self,sock))
191
192 listening_socket_handlers={}
193 def register_listening_socket(sock,handler):
194         global listening_sockets
195         listening_socket_handlers[sock]=handler
196
197 def input_thread(sock,addr):
198         print >>sys.stderr,"Connection from: %r" % (addr,)
199         localip=sock.getsockname()[0]
200         remoteip=addr[0]
201         buf=""
202         while 1:
203                 try:
204                         r=sock.recv(1024)
205                 except socket.error,e:
206                         if e.args[0]==errno.EINTR:
207                                 continue
208                         r=None
209                 if not r:
210                         print >>sys.stderr,"No query"
211                         sock.close()
212                         return
213                 buf+=r
214                 if len(buf)>1024:
215                         print >>sys.stderr,"Query too long"
216                         sock.close()
217                         return
218                 if buf.find("\r\n")>=0:
219                         break
220         query=buf.split("\r",1)[0]
221         print >>sys.stderr,"Query: %r" % (query,)
222         sp=query.split(",")
223         if len(sp)!=2:
224                 print >>sys.stderr,"Invalid query"
225                 sock.close()
226                 return
227         local,remote=sp
228         try:
229                 local=int(local.strip())
230                 remote=int(remote.strip())
231         except ValueError:
232                 sock.send("%s,%s:ERROR:INVALID-PORT\r\n" % (local,remote))
233                 sock.close()
234                 print >>sys.stderr,"Invalid query"
235                 return
236         if local<1 or local>65535 or remote<1 or remote>65535:
237                 sock.send("%i,%i:ERROR:INVALID-PORT\r\n" % (local,remote))
238                 sock.close()
239                 print >>sys.stderr,"Invalid query"
240                 return
241         reply=None
242         mapping=None
243         for d in drivers:
244                 if isinstance(d,Mapping):
245                         mapping=d
246                         continue
247                 if not isinstance(d,Driver):
248                         continue
249                 r=d.lookup(local,localip,remote,remoteip)
250                 if r is None:
251                         continue
252                 if mapping and not r.startswith("ERROR:"):
253                         oldr=r
254                         r=mapping.lookup(r)
255                         print "%r -> %r" % (oldr,r)
256                 if r.startswith("ERROR:"):
257                         reply="%i,%i:%s" % (local,remote,r)
258                         break
259                 else:
260                         reply="%i,%i:USERID:UNIX:%s" % (local,remote,r)
261                         break
262         if not reply:
263                 reply="%i,%i:ERROR:NO-USER" % (local,remote)
264         print >>sys.stderr,"Reply: %s" % (reply,)
265         sock.send(reply+"\r\n")
266         sock.close()
267         return
268
269 def signal_handler(signum,frame):
270         global exit
271         exit=1
272         print >>sys.stderr,"Signal %i received, exiting." % (signum,)
273
274 def accept_connection(sock):
275         th=threading.Thread(target=input_thread,args=sock.accept())
276         th.setDaemon(1)
277         th.start()
278
279 def usage():
280         print "Simple Programmable Ident (RFC1413) Daemon"
281         print "(c) 2004 Jacek Konieczny"
282         print
283         print "Usage:"
284         print "    %s [options] [driver...]"
285         print
286         print "Options:"
287         print "    -h --help              display this help and exit."
288         print "    -i ADDR --ip=ADDR      bind to IP address ADDR."
289         print "    -p PORT --port=PORT    bind to port PORT (default: 113)."
290         print "    -u USER --user=USER    when started with uid=0, switch "
291         print "                           to user USER (default: 'nobody')"
292         print "    -p GROUP --group=GROUP when started with uid=0, switch "
293         print "                           to group GROUP (default: nobody's group)"
294         print "    --socketmode=MODE      access mode for listening socket (--socket driver)"
295         print "    --socketgroup=MODE     owner group for listening socket (--socket driver)"
296         print "Drivers:"
297         print "    --nouser               always reply with NO-USER error."
298         print "    --hidden               always reply with HIDDN-USER error."
299         print "    --fail                 always reply with UNKNOWN-ERROR error."
300         print "    --real                 reply with real connection user name (currently Linux"
301         print "                           only)."
302         print "    --map=FILE             user mapping file FILE for results of the following "
303         print "                           drivers"
304         print "    --static=USER          always reply with the same USER reply."
305         print "    --socket=PATH          listen on UNIX socket PATH for other servers"
306         print "                           registering their connections."
307
308
309 user="nobody"
310 group=None
311 ip="0.0.0.0"
312 port=113
313 drivers=[]
314 socketmode=0775
315 socketgroup=None
316
317 try:
318         opts,args=getopt.getopt(sys.argv[1:], "hi:p:u:g:", ["help","ip=","port=","user=","group=","map=","socketmode=","socketgroup=",
319                 "nouser","hidden","real","fail","static=","socket="])
320 except Exception,e:
321         print e
322         usage()
323         sys.exit(2)
324 for o,a in opts:
325         if o in ("-h","--help"):
326                 usage()
327                 sys.exit()
328         if o in ("-i","--ip"):
329                 ip=a
330         if o in ("-p","--port"):
331                 port=int(a)
332         if o in ("-u","--user"):
333                 user=a
334         if o in ("-g","--group"):
335                 group=a
336         if o in ("--socketmode",):
337                 socketmode=int(a,8)
338         if o in ("--socketgroup",):
339                 socketgroup=a
340         if o in ("--nouser",):
341                 drivers.append(NoUserDriver())
342         if o in ("--hidden",):
343                 drivers.append(HiddenUserDriver())
344         if o in ("--fail",):
345                 drivers.append(FailDriver())
346         if o in ("--real",):
347                 drivers.append(RealDriver())
348         if o in ("--map",):
349                 drivers.append(FileMapping(a))
350         if o in ("--static",):
351                 drivers.append(StaticDriver(a))
352         if o in ("--socket",):
353                 drivers.append(SocketDriver(a))
354 drivers.append(FailDriver())
355
356 if args:
357         usage()
358         sys.exit(2)
359
360 sock=socket.socket()
361 try:
362         sock.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
363 except:
364         pass
365 sock.bind((ip,port))
366
367 if os.getuid()==0:
368         pw=pwd.getpwnam(user)
369         uid=pw[2]
370         gid=pw[3]
371         if group:
372                 gr=grp.getgrnam(group)
373                 gid=gr[2]
374         os.setgroups([gid])
375         os.setgid(gid)
376         os.setuid(uid)
377
378 sock.listen(1)
379 buf=""
380 exit=0
381
382 register_listening_socket(sock,accept_connection)
383
384 signal.signal(signal.SIGINT,signal_handler)
385 signal.signal(signal.SIGPIPE,signal_handler)
386 signal.signal(signal.SIGTERM,signal_handler)
387
388 while not exit and listening_socket_handlers:
389         try:
390                 sockets=listening_socket_handlers.keys()
391                 id,od,ed=select.select(sockets,[],sockets,1)
392         except select.error,e:
393                 if e.args[0]==errno.EINTR:
394                         continue
395         for s in id:
396                 listening_socket_handlers[s](s)
397         for s in ed:
398                 print >>sys.stderr,"Error on socket: %r" % (s,)
399                 del listening_socket_handlers[s]
400 # vi: sts=4 et sw=4
401
Note: See TracBrowser for help on using the browser.