Package entropy :: Package services :: Module interfaces

Source Code for Module entropy.services.interfaces

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