Package entropy :: Package services :: Module interfaces

Source Code for Module entropy.services.interfaces

   1  # -*- coding: utf-8 -*- 
   2  ''' 
   3      # DESCRIPTION: 
   4      # Entropy Object Oriented Interface 
   5   
   6      Copyright (C) 2007-2009 Fabio Erculiani 
   7   
   8      This program is free software; you can redistribute it and/or modify 
   9      it under the terms of the GNU General Public License as published by 
  10      the Free Software Foundation; either version 2 of the License, or 
  11      (at your option) any later version. 
  12   
  13      This program is distributed in the hope that it will be useful, 
  14      but WITHOUT ANY WARRANTY; without even the implied warranty of 
  15      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
  16      GNU General Public License for more details. 
  17   
  18      You should have received a copy of the GNU General Public License 
  19      along with this program; if not, write to the Free Software 
  20      Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
  21  ''' 
  22   
  23  from __future__ import with_statement 
  24  import os 
  25  import shutil 
  26  import time 
  27  from entropy.const import etpConst, ETP_LOGLEVEL_NORMAL, ETP_LOGPRI_INFO, \ 
  28      const_setup_perms 
  29  from entropy.exceptions import * 
  30  from entropy.services.skel import SocketAuthenticator, SocketCommands 
  31  from entropy.i18n import _ 
  32  from entropy.output import blue, red, darkgreen 
  33   
