Revision 4331e4d8 vncauthproxy/proxy.py
b/vncauthproxy/proxy.py | ||
---|---|---|
88 | 88 |
# Take care of differences between python-daemon versions. |
89 | 89 |
try: |
90 | 90 |
from daemon import pidfile as pidlockfile |
91 |
except: |
|
91 |
except ImportError:
|
|
92 | 92 |
from daemon import pidlockfile |
93 | 93 |
|
94 | 94 |
|
95 | 95 |
logger = None |
96 | 96 |
|
97 | 97 |
|
98 |
class InternalError(Exception): |
|
99 |
"""Exception for internal vncauthproxy errors""" |
|
100 |
pass |
|
101 |
|
|
102 |
|
|
98 | 103 |
# Currently, gevent uses libevent-dns for asynchronous DNS resolution, |
99 | 104 |
# which opens a socket upon initialization time. Since we can't get the fd |
100 | 105 |
# reliably, We have to maintain all file descriptors open (which won't harm |
... | ... | |
241 | 246 |
self.debug("Connecting to %s:%s", *sa[:2]) |
242 | 247 |
server.connect(sa) |
243 | 248 |
self.debug("Connection to %s:%s successful", *sa[:2]) |
244 |
except socket.error: |
|
249 |
except socket.error as err: |
|
250 |
self.debug("Failed to perform sever hanshake, retrying...") |
|
245 | 251 |
server.close() |
246 | 252 |
server = None |
247 | 253 |
continue |
... | ... | |
254 | 260 |
gevent.sleep(VncAuthProxy.retry_wait) |
255 | 261 |
|
256 | 262 |
if server is None: |
257 |
raise Exception("Failed to connect to server")
|
|
263 |
raise InternalError("Failed to connect to server")
|
|
258 | 264 |
|
259 | 265 |
version = server.recv(1024) |
260 | 266 |
if not rfb.check_version(version): |
261 |
raise Exception("Unsupported RFB version: %s" % version.strip()) |
|
267 |
raise InternalError("Unsupported RFB version: %s" |
|
268 |
% version.strip()) |
|
262 | 269 |
|
263 | 270 |
server.send(rfb.RFB_VERSION_3_8 + "\n") |
264 | 271 |
|
265 | 272 |
res = server.recv(1024) |
266 | 273 |
types = rfb.parse_auth_request(res) |
267 | 274 |
if not types: |
268 |
raise Exception("Error handshaking with the server")
|
|
275 |
raise InternalError("Error handshaking with the server")
|
|
269 | 276 |
|
270 | 277 |
else: |
271 | 278 |
self.debug("Supported authentication types: %s", |
272 | 279 |
" ".join([str(x) for x in types])) |
273 | 280 |
|
274 | 281 |
if rfb.RFB_AUTHTYPE_NONE not in types: |
275 |
raise Exception("Error, server demands authentication")
|
|
282 |
raise InternalError("Error, server demands authentication")
|
|
276 | 283 |
|
277 | 284 |
server.send(rfb.to_u8(rfb.RFB_AUTHTYPE_NONE)) |
278 | 285 |
|
... | ... | |
281 | 288 |
res = rfb.from_u32(res) |
282 | 289 |
|
283 | 290 |
if res != 0: |
284 |
raise Exception("Authentication error")
|
|
291 |
raise InternalError("Authentication error")
|
|
285 | 292 |
|
286 | 293 |
# Reset the timeout for the rest of the session |
287 | 294 |
server.settimeout(None) |
... | ... | |
298 | 305 |
"status": "FAILED", |
299 | 306 |
} |
300 | 307 |
try: |
301 |
# TODO: support multiple forwardings in the same message? |
|
302 |
# |
|
303 | 308 |
# Control request, in JSON: |
304 | 309 |
# |
305 | 310 |
# { |
... | ... | |
340 | 345 |
|
341 | 346 |
if auth_user not in VncAuthProxy.authdb: |
342 | 347 |
msg = "Authentication failure: user not found" |
343 |
raise Exception(msg)
|
|
348 |
raise InternalError(msg)
|
|
344 | 349 |
|
345 | 350 |
(cipher, authdb_password) = VncAuthProxy.authdb[auth_user] |
346 | 351 |
if cipher == 'HA1': |
... | ... | |
349 | 354 |
|
350 | 355 |
if auth_password != authdb_password: |
351 | 356 |
msg = "Authentication failure: wrong password" |
352 |
raise Exception(msg)
|
|
357 |
raise InternalError(msg)
|
|
353 | 358 |
except KeyError: |
354 | 359 |
msg = "Malformed request: %s" % buf |
355 |
raise Exception(msg) |
|
360 |
raise InternalError(msg) |
|
361 |
except InternalError as err: |
|
362 |
self.warn(err) |
|
363 |
response['reason'] = str(err) |
|
364 |
client.send(json.dumps(response)) |
|
365 |
client.close() |
|
366 |
raise gevent.GreenletExit |
|
356 | 367 |
except Exception as err: |
357 |
logger.exception(err) |
|
358 |
msg = err.args |
|
359 |
self.warn(msg) |
|
360 |
response['reason'] = msg |
|
368 |
self.exception(err) |
|
369 |
self.error("Unexpected error") |
|
361 | 370 |
client.send(json.dumps(response)) |
362 | 371 |
client.close() |
363 | 372 |
raise gevent.GreenletExit |
364 | 373 |
|
365 | 374 |
server = None |
375 |
pool = None |
|
376 |
sport = sport_orig |
|
366 | 377 |
try: |
367 | 378 |
# If the client has so indicated, pick an ephemeral source port |
368 | 379 |
# randomly, and remove it from the port pool. |
... | ... | |
376 | 387 |
self.debug("Port %d already taken", sport) |
377 | 388 |
|
378 | 389 |
self.debug("Got port %d from pool, %d remaining", |
379 |
sport, len(ports))
|
|
390 |
sport, len(ports)) |
|
380 | 391 |
pool = ports |
381 |
else: |
|
382 |
sport = sport_orig |
|
383 |
pool = None |
|
384 | 392 |
|
385 | 393 |
self.sport = sport |
386 | 394 |
self.pool = pool |
387 | 395 |
|
388 |
self.listeners = get_listening_sockets(self, sport)
|
|
396 |
self.listeners = get_listening_sockets(sport) |
|
389 | 397 |
self._perform_server_handshake() |
390 | 398 |
|
391 | 399 |
self.info("New forwarding: %d (client req'd: %d) -> %s:%d", |
... | ... | |
397 | 405 |
"client: %d -> %s:%d]"), |
398 | 406 |
sport_orig, self.daddr, self.dport) |
399 | 407 |
raise gevent.GreenletExit |
400 |
except Exception, msg: |
|
401 |
self.error(msg) |
|
408 |
except InternalError as err: |
|
409 |
self.error(err) |
|
410 |
self.error(("FAILED forwarding: %d (client req'd: %d) -> " |
|
411 |
"%s:%d"), sport, sport_orig, self.daddr, self.dport) |
|
412 |
if pool: |
|
413 |
pool.append(sport) |
|
414 |
self.debug("Returned port %d to pool, %d remanining", |
|
415 |
sport, len(pool)) |
|
416 |
if server: |
|
417 |
server.close() |
|
418 |
raise gevent.GreenletExit |
|
419 |
except Exception as err: |
|
420 |
self.exception(err) |
|
421 |
self.error("Unexpected error") |
|
402 | 422 |
self.error(("FAILED forwarding: %d (client req'd: %d) -> " |
403 | 423 |
"%s:%d"), sport, sport_orig, self.daddr, self.dport) |
404 |
if not pool is None:
|
|
424 |
if pool:
|
|
405 | 425 |
pool.append(sport) |
406 | 426 |
self.debug("Returned port %d to pool, %d remanining", |
407 | 427 |
sport, len(pool)) |
408 |
if not server is None:
|
|
428 |
if server:
|
|
409 | 429 |
server.close() |
410 | 430 |
raise gevent.GreenletExit |
411 | 431 |
finally: |
... | ... | |
526 | 546 |
# all worker, socket and port cleanup |
527 | 547 |
self.debug("A forwarder died, our work here is done") |
528 | 548 |
raise gevent.GreenletExit |
529 |
except Exception, e:
|
|
549 |
except Exception as err:
|
|
530 | 550 |
# Any unhandled exception in the previous block |
531 | 551 |
# is an error and must be logged accordingly |
532 | 552 |
if not isinstance(e, gevent.GreenletExit): |
533 |
self.exception(e) |
|
534 |
raise e |
|
553 |
self.exception(err) |
|
554 |
self.error("Unexpected error") |
|
555 |
raise err |
|
535 | 556 |
finally: |
536 | 557 |
self._cleanup() |
537 | 558 |
|
... | ... | |
557 | 578 |
raise SystemExit |
558 | 579 |
|
559 | 580 |
|
560 |
def get_listening_sockets(logger, sport, saddr=None, reuse_addr=False):
|
|
581 |
def get_listening_sockets(sport, saddr=None, reuse_addr=False): |
|
561 | 582 |
sockets = [] |
562 | 583 |
|
563 | 584 |
# Use two sockets, one for IPv4, one for IPv6. IPv4-to-IPv6 mapped |
... | ... | |
582 | 603 |
s.listen(1) |
583 | 604 |
sockets.append(s) |
584 | 605 |
logger.debug("Listening on %s:%d", *sa[:2]) |
585 |
except socket.error, msg:
|
|
586 |
logger.error("Error binding to %s:%d: %s", sa[0], sa[1], msg[1])
|
|
606 |
except socket.error as err:
|
|
607 |
logger.error("Error binding to %s:%d: %s", sa[0], sa[1], err[1])
|
|
587 | 608 |
if s: |
588 | 609 |
s.close() |
589 | 610 |
while sockets: |
590 | 611 |
sock = sockets.pop().close() |
591 | 612 |
|
592 | 613 |
# Make sure we fail immediately if we cannot get a socket |
593 |
raise msg
|
|
614 |
raise InernalError(err)
|
|
594 | 615 |
|
595 | 616 |
return sockets |
596 | 617 |
|
... | ... | |
599 | 620 |
supported_ciphers = ('cleartext', 'HA1') |
600 | 621 |
|
601 | 622 |
users = {} |
602 |
with open(auth_file) as f: |
|
603 |
lines = [l.strip().split() for l in f.readlines()] |
|
623 |
try: |
|
624 |
with open(auth_file) as f: |
|
625 |
lines = [l.strip().split() for l in f.readlines()] |
|
604 | 626 |
|
605 |
for line in lines: |
|
606 |
if not line or line[0][0] == '#': |
|
607 |
continue |
|
627 |
for line in lines:
|
|
628 |
if not line or line[0][0] == '#':
|
|
629 |
continue
|
|
608 | 630 |
|
609 |
if len(line) != 2: |
|
610 |
raise Exception("Invaild user entry in auth file")
|
|
631 |
if len(line) != 2:
|
|
632 |
raise InternalError("Invaild user entry in auth file")
|
|
611 | 633 |
|
612 |
user = line[0] |
|
613 |
password = line[1] |
|
634 |
user = line[0]
|
|
635 |
password = line[1]
|
|
614 | 636 |
|
615 |
split_password = ('{cleartext}', password) |
|
616 |
if password[0] == '{': |
|
617 |
split_password = password[1:].split('}') |
|
618 |
if len(split_password) != 2 or not split_password[1] \ |
|
619 |
or split_password[0] not in supported_ciphers: |
|
620 |
raise Exception("Invalid password format in auth file") |
|
637 |
split_password = ('{cleartext}', password) |
|
638 |
if password[0] == '{': |
|
639 |
split_password = password[1:].split('}') |
|
640 |
if len(split_password) != 2 or not split_password[1] \ |
|
641 |
or split_password[0] not in supported_ciphers: |
|
642 |
raise InternalError("Invalid password format " |
|
643 |
"in auth file") |
|
621 | 644 |
|
622 |
if user in users: |
|
623 |
raise Exception("Duplicate user entry in auth file")
|
|
645 |
if user in users:
|
|
646 |
raise InternalError("Duplicate user entry in auth file")
|
|
624 | 647 |
|
625 |
users[user] = split_password |
|
648 |
users[user] = split_password |
|
649 |
except IOError as err: |
|
650 |
logger.error("Couldn't read auth file") |
|
651 |
raise InternalError(err) |
|
626 | 652 |
|
627 | 653 |
if not users: |
628 | 654 |
logger.warn("No users specified.") |
... | ... | |
786 | 812 |
|
787 | 813 |
try: |
788 | 814 |
VncAuthProxy.authdb = parse_auth_file(opts.auth_file) |
789 |
sockets = get_listening_sockets(logger, opts.listen_port, |
|
790 |
opts.listen_address, reuse_addr=True) |
|
791 |
except socket.error as err: |
|
815 |
except InternalError as err: |
|
816 |
logger.critical(err) |
|
817 |
sys.exit(1) |
|
818 |
except Exception as err: |
|
792 | 819 |
logger.exception(err) |
820 |
logger.error("Unexpected error") |
|
821 |
sys.exit(1) |
|
822 |
|
|
823 |
try: |
|
824 |
sockets = get_listening_sockets(opts.listen_port, opts.listen_address, |
|
825 |
reuse_addr=True) |
|
826 |
except InternalError as err: |
|
793 | 827 |
logger.critical("Error binding control socket") |
794 | 828 |
sys.exit(1) |
795 | 829 |
except Exception as err: |
796 | 830 |
logger.exception(err) |
797 |
logger.critical("Unexpected error: %s", err.args)
|
|
831 |
logger.error("Unexpected error")
|
|
798 | 832 |
sys.exit(1) |
799 | 833 |
|
800 | 834 |
while True: |
... | ... | |
815 | 849 |
continue |
816 | 850 |
except Exception, e: |
817 | 851 |
logger.exception(e) |
852 |
logger.error("Unexpected error") |
|
818 | 853 |
if client: |
819 | 854 |
client.close() |
820 | 855 |
continue |
Also available in: Unified diff