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