34 -class SocketHost:
35 36 import socket 37 import SocketServer 38 from threading import Thread 39
40 - class BasicPamAuthenticator(SocketAuthenticator):
41 42 import entropy.tools as entropyTools 43
44 - def __init__(self, HostInterface, *args, **kwargs):
45 self.valid_auth_types = [ "plain", "shadow", "md5" ] 46 SocketAuthenticator.__init__(self, HostInterface)
47
48 - def docmd_login(self, arguments):
49 50 # filter n00bs 51 if not arguments or (len(arguments) != 3): 52 return False,None,None,'wrong arguments' 53 54 user = arguments[0] 55 auth_type = arguments[1] 56 auth_string = arguments[2] 57 58 # check auth type validity 59 if auth_type not in self.valid_auth_types: 60 return False,user,None,'invalid auth type' 61 62 udata = self.__get_user_data(user) 63 if udata == None: 64 return False,user,None,'invalid user' 65 66 uid = udata[2] 67 # check if user is in the Entropy group 68 if not self.entropyTools.is_user_in_entropy_group(uid): 69 return False,user,uid,'user not in %s group' % (etpConst['sysgroup'],) 70 71 # now validate password 72 valid = self.__validate_auth(user,auth_type,auth_string) 73 if not valid: 74 return False,user,uid,'auth failed' 75 76 if not uid: 77 self.HostInterface.sessions[self.session]['admin'] = True 78 else: 79 self.HostInterface.sessions[self.session]['user'] = True 80 return True,user,uid,"ok"
81 82 # it we get here is because user is logged in
83 - def docmd_userdata(self):
84 85 auth_uid = self.HostInterface.sessions[self.session]['auth_uid'] 86 mydata = {} 87 udata = self.__get_uid_data(auth_uid) 88 if udata: 89 mydata['username'] = udata[0] 90 mydata['uid'] = udata[2] 91 mydata['gid'] = udata[3] 92 mydata['references'] = udata[4] 93 mydata['home'] = udata[5] 94 mydata['shell'] = udata[6] 95 return True,mydata,'ok'
96
97 - def __get_uid_data(self, user_id):
98 import pwd 99 # check user validty 100 try: 101 udata = pwd.getpwuid(user_id) 102 except KeyError: 103 return None 104 return udata
105
106 - def __get_user_data(self, user):
107 import pwd 108 # check user validty 109 try: 110 udata = pwd.getpwnam(user) 111 except KeyError: 112 return None 113 return udata
114
115 - def __validate_auth(self, user, auth_type, auth_string):
116 valid = False 117 if auth_type == "plain": 118 valid = self.__do_auth(user, auth_string) 119 elif auth_type == "shadow": 120 valid = self.__do_auth(user, auth_string, auth_type = "shadow") 121 elif auth_type == "md5": 122 valid = self.__do_auth(user, auth_string, auth_type = "md5") 123 return valid
124
125 - def __do_auth(self, user, password, auth_type = None):
126 import spwd 127 128 try: 129 enc_pass = spwd.getspnam(user)[1] 130 except KeyError: 131 return False 132 133 if auth_type == None: # plain 134 import crypt 135 generated_pass = crypt.crypt(str(password), enc_pass) 136 elif auth_type == "shadow": 137 generated_pass = password 138 elif auth_type == "md5": # md5 139 import hashlib 140 m = hashlib.md5() 141 m.update(enc_pass) 142 enc_pass = m.hexdigest() 143 generated_pass = str(password) 144 else: # haha, fuck! 145 generated_pass = None 146 147 if generated_pass == enc_pass: 148 return True 149 return False
150
151 - def docmd_logout(self, myargs):
152 153 # filter n00bs 154 if (len(myargs) < 1) or (len(myargs) > 1): 155 return False,None,'wrong arguments' 156 157 user = myargs[0] 158 # filter n00bs 159 if not user or not isinstance(user,basestring): 160 return False,None,"wrong user" 161 162 return True,user,"ok"
163
164 - def set_exc_permissions(self, uid, gid):
165 if gid != None: 166 os.setgid(gid) 167 if uid != None: 168 os.setuid(uid)
169
170 - def hide_login_data(self, args):
171 myargs = args[:] 172 myargs[-1] = 'hidden' 173 return myargs
174
175 - def terminate_instance(self):
176 pass
177
178 - class HostServerMixin(SocketServer.ThreadingMixIn, SocketServer.TCPServer):
179
180 - class ConnWrapper:
181 ''' 182 Base class for implementing the rest of the wrappers in this module. 183 Operates by taking a connection argument which is used when 'self' doesn't 184 provide the functionality being requested. 185 '''
186 - def __init__(self, connection) :
187 self.connection = connection
188
189 - def __getattr__(self, function) :
190 return getattr(self.connection, function)
191 192 import entropy.tools as entropyTools 193 import socket as socket_mod 194 import select 195 import SocketServer 196 # This means the main server will not do the equivalent of a 197 # pthread_join() on the new threads. With this set, Ctrl-C will 198 # kill the server reliably. 199 daemon_threads = True 200 201 # By setting this we allow the server to re-bind to the address by 202 # setting SO_REUSEADDR, meaning you don't have to wait for 203 # timeouts when you kill the server and the sockets don't get 204 # closed down correctly. 205 allow_reuse_address = True 206
207 - def __init__(self, server_address, RequestHandlerClass, processor, HostInterface, authorized_clients_only = False):
208 209 self.alive = True 210 self.socket = self.socket_mod 211 self.processor = processor 212 self.server_address = server_address 213 self.HostInterface = HostInterface 214 self.SSL = self.HostInterface.SSL 215 self.real_sock = None 216 self.ssl_authorized_clients_only = authorized_clients_only 217 218 if self.SSL: 219 self.SocketServer.BaseServer.__init__(self, server_address, RequestHandlerClass) 220 self.load_ssl_context() 221 self.make_ssl_connection_alive() 222 else: 223 try: 224 self.SocketServer.TCPServer.__init__(self, server_address, RequestHandlerClass) 225 except self.socket_mod.error, e: 226 if e[0] == 13: 227 raise ConnectionError('ConnectionError: %s' % (_("Cannot bind the service"),)) 228 raise
229
230 - def load_ssl_context(self):
231 # setup an SSL context. 232 self.context = self.SSL['m'].Context(self.SSL['m'].SSLv23_METHOD) 233 self.context.set_verify(self.SSL['m'].VERIFY_PEER, self.verify_ssl_cb) # ask for a certificate 234 self.context.set_options(self.SSL['m'].OP_NO_SSLv2) 235 # load up certificate stuff. 236 self.context.use_privatekey_file(self.SSL['key']) 237 self.context.use_certificate_file(self.SSL['cert']) 238 self.context.load_verify_locations(self.SSL['ca_cert']) 239 self.context.load_client_ca(self.SSL['ca_cert']) 240 self.HostInterface.updateProgress('SSL context loaded, key: %s - cert: %s, CA cert: %s, CA pkey: %s' % ( 241 self.SSL['key'], 242 self.SSL['cert'], 243 self.SSL['ca_cert'], 244 self.SSL['ca_pkey'] 245 ) 246 )
247
248 - def make_ssl_connection_alive(self):
249 self.real_sock = self.socket_mod.socket(self.address_family, self.socket_type) 250 self.socket = self.ConnWrapper(self.SSL['m'].Connection(self.context, self.real_sock)) 251 self.server_bind() 252 self.server_activate()
253 254 # this function should do the authentication checking to see that 255 # the client is who they say they are.
256 - def verify_ssl_cb(self, conn, cert, errnum, depth, ok) :
257 return ok
258
259 - def verify_request(self, request, client_address):
260 261 self.do_ssl = self.HostInterface.SSL 262 if self.do_ssl: self.do_ssl = True 263 else: self.do_ssl = False 264 265 allowed = self.ip_blacklist_check(client_address[0]) 266 if allowed: allowed = self.ip_max_connections_check(client_address[0]) 267 if not allowed: 268 self.HostInterface.updateProgress( 269 '[from: %s | SSL: %s] connection refused, ip blacklisted or maximum connections per IP reached' % ( 270 client_address, 271 self.do_ssl, 272 ) 273 ) 274 return False 275 276 allowed = self.max_connections_check(request) 277 if not allowed: 278 self.HostInterface.updateProgress( 279 '[from: %s | SSL: %s] connection refused (max connections reached: %s)' % ( 280 client_address, 281 self.do_ssl, 282 self.HostInterface.max_connections, 283 ) 284 ) 285 return False 286 287 ### let's go! 288 self.HostInterface.connections += 1 289 self.HostInterface.updateProgress( 290 '[from: %s | SSL: %s] connection established (%s of %s max connections)' % ( 291 client_address, 292 self.do_ssl, 293 self.HostInterface.connections, 294 self.HostInterface.max_connections, 295 ) 296 ) 297 return True
298
299 - def ip_blacklist_check(self, client_addr):
300 if client_addr in self.HostInterface.ip_blacklist: 301 return False 302 return True
303
304 - def ip_max_connections_check(self, ip_address):
305 max_conn_per_ip = self.HostInterface.max_connections_per_host 306 max_conn_per_ip_barrier = self.HostInterface.max_connections_per_host_barrier 307 per_host_connections = self.HostInterface.per_host_connections 308 conn_data = per_host_connections.get(ip_address) 309 if conn_data == None: 310 per_host_connections[ip_address] = 1 311 else: 312 conn_data += 1 313 per_host_connections[ip_address] += 1 314 if conn_data > max_conn_per_ip: 315 self.HostInterface.updateProgress( 316 '[from: %s] ------- :EEK: !! connection closed too many simultaneous connections from host (current: %s | limit: %s) -------' % ( 317 ip_address, 318 conn_data, 319 max_conn_per_ip, 320 ) 321 ) 322 return False 323 elif conn_data > max_conn_per_ip_barrier: 324 times = [5,6,7,8] 325 self.HostInterface.updateProgress( 326 '[from: %s] ------- :EEEK: !! connection warning simultaneous connection barrier reached from host (current: %s | soft limit: %s) -------' % ( 327 ip_address, 328 conn_data, 329 max_conn_per_ip_barrier, 330 ) 331 ) 332 rnd_num = self.entropyTools.get_random_number() 333 time.sleep(times[abs(hash(rnd_num))%len(times)]) 334 335 return True
336
337 - def max_connections_check(self, request):
338 current = self.HostInterface.connections 339 maximum = self.HostInterface.max_connections 340 if current >= maximum: 341 try: 342 self.HostInterface.transmit( 343 request, 344 self.HostInterface.answers['mcr'] 345 ) 346 except: 347 pass 348 return False 349 else: 350 return True
351
352 - def serve_forever(self):
353 while self.alive: 354 #r,w,e = self.select.select([self.socket], [], [], 1) 355 #if r: 356 self.handle_request()
357 358 # taken from SocketServer.py
359 - def finish_request(self, request, client_address):
360 """Finish one request by instantiating RequestHandlerClass.""" 361 self.RequestHandlerClass(request, client_address, self) 362 363 self.HostInterface.updateProgress( 364 '[from: %s] connection closed (%s of %s max connections)' % ( 365 client_address, 366 self.HostInterface.connections - 1, 367 self.HostInterface.max_connections, 368 ) 369 ) 370 per_host_connections = self.HostInterface.per_host_connections 371 conn_data = per_host_connections.get(client_address[0]) 372 if conn_data != None: 373 if conn_data < 1: 374 del per_host_connections[client_address[0]] 375 else: 376 per_host_connections[client_address[0]] -= 1
377
378 - def close_request(self, request):
379 if self.HostInterface.connections > 0: 380 self.HostInterface.connections -= 1
381
382 - class RequestHandler(SocketServer.BaseRequestHandler):
383 384 import SocketServer 385 import select 386 import socket 387 import entropy.tools as entropyTools 388 import gc 389 timed_out = False 390
391 - def __init__(self, request, client_address, server):
392 393 # pre-init attribues 394 self.server = None 395 self.request = None 396 self.client_address = None 397 self.SocketServer.BaseRequestHandler.__init__(self, request, 398 client_address, server)
399
400 - def data_receiver(self):
401 402 if self.timed_out: 403 return True 404 self.timed_out = True 405 try: 406 ready_to_read, ready_to_write, in_error = self.select.select( 407 [self.request], [], [], self.default_timeout) 408 except KeyboardInterrupt: 409 self.timed_out = True 410 return True 411 412 if len(ready_to_read) == 1 and ready_to_read[0] == self.request: 413 414 self.timed_out = False 415 416 try: 417 418 data = self.request.recv(1024) 419 if self.ssl: 420 while self.request.pending(): 421 data += self.request.recv(1024) 422 423 if self.data_counter == None: 424 if data == '': # client wants to close 425 return True 426 elif data == self.server.processor.HostInterface.answers['noop']: 427 return False 428 elif len(data) < len(self.myeos): 429 self.server.processor.HostInterface.updateProgress( 430 'interrupted: %s, reason: %s - from client: %s - data: "%s" - counter: %s' % ( 431 self.server.server_address, 432 "malformed EOS", 433 self.client_address, 434 repr(data), 435 self.data_counter, 436 ) 437 ) 438 self.buffered_data = '' 439 return True 440 mystrlen = data.split(self.myeos)[0] 441 self.data_counter = int(mystrlen) 442 data = data[len(mystrlen)+1:] 443 self.data_counter -= len(data) 444 self.buffered_data += data 445 446 # command length exceeds our command length limit 447 if self.data_counter > self.max_command_length: 448 raise InterruptError('InterruptError: command too long: %s, limit: %s' % (self.data_counter,self.max_command_length,)) 449 450 while self.data_counter > 0: 451 if self.ssl: 452 x = '' 453 while self.request.pending(): 454 x += self.request.recv(1024) 455 else: 456 x = self.request.recv(1024) 457 xlen = len(x) 458 self.data_counter -= xlen 459 self.buffered_data += x 460 if not xlen: 461 break 462 463 self.data_counter = None 464 except ValueError: 465 self.entropyTools.print_traceback() 466 self.server.processor.HostInterface.updateProgress( 467 'interrupted: %s, reason: %s - from client: %s' % ( 468 self.server.server_address, 469 "malformed transmission", 470 self.client_address, 471 ) 472 ) 473 return True 474 except self.socket.timeout, e: 475 self.server.processor.HostInterface.updateProgress( 476 'interrupted: %s, reason: %s - from client: %s' % ( 477 self.server.server_address, 478 e, 479 self.client_address, 480 ) 481 ) 482 return True 483 except self.socket.sslerror, e: 484 self.server.processor.HostInterface.updateProgress( 485 'interrupted: %s, SSL socket error reason: %s - from client: %s' % ( 486 self.server.server_address, 487 e, 488 self.client_address, 489 ) 490 ) 491 return True 492 except (self.ssl_exceptions['WantReadError'], self.ssl_exceptions['WantX509LookupError'],): 493 return False 494 except self.ssl_exceptions['ZeroReturnError']: 495 return True 496 except self.ssl_exceptions['Error'], e: 497 self.server.processor.HostInterface.updateProgress( 498 'interrupted: SSL Error, reason: %s - from client: %s' % ( 499 e, 500 self.client_address, 501 ) 502 ) 503 return True 504 except InterruptError, e: 505 self.server.processor.HostInterface.updateProgress( 506 'interrupted: Command Error, reason: %s - from client: %s' % ( 507 e, 508 self.client_address, 509 ) 510 ) 511 return True 512 513 if not self.buffered_data: 514 return True 515 516 cmd = self.server.processor.process(self.buffered_data, self.request, self.client_address) 517 if cmd == 'close': 518 # send KAPUTT signal JA! 519 self.server.processor.transmit(self.server.processor.HostInterface.answers['cl']) 520 return True 521 self.buffered_data = '' 522 return False
523
524 - def fork_lock_acquire(self):
525 if hasattr(self.server.processor.HostInterface,'ForkLock'): 526 x = getattr(self.server.processor.HostInterface,'ForkLock') 527 if hasattr(x,'acquire') and hasattr(x,'release') and hasattr(x,'locked'): 528 x.acquire()
529
530 - def fork_lock_release(self):
531 if hasattr(self.server.processor.HostInterface,'ForkLock'): 532 x = getattr(self.server.processor.HostInterface,'ForkLock') 533 if hasattr(x,'acquire') and hasattr(x,'release') and hasattr(x,'locked'): 534 if x.locked(): x.release()
535
536 - def handle(self):
537 # not using spawnFunction because it causes some mess 538 # forking this way avoids having memory leaks 539 if self.server.processor.HostInterface.fork_requests: 540 self.fork_lock_acquire() 541 try: 542 my_timeout = self.server.processor.HostInterface.fork_request_timeout_seconds 543 pid = os.fork() 544 seconds = 0 545 if pid > 0: # parent here 546 # pid killer after timeout 547 passed_away = False 548 while 1: 549 time.sleep(1) 550 seconds += 1 551 try: 552 dead = os.waitpid(pid, os.WNOHANG)[0] 553 except OSError, e: 554 if e.errno != 10: raise 555 dead = True 556 if passed_away: 557 break 558 if dead: break 559 if seconds > my_timeout: 560 self.server.processor.HostInterface.updateProgress( 561 'interrupted: forked request timeout: %s,%s from client: %s' % ( 562 seconds, 563 dead, 564 self.client_address, 565 ) 566 ) 567 if not dead: 568 import signal 569 os.kill(pid,signal.SIGKILL) 570 passed_away = True # in this way, the process table should be clean 571 continue 572 break 573 else: 574 self.do_handle() 575 os._exit(0) 576 finally: 577 self.fork_lock_release() 578 else: 579 self.do_handle()
580 #self.entropyTools.spawn_function(self.do_handle) 581
582 - def do_handle(self):
583 584 self.default_timeout = self.server.processor.HostInterface.timeout 585 self.ssl = self.server.processor.HostInterface.SSL 586 self.ssl_exceptions = self.server.processor.HostInterface.SSL_exceptions 587 self.myeos = self.server.processor.HostInterface.answers['eos'] 588 self.max_command_length = self.server.processor.HostInterface.max_command_length 589 590 while 1: 591 592 try: 593 dobreak = self.data_receiver() 594 if dobreak: break 595 except Exception, e: 596 self.server.processor.HostInterface.updateProgress( 597 'interrupted: Unhandled exception: %s, error: %s - from client: %s' % ( 598 Exception, 599 e, 600 self.client_address, 601 ) 602 ) 603 # print exception 604 tb = self.entropyTools.get_traceback() 605 print tb 606 self.server.processor.HostInterface.socketLog.write(tb) 607 break 608 609 self.request.close()
610
611 - def setup(self):
612 self.data_counter = None 613 self.buffered_data = ''
614 615
616 - class CommandProcessor:
617 618 import entropy.tools as entropyTools 619 import socket 620 import gc 621
622 - def __init__(self, HostInterface):
623 self.HostInterface = HostInterface 624 self.channel = None
625
626 - def handle_termination_commands(self, data):
627 if data.strip() in self.HostInterface.termination_commands: 628 self.HostInterface.updateProgress('close: %s' % (self.client_address,)) 629 self.transmit(self.HostInterface.answers['cl']) 630 return "close" 631 632 if not data.strip(): 633 return "ignore"
634
635 - def handle_command_string(self, string):
636 # validate command 637 args = string.strip().split(" ") 638 session = args[0] 639 if (session in self.HostInterface.initialization_commands) or \ 640 (session in self.HostInterface.no_session_commands) or \ 641 len(args) < 2: 642 cmd = args[0] 643 session = None 644 else: 645 cmd = args[1] 646 args = args[1:] # remove session 647 648 stream_enabled = False 649 if (session != None) and self.HostInterface.sessions.has_key(session): 650 stream_enabled = self.HostInterface.sessions[session].get('stream_mode') 651 652 if stream_enabled and (cmd not in self.HostInterface.config_commands): 653 session_len = 0 654 if session: session_len = len(session)+1 655 return cmd,[string[session_len+len(cmd)+1:]],session 656 else: 657 myargs = [] 658 if len(args) > 1: 659 myargs = args[1:] 660 661 return cmd,myargs,session
662
663 - def handle_end_answer(self, cmd, whoops, valid_cmd):
664 if not valid_cmd: 665 self.transmit(self.HostInterface.answers['no']) 666 elif whoops: 667 self.transmit(self.HostInterface.answers['er']) 668 elif cmd not in self.HostInterface.no_acked_commands: 669 self.transmit(self.HostInterface.answers['ok'])
670
671 - def validate_command(self, cmd, args, session):
672 673 # answer to invalid commands 674 if (cmd not in self.HostInterface.valid_commands): 675 return False,"not a valid command" 676 677 if session == None: 678 if cmd not in self.HostInterface.no_session_commands: 679 return False,"need a valid session" 680 elif session not in self.HostInterface.sessions: 681 return False,"session is not alive" 682 683 # check if command needs authentication 684 if session != None: 685 auth = self.HostInterface.valid_commands[cmd]['auth'] 686 if auth: 687 # are we? 688 authed = self.HostInterface.sessions[session]['auth_uid'] 689 if authed == None: 690 # nope 691 return False,"not authenticated" 692 693 # keep session alive 694 if session != None: 695 self.HostInterface.set_session_running(session) 696 self.HostInterface.update_session_time(session) 697 698 return True,"all good"
699
700 - def load_authenticator(self):
701 f, args, kwargs = self.HostInterface.AuthenticatorInst 702 myinst = f(*args,**kwargs) 703 return myinst
704
705 - def load_service_interface(self, session):
706 707 uid = None 708 if session != None: 709 uid = self.HostInterface.sessions[session]['auth_uid'] 710 711 intf = self.HostInterface.EntropyInstantiation[0] 712 args = self.HostInterface.EntropyInstantiation[1] 713 kwds = self.HostInterface.EntropyInstantiation[2] 714 return intf(*args, **kwds)
715
716 - def process(self, data, channel, client_address):
717 718 self.channel = channel 719 self.client_address = client_address 720 721 term = self.handle_termination_commands(data) 722 if term: 723 del authenticator 724 return term 725 726 cmd, args, session = self.handle_command_string(data) 727 valid_cmd, reason = self.validate_command(cmd, args, session) 728 729 # decide if we need to load authenticator or Entropy 730 authenticator = None 731 cmd_data = self.HostInterface.valid_commands.get(cmd) 732 if not isinstance(cmd_data,dict): 733 self.HostInterface.updateProgress( 734 '[from: %s] command error: invalid command: %s' % ( 735 self.client_address, 736 cmd, 737 ) 738 ) 739 return "close" 740 elif (("authenticator" in cmd_data['args']) or (cmd in self.HostInterface.login_pass_commands)): 741 try: 742 authenticator = self.load_authenticator() 743 except ConnectionError, e: 744 self.HostInterface.updateProgress( 745 '[from: %s] authenticator error: cannot load: %s' % ( 746 self.client_address, 747 e, 748 ) 749 ) 750 tb = self.entropyTools.get_traceback() 751 print tb 752 self.HostInterface.socketLog.write(tb) 753 return "close" 754 except Exception, e: 755 self.HostInterface.updateProgress( 756 '[from: %s] authenticator error: cannot load: %s - unknown error' % ( 757 self.client_address, 758 e, 759 ) 760 ) 761 tb = self.entropyTools.get_traceback() 762 print tb 763 self.HostInterface.socketLog.write(tb) 764 return "close" 765 766 p_args = args 767 if (cmd in self.HostInterface.login_pass_commands) and authenticator != None: 768 p_args = authenticator.hide_login_data(p_args) 769 elif cmd in self.HostInterface.raw_commands: 770 p_args = ['raw data'] 771 self.HostInterface.updateProgress( 772 '[from: %s] command validation :: called %s: length: %s, args: %s, session: %s, valid: %s, reason: %s' % ( 773 self.client_address, 774 cmd, 775 len(data), 776 p_args, 777 session, 778 valid_cmd, 779 reason, 780 ) 781 ) 782 783 whoops = False 784 if valid_cmd: 785 786 if authenticator != None: 787 # now set session 788 authenticator.set_session(session) 789 790 Entropy = None 791 if "Entropy" in cmd_data['args']: 792 Entropy = self.load_service_interface(session) 793 try: 794 self.run_task(cmd, args, session, Entropy, authenticator) 795 except self.socket.timeout: 796 self.HostInterface.updateProgress( 797 '[from: %s] command error: timeout, closing connection' % ( 798 self.client_address, 799 ) 800 ) 801 # close connection 802 del authenticator 803 del Entropy 804 return "close" 805 except self.socket.error, e: 806 self.HostInterface.updateProgress( 807 '[from: %s] command error: socket error: %s' % ( 808 self.client_address, 809 e, 810 ) 811 ) 812 # close connection 813 del authenticator 814 del Entropy 815 return "close" 816 except self.HostInterface.SSL_exceptions['SysCallError'], e: 817 self.HostInterface.updateProgress( 818 '[from: %s] command error: SSL SysCallError: %s' % ( 819 self.client_address, 820 e, 821 ) 822 ) 823 # close connection 824 del authenticator 825 del Entropy 826 return "close" 827 except Exception, e: 828 # write to self.HostInterface.socketLog 829 tb = self.entropyTools.get_traceback() 830 print tb 831 self.HostInterface.socketLog.write(tb) 832 # store error 833 self.HostInterface.updateProgress( 834 '[from: %s] command error: %s, type: %s' % ( 835 self.client_address, 836 e, 837 type(e), 838 ) 839 ) 840 if session != None: 841 self.HostInterface.store_rc(str(e),session) 842 whoops = True 843 844 del Entropy 845 846 if session != None: 847 self.HostInterface.update_session_time(session) 848 self.HostInterface.unset_session_running(session) 849 rcmd = None 850 try: 851 self.handle_end_answer(cmd, whoops, valid_cmd) 852 except (self.socket.error, self.socket.timeout,self.HostInterface.SSL_exceptions['SysCallError'],): 853 rcmd = "close" 854 855 if authenticator != None: 856 authenticator.terminate_instance() 857 del authenticator 858 if not self.HostInterface.fork_requests: 859 self.gc.collect() 860 return rcmd
861
862 - def transmit(self, data):
863 self.HostInterface.transmit(self.channel, data)
864
865 - def run_task(self, cmd, args, session, Entropy, authenticator):
866 867 p_args = args 868 if cmd in self.HostInterface.login_pass_commands: 869 p_args = authenticator.hide_login_data(p_args) 870 elif cmd in self.HostInterface.raw_commands: 871 p_args = ['raw data'] 872 self.HostInterface.updateProgress( 873 '[from: %s] run_task :: called %s: args: %s, session: %s' % ( 874 self.client_address, 875 cmd, 876 p_args, 877 session, 878 ) 879 ) 880 881 myargs = args 882 mykwargs = {} 883 if cmd not in self.HostInterface.raw_commands: 884 myargs, mykwargs = self._get_args_kwargs(args) 885 886 rc = self.spawn_function(cmd, myargs, mykwargs, session, Entropy, authenticator) 887 if session != None and self.HostInterface.sessions.has_key(session): 888 self.HostInterface.store_rc(rc, session) 889 return rc
890
891 - def _get_args_kwargs(self, args):
892 myargs = [] 893 mykwargs = {} 894 895 def is_int(x): 896 try: 897 int(x) 898 except ValueError: 899 return False 900 return True
901 902 for arg in args: 903 if (arg.find("=") != -1) and not arg.startswith("="): 904 x = arg.split("=") 905 a = x[0] 906 b = ''.join(x[1:]) 907 if (b in ("True","False",)) or is_int(b): 908 mykwargs[a] = eval(b) 909 else: 910 myargs.append(arg) 911 else: 912 if (arg in ("True","False",)) or is_int(arg): 913 myargs.append(eval(arg)) 914 else: 915 myargs.append(arg) 916 return myargs, mykwargs
917
918 - def spawn_function(self, cmd, myargs, mykwargs, session, Entropy, authenticator):
919 920 p_args = myargs 921 if cmd in self.HostInterface.login_pass_commands: 922 p_args = authenticator.hide_login_data(p_args) 923 elif cmd in self.HostInterface.raw_commands: 924 p_args = ['raw data'] 925 self.HostInterface.updateProgress( 926 '[from: %s] called %s: args: %s, kwargs: %s' % ( 927 self.client_address, 928 cmd, 929 p_args, 930 mykwargs, 931 ) 932 ) 933 return self.do_spawn(cmd, myargs, mykwargs, session, Entropy, authenticator)
934
935 - def do_spawn(self, cmd, myargs, mykwargs, session, Entropy, authenticator):
936 937 cmd_data = self.HostInterface.valid_commands.get(cmd) 938 do_fork = cmd_data['as_user'] 939 f = cmd_data['cb'] 940 func_args = [] 941 for arg in cmd_data['args']: 942 try: 943 func_args.append(eval(arg)) 944 except (NameError, SyntaxError): 945 func_args.append(str(arg)) 946 947 if do_fork: 948 myfargs = func_args[:] 949 myfargs.extend(myargs) 950 return self.fork_task(f, session, authenticator, *myfargs, **mykwargs) 951 else: 952 return f(*func_args)
953
954 - def fork_task(self, f, session, authenticator, *args, **kwargs):
955 gid = None 956 uid = None 957 if session != None: 958 logged_in = self.HostInterface.sessions[session]['auth_uid'] 959 if logged_in != None: 960 uid = logged_in 961 gid = etpConst['entropygid'] 962 return self.entropyTools.spawn_function(self._do_fork, f, authenticator, uid, gid, *args, **kwargs)
963
964 - def _do_fork(self, f, authenticator, uid, gid, *args, **kwargs):
965 authenticator.set_exc_permissions(uid,gid) 966 rc = f(*args,**kwargs) 967 return rc
968
969 - class BuiltInCommands(SocketCommands):
970 971 import entropy.dump as dumpTools 972 import zlib 973
974 - def __init__(self, HostInterface):
975 976 SocketCommands.__init__(self, HostInterface, inst_name = "builtin") 977 978 self.valid_commands = { 979 'begin': { 980 'auth': False, # does it need authentication ? 981 'built_in': True, # is it built-in ? 982 'cb': self.docmd_begin, # function to call 983 'args': ["self.transmit", "self.client_address"], # arguments to be passed before *args and **kwards, in SocketHostInterface.do_spawn() 984 'as_user': False, # do I have to fork the process and run it as logged user? 985 # needs auth = True 986 'desc': "instantiate a session", # description 987 'syntax': "begin", # syntax 988 'from': unicode(self), # from what class 989 }, 990 'end': { 991 'auth': False, 992 'built_in': True, 993 'cb': self.docmd_end, 994 'args': ["self.transmit", "session"], 995 'as_user': False, 996 'desc': "end a session", 997 'syntax': "<SESSION_ID> end", 998 'from': unicode(self), 999 }, 1000 'session_config': { 1001 'auth': False, 1002 'built_in': True, 1003 'cb': self.docmd_session_config, 1004 'args': ["session","myargs"], 1005 'as_user': False, 1006 'desc': "set session configuration options", 1007 'syntax': "<SESSION_ID> session_config <option> [parameters]", 1008 'from': unicode(self), 1009 }, 1010 'rc': { 1011 'auth': False, 1012 'built_in': True, 1013 'cb': self.docmd_rc, 1014 'args': ["self.transmit","session"], 1015 'as_user': False, 1016 'desc': "get data returned by the last valid command (streamed python object)", 1017 'syntax': "<SESSION_ID> rc", 1018 'from': unicode(self), 1019 }, 1020 'hello': { 1021 'auth': False, 1022 'built_in': True, 1023 'cb': self.docmd_hello, 1024 'args': ["self.transmit"], 1025 'as_user': False, 1026 'desc': "get server status", 1027 'syntax': "hello", 1028 'from': unicode(self), 1029 }, 1030 'alive': { 1031 'auth': True, 1032 'built_in': True, 1033 'cb': self.docmd_alive, 1034 'args': ["self.transmit","self.client_address","myargs"], 1035 'as_user': False, 1036 'desc': "check if a session is still alive", 1037 'syntax': "alive <SESSION_ID>", 1038 'from': unicode(self), 1039 }, 1040 'login': { 1041 'auth': False, 1042 'built_in': True, 1043 'cb': self.docmd_login, 1044 'args': ["self.transmit", "authenticator", "session", "self.client_address", "myargs"], 1045 'as_user': False, 1046 'desc': "login on the running server (allows running extra commands)", 1047 'syntax': "<SESSION_ID> login <authenticator parameters, default: <user> <auth_type> <password> >", 1048 'from': unicode(self), 1049 }, 1050 'user_data': { 1051 'auth': True, 1052 'built_in': True, 1053 'cb': self.docmd_userdata, 1054 'args': ["self.transmit", "authenticator", "session"], 1055 'as_user': False, 1056 'desc': "get general user information, user must be logged in", 1057 'syntax': "<SESSION_ID> user_data", 1058 'from': unicode(self), 1059 }, 1060 'logout': { 1061 'auth': True, 1062 'built_in': True, 1063 'cb': self.docmd_logout, 1064 'args': ["self.transmit", "authenticator", "session", "self.client_address", "myargs"], 1065 'as_user': False, 1066 'desc': "logout on the running server", 1067 'syntax': "<SESSION_ID> logout <USER>", 1068 'from': unicode(self), 1069 }, 1070 'help': { 1071 'auth': False, 1072 'built_in': True, 1073 'cb': self.docmd_help, 1074 'args': ["self.transmit"], 1075 'as_user': False, 1076 'desc': "this output", 1077 'syntax': "help", 1078 'from': unicode(self), 1079 }, 1080 'available_commands': { 1081 'auth': False, 1082 'built_in': True, 1083 'cb': self.docmd_available_commands, 1084 'args': ["self.HostInterface"], 1085 'as_user': False, 1086 'desc': "get info about available commands (you must retrieve this using the 'rc' command)", 1087 'syntax': "available_commands", 1088 'from': unicode(self), 1089 }, 1090 'stream': { 1091 'auth': True, 1092 'built_in': True, 1093 'cb': self.docmd_stream, 1094 'args': ["session", "myargs"], 1095 'as_user': False, 1096 'desc': "send a chunk of data to be saved on the session temp file path (will be removed on session expiration)", 1097 'syntax': "<SESSION_ID> stream <chunk of byte-string to write to file>", 1098 'from': unicode(self), 1099 }, 1100 } 1101 1102 self.no_acked_commands = ["rc", "begin", "end", "hello", "alive", "login", "logout","help"] 1103 self.termination_commands = ["quit","close"] 1104 self.initialization_commands = ["begin"] 1105 self.login_pass_commands = ["login"] 1106 self.no_session_commands = ["begin","hello","alive","help"] 1107 self.raw_commands = ["stream"] 1108 self.config_commands = ["session_config"]
1109
1110 - def docmd_session_config(self, session, myargs):
1111 1112 if not myargs: 1113 return False,"not enough parameters" 1114 1115 option = myargs[0] 1116 myopts = myargs[1:] 1117 1118 if option == "compression": 1119 docomp = True 1120 do_zlib = False 1121 if "zlib" in myopts: 1122 do_zlib = True 1123 if myopts: 1124 if isinstance(myopts[0],bool): 1125 docomp = myopts[0] 1126 else: 1127 try: 1128 docomp = eval(myopts[0]) 1129 except (NameError, TypeError,): 1130 pass 1131 if docomp and do_zlib: 1132 docomp = "zlib" 1133 elif docomp and not do_zlib: 1134 docomp = "gzip" 1135 else: 1136 docomp = None 1137 self.HostInterface.sessions[session]['compression'] = docomp 1138 return True,"compression now: %s" % (docomp,) 1139 elif option == "stream": 1140 dostream = True 1141 if "off" in myopts: 1142 dostream = False 1143 self.HostInterface.sessions[session]['stream_mode'] = dostream 1144 return True,'stream mode: %s' % (dostream,) 1145 else: 1146 return False,"invalid config option"
1147
1148 - def docmd_available_commands(self, host_interface):
1149 1150 def copy_obj(obj): 1151 if isinstance(obj,set) or isinstance(obj,dict): 1152 return obj.copy() 1153 elif isinstance(obj,list) or isinstance(obj,tuple): 1154 return obj[:] 1155 return obj
1156 1157 def can_be_streamed(obj): 1158 if isinstance(obj,(bool,basestring,int,float,list,tuple,set,dict,)): 1159 return True 1160 return False
1161 1162 mydata = {} 1163 mydata['disabled_commands'] = copy_obj(host_interface.disabled_commands) 1164 valid_cmds = copy_obj(host_interface.valid_commands) 1165 mydata['valid_commands'] = {} 1166 for cmd in valid_cmds: 1167 mydict = {} 1168 for item in valid_cmds[cmd]: 1169 param = valid_cmds[cmd][item] 1170 if not can_be_streamed(param): 1171 continue 1172 mydict[item] = param 1173 mydata['valid_commands'][cmd] = mydict.copy() 1174 1175 return mydata 1176
1177 - def docmd_stream(self, session, myargs):
1178 1179 if not self.HostInterface.sessions[session]['stream_mode']: 1180 return False,'not in stream mode' 1181 if not myargs: 1182 return False,'no stream sent' 1183 1184 compression = self.HostInterface.sessions[session]['compression'] 1185 1186 stream = myargs[0] 1187 stream_path = self.HostInterface.sessions[session]['stream_path'] 1188 stream_dir = os.path.dirname(stream_path) 1189 if not os.path.isdir(os.path.dirname(stream_path)): 1190 try: 1191 os.makedirs(stream_dir) 1192 if etpConst['entropygid'] != None: 1193 const_setup_perms(stream_dir,etpConst['entropygid']) 1194 except OSError: 1195 return False,'cannot initialize stream directory' 1196 1197 f = open(stream_path,'abw') 1198 if compression: 1199 stream = self.zlib.decompress(stream) 1200 f.write(stream) 1201 f.flush() 1202 f.close() 1203 1204 return True,'ok'
1205
1206 - def docmd_login(self, transmitter, authenticator, session, client_address, myargs):
1207 1208 # is already auth'd? 1209 auth_uid = self.HostInterface.sessions[session]['auth_uid'] 1210 if auth_uid != None: 1211 return False,"already authenticated" 1212 1213 status, user, uid, reason = authenticator.docmd_login(myargs) 1214 if status: 1215 self.HostInterface.updateProgress( 1216 '[from: %s] user %s logged in successfully, session: %s' % ( 1217 client_address, 1218 user, 1219 session, 1220 ) 1221 ) 1222 self.HostInterface.sessions[session]['auth_uid'] = uid 1223 transmitter(self.HostInterface.answers['ok']) 1224 return True,reason 1225 elif user == None: 1226 self.HostInterface.updateProgress( 1227 '[from: %s] user -not specified- login failed, session: %s, reason: %s' % ( 1228 client_address, 1229 session, 1230 reason, 1231 ) 1232 ) 1233 transmitter(self.HostInterface.answers['no']) 1234 return False,reason 1235 else: 1236 self.HostInterface.updateProgress( 1237 '[from: %s] user %s login failed, session: %s, reason: %s' % ( 1238 client_address, 1239 user, 1240 session, 1241 reason, 1242 ) 1243 ) 1244 transmitter(self.HostInterface.answers['no']) 1245 return False,reason
1246
1247 - def docmd_userdata(self, transmitter, authenticator, session):
1248 1249 auth_uid = self.HostInterface.sessions[session]['auth_uid'] 1250 if auth_uid == None: 1251 return False,None,"not authenticated" 1252 1253 return authenticator.docmd_userdata()
1254
1255 - def docmd_logout(self, transmitter, authenticator, session, client_address, myargs):
1256 status, user, reason = authenticator.docmd_logout(myargs) 1257 if status: 1258 self.HostInterface.updateProgress( 1259 '[from: %s] user %s logged out successfully, session: %s, args: %s ' % ( 1260 client_address, 1261 user, 1262 session, 1263 myargs, 1264 ) 1265 ) 1266 self.HostInterface.sessions[session]['auth_uid'] = None 1267 transmitter(self.HostInterface.answers['ok']) 1268 return True,reason 1269 elif user == None: 1270 self.HostInterface.updateProgress( 1271 '[from: %s] user -not specified- logout failed, session: %s, args: %s, reason: %s' % ( 1272 client_address, 1273 session, 1274 myargs, 1275 reason, 1276 ) 1277 ) 1278 transmitter(self.HostInterface.answers['no']) 1279 return False,reason 1280 else: 1281 self.HostInterface.updateProgress( 1282 '[from: %s] user %s logout failed, session: %s, args: %s, reason: %s' % ( 1283 client_address, 1284 user, 1285 session, 1286 myargs, 1287 reason, 1288 ) 1289 ) 1290 transmitter(self.HostInterface.answers['no']) 1291 return False,reason
1292
1293 - def docmd_alive(self, transmitter, client_address, myargs):
1294 cmd = self.HostInterface.answers['no'] 1295 alive = False 1296 if myargs: 1297 session_data = self.HostInterface.sessions.get(myargs[0]) 1298 if session_data != None: 1299 if client_address[0] == session_data.get('ip_address'): 1300 cmd = self.HostInterface.answers['ok'] 1301 alive = True 1302 transmitter(cmd) 1303 return alive
1304
1305 - def docmd_hello(self, transmitter):
1306 from entropy.tools import getstatusoutput 1307 from entropy.core import SystemSettings 1308 sys_settings = SystemSettings() 1309 uname = os.uname() 1310 kern_string = uname[2] 1311 running_host = uname[1] 1312 running_arch = uname[4] 1313 load_stats = getstatusoutput('uptime')[1].split("\n")[0] 1314 text = "Entropy Server %s, connections: %s ~ running on: %s ~ host: %s ~ arch: %s, kernel: %s, stats: %s\n" % ( 1315 etpConst['entropyversion'], 1316 self.HostInterface.connections, 1317 sys_settings['system']['name'], 1318 running_host, 1319 running_arch, 1320 kern_string, 1321 load_stats 1322 ) 1323 transmitter(text)
1324
1325 - def docmd_help(self, transmitter):
1326 text = '\nEntropy Socket Interface Help Menu\n' + \ 1327 'Available Commands:\n\n' 1328 valid_cmds = sorted(self.HostInterface.valid_commands.keys()) 1329 for cmd in valid_cmds: 1330 if self.HostInterface.valid_commands[cmd].has_key('desc'): 1331 desc = self.HostInterface.valid_commands[cmd]['desc'] 1332 else: 1333 desc = 'no description available' 1334 1335 if self.HostInterface.valid_commands[cmd].has_key('syntax'): 1336 syntax = self.HostInterface.valid_commands[cmd]['syntax'] 1337 else: 1338 syntax = 'no syntax available' 1339 if self.HostInterface.valid_commands[cmd].has_key('from'): 1340 myfrom = self.HostInterface.valid_commands[cmd]['from'] 1341 else: 1342 myfrom = 'N/A' 1343 text += "[%s] %s\n %s: %s\n %s: %s\n" % ( 1344 myfrom, 1345 blue(cmd), 1346 red("description"), 1347 desc.strip(), 1348 darkgreen("syntax"), 1349 syntax, 1350 ) 1351 transmitter(text)
1352
1353 - def docmd_end(self, transmitter, session):
1354 rc = self.HostInterface.destroy_session(session) 1355 cmd = self.HostInterface.answers['no'] 1356 if rc: cmd = self.HostInterface.answers['ok'] 1357 transmitter(cmd) 1358 return rc
1359
1360 - def docmd_begin(self, transmitter, client_address):
1361 session = self.HostInterface.get_new_session(client_address[0]) 1362 transmitter(session) 1363 return session
1364
1365 - def docmd_rc(self, transmitter, session):
1366 rc = self.HostInterface.get_rc(session) 1367 comp = self.HostInterface.sessions[session]['compression'] 1368 myserialized = self.dumpTools.serialize_string(rc) 1369 if comp == "zlib": # new shiny zlib 1370 myserialized = self.zlib.compress(myserialized, 7) # compression level 1-9 1371 elif comp == "gzip": # old and burried 1372 import gzip 1373 try: 1374 import cStringIO as stringio 1375 except ImportError: 1376 import StringIO as stringio 1377 f = stringio.StringIO() 1378 self.dumpTools.serialize(rc, f) 1379 myf = stringio.StringIO() 1380 mygz = gzip.GzipFile( 1381 mode = 'wb', 1382 fileobj = myf 1383 ) 1384 f.seek(0) 1385 chunk = f.read(8192) 1386 while chunk: 1387 mygz.write(chunk) 1388 chunk = f.read(8192) 1389 mygz.flush() 1390 mygz.close() 1391 myserialized = myf.getvalue() 1392 f.close() 1393 myf.close() 1394 1395 1396 transmitter(myserialized) 1397 1398 return rc
1399
1400 - def __init__(self, service_interface, *args, **kwds):
1401 1402 import gc 1403 self.gc = gc 1404 import threading 1405 self.threading = threading 1406 import entropy.tools as entropyTools 1407 from entropy.misc import TimeScheduled 1408 self.TimeScheduled = TimeScheduled 1409 self.entropyTools = entropyTools 1410 self.Server = None 1411 self.Gc = None 1412 self.PythonGarbageCollector = None 1413 self.AuthenticatorInst = None 1414 1415 self.args = args 1416 self.kwds = kwds 1417 from entropy.misc import LogFile 1418 self.socketLog = LogFile( 1419 level = etpConst['socketloglevel'], 1420 filename = etpConst['socketlogfile'], 1421 header = "[Socket]" 1422 ) 1423 1424 # settings 1425 from entropy.core import SystemSettings 1426 import copy 1427 """ 1428 SystemSettings is a singleton, and we just need to read 1429 socket configuration. we don't want to mess other instances 1430 so we pay attention to not use it more than what is needed. 1431 """ 1432 sys_settings = SystemSettings() 1433 self.__socket_settings = copy.deepcopy(sys_settings['socket_service']) 1434 1435 self.SessionsLock = self.threading.Lock() 1436 self.fork_requests = True # used by the command processor 1437 self.fork_request_timeout_seconds = self.__socket_settings['forked_requests_timeout'] 1438 self.stdout_logging = True 1439 self.timeout = self.__socket_settings['timeout'] 1440 self.hostname = self.__socket_settings['hostname'] 1441 self.session_ttl = self.__socket_settings['session_ttl'] 1442 if self.hostname == "*": self.hostname = '' 1443 self.port = self.__socket_settings['port'] 1444 self.threads = self.__socket_settings['threads'] # maximum number of allowed sessions 1445 self.max_connections = self.__socket_settings['max_connections'] 1446 self.max_connections_per_host = self.__socket_settings['max_connections_per_host'] 1447 self.max_connections_per_host_barrier = self.__socket_settings['max_connections_per_host_barrier'] 1448 self.max_command_length = self.__socket_settings['max_command_length'] 1449 self.disabled_commands = self.__socket_settings['disabled_cmds'] 1450 self.ip_blacklist = self.__socket_settings['ip_blacklist'] 1451 self.answers = self.__socket_settings['answers'] 1452 self.connections = 0 1453 self.per_host_connections = {} 1454 self.sessions = {} 1455 self.__output = None 1456 self.SSL = {} 1457 self.SSL_exceptions = {} 1458 self.SSL_exceptions['WantReadError'] = None 1459 self.SSL_exceptions['WantWriteError'] = None 1460 self.SSL_exceptions['WantX509LookupError'] = None 1461 self.SSL_exceptions['ZeroReturnError'] = None 1462 self.SSL_exceptions['SysCallError'] = None 1463 self.SSL_exceptions['Error'] = [] 1464 self.last_print = '' 1465 self.valid_commands = {} 1466 self.no_acked_commands = [] 1467 self.raw_commands = [] 1468 self.config_commands = [] 1469 self.termination_commands = [] 1470 self.initialization_commands = [] 1471 self.login_pass_commands = [] 1472 self.no_session_commands = [] 1473 self.command_classes = [self.BuiltInCommands] 1474 self.command_instances = [] 1475 self.EntropyInstantiation = (service_interface, self.args, self.kwds) 1476 1477 self.setup_external_command_classes() 1478 self.start_local_output_interface() 1479 self.setup_authenticator() 1480 self.setup_hostname() 1481 self.setup_commands() 1482 self.disable_commands() 1483 self.start_session_garbage_collector() 1484 self.setup_ssl() 1485 self.start_python_garbage_collector()
1486
1487 - def killall(self):
1488 if hasattr(self,'socketLog'): 1489 self.socketLog.close() 1490 if self.Server != None: 1491 self.Server.alive = False 1492 if self.Gc != None: 1493 self.Gc.kill() 1494 if self.PythonGarbageCollector != None: 1495 self.PythonGarbageCollector.kill()
1496
1497 - def append_eos(self, data):
1498 return str(len(data)) + \ 1499 self.answers['eos'] + \ 1500 data
1501
1502 - def setup_ssl(self):
1503 1504 do_ssl = False 1505 if self.kwds.has_key('ssl'): 1506 do_ssl = self.kwds.pop('ssl') 1507 1508 if not do_ssl: 1509 return 1510 1511 try: 1512 from OpenSSL import SSL, crypto 1513 except ImportError, e: 1514 self.updateProgress('Unable to load OpenSSL, error: %s' % (repr(e),)) 1515 return 1516 self.SSL_exceptions['WantReadError'] = SSL.WantReadError 1517 self.SSL_exceptions['Error'] = SSL.Error 1518 self.SSL_exceptions['WantWriteError'] = SSL.WantWriteError 1519 self.SSL_exceptions['WantX509LookupError'] = SSL.WantX509LookupError 1520 self.SSL_exceptions['ZeroReturnError'] = SSL.ZeroReturnError 1521 self.SSL_exceptions['SysCallError'] = SSL.SysCallError 1522 self.SSL['m'] = SSL 1523 self.SSL['crypto'] = crypto 1524 self.SSL['key'] = self.__socket_settings['ssl_key'] 1525 self.SSL['cert'] = self.__socket_settings['ssl_cert'] 1526 self.SSL['ca_cert'] = self.__socket_settings['ssl_ca_cert'] 1527 self.SSL['ca_pkey'] = self.__socket_settings['ssl_ca_pkey'] 1528 # change port 1529 self.port = self.__socket_settings['ssl_port'] 1530 self.SSL['not_before'] = 0 1531 self.SSL['not_after'] = 60*60*24*365*5 # 5 years 1532 self.SSL['serial'] = 0 1533 self.SSL['digest'] = 'md5' 1534 1535 if not (os.path.isfile(self.SSL['ca_cert']) and \ 1536 os.path.isfile(self.SSL['ca_pkey']) and \ 1537 os.path.isfile(self.SSL['key']) and \ 1538 os.path.isfile(self.SSL['cert'])): 1539 self.create_ca_server_certs( 1540 self.SSL['serial'], 1541 self.SSL['digest'], 1542 self.SSL['not_before'], 1543 self.SSL['not_after'], 1544 self.SSL['ca_pkey'], 1545 self.SSL['ca_cert'], 1546 self.SSL['key'], 1547 self.SSL['cert'] 1548 ) 1549 os.chmod(self.SSL['ca_cert'],0644) 1550 try: 1551 os.chown(self.SSL['ca_cert'],-1,0) 1552 except OSError: 1553 pass 1554 os.chmod(self.SSL['ca_pkey'],0600) 1555 try: 1556 os.chown(self.SSL['ca_pkey'],-1,0) 1557 except OSError: 1558 pass 1559 1560 os.chmod(self.SSL['key'],0600) 1561 try: 1562 os.chown(self.SSL['key'],-1,0) 1563 except OSError: 1564 pass 1565 os.chmod(self.SSL['cert'],0644) 1566 try: 1567 os.chown(self.SSL['cert'],-1,0) 1568 except OSError: 1569 pass
1570
1571 - def create_ca_server_certs(self, serial, digest, not_before, not_after, ca_pkey_dest, ca_cert_dest, server_key, server_cert):
1572 1573 mycn = 'Entropy Repository Service' 1574 cakey = self.create_ssl_key_pair(self.SSL['crypto'].TYPE_RSA, 1024) 1575 careq = self.create_ssl_certificate_request(cakey, digest, CN = mycn) 1576 cert = self.SSL['crypto'].X509() 1577 cert.set_serial_number(serial) 1578 cert.gmtime_adj_notBefore(not_before) 1579 cert.gmtime_adj_notAfter(not_after) 1580 cert.set_issuer(careq.get_subject()) 1581 cert.set_subject(careq.get_subject()) 1582 cert.sign(cakey, digest) 1583 1584 # now create server key + cert 1585 s_pkey = self.create_ssl_key_pair(self.SSL['crypto'].TYPE_RSA, 1024) 1586 s_req = self.create_ssl_certificate_request(s_pkey, digest, CN = mycn) 1587 s_cert = self.SSL['crypto'].X509() 1588 s_cert.set_serial_number(serial+1) 1589 s_cert.gmtime_adj_notBefore(not_before) 1590 s_cert.gmtime_adj_notAfter(not_after) 1591 s_cert.set_issuer(cert.get_subject()) 1592 s_cert.set_subject(s_req.get_subject()) 1593 s_cert.set_pubkey(s_req.get_pubkey()) 1594 s_cert.sign(cakey, digest) 1595 1596 # write CA 1597 if os.path.isfile(ca_pkey_dest): 1598 shutil.move(ca_pkey_dest,ca_pkey_dest+".moved") 1599 f = open(ca_pkey_dest,"w") 1600 f.write(self.SSL['crypto'].dump_privatekey(self.SSL['crypto'].FILETYPE_PEM, cakey)) 1601 f.flush() 1602 f.close() 1603 if os.path.isfile(ca_cert_dest): 1604 shutil.move(ca_cert_dest,ca_cert_dest+".moved") 1605 f = open(ca_cert_dest,"w") 1606 f.write(self.SSL['crypto'].dump_certificate(self.SSL['crypto'].FILETYPE_PEM, cert)) 1607 f.flush() 1608 f.close() 1609 1610 if os.path.isfile(server_key): 1611 shutil.move(server_key,server_key+".moved") 1612 # write server 1613 f = open(server_key,"w") 1614 f.write(self.SSL['crypto'].dump_privatekey(self.SSL['crypto'].FILETYPE_PEM, s_pkey)) 1615 f.flush() 1616 f.close() 1617 if os.path.isfile(server_cert): 1618 shutil.move(server_cert,server_cert+".moved") 1619 f = open(server_cert,"w") 1620 f.write(self.SSL['crypto'].dump_certificate(self.SSL['crypto'].FILETYPE_PEM, s_cert)) 1621 f.flush() 1622 f.close()
1623
1624 - def create_ssl_key_pair(self, keytype, bits):
1625 pkey = self.SSL['crypto'].PKey() 1626 pkey.generate_key(keytype, bits) 1627 return pkey
1628
1629 - def create_ssl_certificate_request(self, pkey, digest, **name):
1630 req = self.SSL['crypto'].X509Req() 1631 subj = req.get_subject() 1632 for (key,value) in name.items(): 1633 setattr(subj, key, value) 1634 req.set_pubkey(pkey) 1635 req.sign(pkey, digest) 1636 return req
1637
1638 - def setup_external_command_classes(self):
1639 1640 if self.kwds.has_key('external_cmd_classes'): 1641 ext_commands = self.kwds.pop('external_cmd_classes') 1642 if not isinstance(ext_commands,list): 1643 raise InvalidDataType("InvalidDataType: external_cmd_classes must be a list") 1644 self.command_classes += ext_commands
1645
1646 - def setup_commands(self):
1647 1648 identifiers = set() 1649 for myclass in self.command_classes: 1650 1651 myargs = [] 1652 mykwargs = {} 1653 if isinstance(myclass,tuple) or isinstance(myclass,list): 1654 if len(myclass) > 2: 1655 mykwargs = myclass[2] 1656 if len(myclass) > 1: 1657 myargs = myclass[1] 1658 myclass = myclass[0] 1659 1660 myinst = myclass(self, *myargs, **mykwargs) 1661 if str(myinst) in identifiers: 1662 raise PermissionDenied("PermissionDenied: another command instance is owning this name") 1663 identifiers.add(str(myinst)) 1664 self.command_instances.append(myinst) 1665 # now register 1666 myinst.register( self.valid_commands, 1667 self.no_acked_commands, 1668 self.termination_commands, 1669 self.initialization_commands, 1670 self.login_pass_commands, 1671 self.no_session_commands, 1672 self.raw_commands, 1673 self.config_commands 1674 )
1675
1676 - def disable_commands(self):
1677 for cmd in self.disabled_commands: 1678 1679 if cmd in self.valid_commands: 1680 self.valid_commands.pop(cmd) 1681 1682 if cmd in self.no_acked_commands: 1683 self.no_acked_commands.remove(cmd) 1684 1685 if cmd in self.termination_commands: 1686 self.termination_commands.remove(cmd) 1687 1688 if cmd in self.initialization_commands: 1689 self.initialization_commands.remove(cmd) 1690 1691 if cmd in self.login_pass_commands: 1692 self.login_pass_commands.remove(cmd) 1693 1694 if cmd in self.no_session_commands: 1695 self.no_session_commands.remove(cmd) 1696 1697 if cmd in self.raw_commands: 1698 self.raw_commands.remove(cmd) 1699 1700 if cmd in self.config_commands: 1701 self.config_commands.remove(cmd)
1702
1703 - def start_local_output_interface(self):
1704 if self.kwds.has_key('sock_output'): 1705 outputIntf = self.kwds.pop('sock_output') 1706 self.__output = outputIntf
1707
1708 - def setup_authenticator(self):
1709 1710 # lock, if perhaps some implementations need it 1711 self.AuthenticatorLock = self.threading.Lock() 1712 auth_inst = (self.BasicPamAuthenticator, [self], {},) # authentication class, args, keywords 1713 # external authenticator 1714 if self.kwds.has_key('sock_auth'): 1715 authIntf = self.kwds.pop('sock_auth') 1716 if type(authIntf) is tuple: 1717 if len(authIntf) == 3: 1718 auth_inst = authIntf[:] 1719 else: 1720 raise IncorrectParameter("IncorrectParameter: wront authentication interface specified") 1721 else: 1722 raise IncorrectParameter("IncorrectParameter: wront authentication interface specified") 1723 # initialize authenticator 1724 self.AuthenticatorInst = (auth_inst[0],[self]+auth_inst[1],auth_inst[2],)
1725
1726 - def start_python_garbage_collector(self):
1727 self.PythonGarbageCollector = self.TimeScheduled(3600, self.python_garbage_collect) 1728 self.PythonGarbageCollector.set_accuracy(False) 1729 self.PythonGarbageCollector.start()
1730
1731 - def start_session_garbage_collector(self):
1732 self.Gc = self.TimeScheduled(5, self.gc_clean) 1733 self.Gc.start()
1734
1735 - def python_garbage_collect(self):
1736 self.gc.collect() 1737 self.gc.collect() 1738 self.gc.collect()
1739
1740 - def gc_clean(self):
1741 if not self.sessions: 1742 return 1743 1744 with self.SessionsLock: 1745 for session_id in self.sessions.keys(): 1746 sess_time = self.sessions[session_id]['t'] 1747 is_running = self.sessions[session_id]['running'] 1748 auth_uid = self.sessions[session_id]['auth_uid'] # is kept alive? 1749 if (is_running) or (auth_uid == -1): 1750 if auth_uid == -1: 1751 self.updateProgress('not killing session %s, since it is kept alive by auth_uid=-1' % (session_id,) ) 1752 continue 1753 cur_time = time.time() 1754 ttl = self.session_ttl 1755 check_time = sess_time + ttl 1756 if cur_time > check_time: 1757 self.updateProgress('killing session %s, ttl: %ss: no activity' % (session_id,ttl,) ) 1758 self._destroy_session(session_id)
1759
1760 - def setup_hostname(self):
1761 if self.hostname: 1762 try: 1763 self.hostname = self.get_ip_address(self.hostname) 1764 except IOError: # it isn't a device name 1765 pass
1766
1767 - def get_ip_address(self, ifname):
1768 import fcntl 1769 import struct 1770 mysock = self.socket.socket ( self.socket.AF_INET, self.socket.SOCK_STREAM ) 1771 return self.socket.inet_ntoa(fcntl.ioctl(mysock.fileno(), 0x8915, struct.pack('256s', ifname[:15]))[20:24])
1772
1773 - def get_md5_hash(self):
1774 import hashlib 1775 m = hashlib.md5() 1776 m.update(os.urandom(2)) 1777 return m.hexdigest()
1778
1779 - def get_new_session(self, ip_address = None):
1780 with self.SessionsLock: 1781 if len(self.sessions) > self.threads: 1782 # fuck! 1783 return "0" 1784 rng = self.get_md5_hash() 1785 while rng in self.sessions: 1786 rng = self.get_md5_hash() 1787 self.sessions[rng] = {} 1788 self.sessions[rng]['running'] = False 1789 self.sessions[rng]['auth_uid'] = None 1790 self.sessions[rng]['admin'] = False 1791 self.sessions[rng]['moderator'] = False 1792 self.sessions[rng]['user'] = False 1793 self.sessions[rng]['developer'] = False 1794 self.sessions[rng]['compression'] = None 1795 self.sessions[rng]['stream_mode'] = False 1796 try: 1797 self.sessions[rng]['stream_path'] = self.entropyTools.get_random_temp_file() 1798 except (IOError,OSError,): 1799 self.sessions[rng]['stream_path'] = '' 1800 self.sessions[rng]['t'] = time.time() 1801 self.sessions[rng]['ip_address'] = ip_address 1802 return rng
1803
1804 - def update_session_time(self, session):
1805 with self.SessionsLock: 1806 if self.sessions.has_key(session): 1807 self.sessions[session]['t'] = time.time() 1808 self.updateProgress('session time updated for %s' % (session,) )
1809
1810 - def set_session_running(self, session):
1811 with self.SessionsLock: 1812 if self.sessions.has_key(session): 1813 self.sessions[session]['running'] = True
1814
1815 - def unset_session_running(self, session):
1816 with self.SessionsLock: 1817 if self.sessions.has_key(session): 1818 self.sessions[session]['running'] = False
1819
1820 - def destroy_session(self, session):
1821 with self.SessionsLock: 1822 self._destroy_session(session)
1823
1824 - def _destroy_session(self, session):
1825 if self.sessions.has_key(session): 1826 stream_path = self.sessions[session]['stream_path'] 1827 del self.sessions[session] 1828 if os.path.isfile(stream_path) and os.access(stream_path,os.W_OK) and not os.path.islink(stream_path): 1829 try: os.remove(stream_path) 1830 except OSError: pass 1831 return True 1832 return False
1833
1834 - def go(self):
1835 self.socket.setdefaulttimeout(self.timeout) 1836 while 1: 1837 try: 1838 self.Server = self.HostServerMixin( 1839 (self.hostname, self.port), 1840 self.RequestHandler, 1841 self.CommandProcessor(self), 1842 self 1843 ) 1844 break 1845 except self.socket.error, e: 1846 if e[0] == 98: 1847 # Address already in use 1848 self.updateProgress('address already in use (%s, port: %s), waiting 5 seconds...' % (self.hostname,self.port,)) 1849 time.sleep(5) 1850 continue 1851 else: 1852 raise 1853 self.updateProgress('server connected, listening on: %s, port: %s, timeout: %s' % (self.hostname,self.port,self.timeout,)) 1854 self.Server.serve_forever() 1855 self.Gc.kill()
1856
1857 - def store_rc(self, rc, session):
1858 with self.SessionsLock: 1859 if session in self.sessions: 1860 if type(rc) in (list,tuple,): 1861 rc_item = rc[:] 1862 elif type(rc) in (set,frozenset,dict,): 1863 rc_item = rc.copy() 1864 else: 1865 rc_item = rc 1866 self.sessions[session]['rc'] = rc_item
1867
1868 - def get_rc(self, session):
1869 with self.SessionsLock: 1870 if session in self.sessions: 1871 return self.sessions[session].get('rc')
1872
1873 - def transmit(self, channel, data):
1874 if self.SSL: 1875 mydata = self.append_eos(data) 1876 encode_done = False 1877 while 1: 1878 try: 1879 sent = channel.send(mydata) 1880 if sent == len(mydata): 1881 break 1882 mydata = mydata[sent:] 1883 except (self.SSL_exceptions['WantWriteError'],self.SSL_exceptions['WantReadError']): 1884 time.sleep(0.2) 1885 continue 1886 except UnicodeEncodeError: 1887 if encode_done: 1888 raise 1889 mydata = mydata.encode('utf-8') 1890 encode_done = True 1891 continue 1892 else: 1893 channel.sendall(self.append_eos(data))
1894
1895 - def updateProgress(self, *args, **kwargs):
1896 message = args[0] 1897 if message != self.last_print: 1898 self.socketLog.log(ETP_LOGPRI_INFO,ETP_LOGLEVEL_NORMAL,str(args[0])) 1899 if self.__output != None and self.stdout_logging: 1900 self.__output.updateProgress(*args,**kwargs) 1901 self.last_print = message
1902