Revision d49bd2fb vncauthproxy/proxy.py
b/vncauthproxy/proxy.py | ||
---|---|---|
57 | 57 |
DEFAULT_MAX_PORT = 30000 |
58 | 58 |
|
59 | 59 |
# SSL certificate / key files |
60 |
DEFAULT_CERT_FILE = "/etc/ssl/certs/cert.pem" |
|
61 |
DEFAULT_KEY_FILE = "/etc/ssl/certs/key.pem" |
|
60 |
DEFAULT_CERT_FILE = "/var/lib/vncauthproxy/cert.pem" |
|
61 |
DEFAULT_KEY_FILE = "/var/lib/vncauthproxy/key.pem" |
|
62 |
|
|
63 |
# Auth file |
|
64 |
DEFAULT_AUTH_FILE = "/var/lib/vncauthproxy/users" |
|
62 | 65 |
|
63 | 66 |
import os |
64 | 67 |
import sys |
... | ... | |
68 | 71 |
import daemon |
69 | 72 |
import random |
70 | 73 |
import daemon.runner |
74 |
import hashlib |
|
71 | 75 |
|
72 | 76 |
import rfb |
73 | 77 |
|
... | ... | |
307 | 311 |
# <destination port> |
308 | 312 |
# "password": |
309 | 313 |
# <the password to use to authenticate clients> |
314 |
# "auth_user": |
|
315 |
# <user for control connection authentication>, |
|
316 |
# "auth_password": |
|
317 |
# <password for control connection authentication>, |
|
310 | 318 |
# } |
311 | 319 |
# |
312 | 320 |
# The <password> is used for MITM authentication of clients |
... | ... | |
323 | 331 |
buf = client.recv(1024) |
324 | 332 |
req = json.loads(buf) |
325 | 333 |
|
334 |
auth_user = req['auth_user'] |
|
335 |
auth_password = req['auth_password'] |
|
326 | 336 |
sport_orig = int(req['source_port']) |
327 | 337 |
self.daddr = req['destination_address'] |
328 | 338 |
self.dport = int(req['destination_port']) |
329 | 339 |
self.password = req['password'] |
330 |
except Exception, e: |
|
331 |
self.warn("Malformed request: %s", buf) |
|
340 |
|
|
341 |
if auth_user not in VncAuthProxy.authdb: |
|
342 |
msg = "Authentication failure: user not found" |
|
343 |
raise Exception(msg) |
|
344 |
|
|
345 |
(cipher, authdb_password) = VncAuthProxy.authdb[auth_user] |
|
346 |
if cipher == 'HA1': |
|
347 |
message = auth_user + ':vncauthproxy:' + auth_password |
|
348 |
auth_password = hashlib.md5(message).hexdigest() |
|
349 |
|
|
350 |
if auth_password != authdb_password: |
|
351 |
msg = "Authentication failure: wrong password" |
|
352 |
raise Exception(msg) |
|
353 |
except KeyError: |
|
354 |
msg = "Malformed request: %s" % buf |
|
355 |
raise Exception(msg) |
|
356 |
except Exception as err: |
|
357 |
logger.exception(err) |
|
358 |
msg = err.args |
|
359 |
self.warn(msg) |
|
360 |
response['reason'] = msg |
|
332 | 361 |
client.send(json.dumps(response)) |
333 | 362 |
client.close() |
334 | 363 |
raise gevent.GreenletExit |
... | ... | |
566 | 595 |
return sockets |
567 | 596 |
|
568 | 597 |
|
598 |
def parse_auth_file(auth_file): |
|
599 |
supported_ciphers = ('cleartext', 'HA1') |
|
600 |
|
|
601 |
with open(auth_file) as f: |
|
602 |
users = {} |
|
603 |
lines = [l.strip().split() for l in f.readlines()] |
|
604 |
|
|
605 |
for line in lines: |
|
606 |
if not line or line[0][0] == '#': |
|
607 |
continue |
|
608 |
|
|
609 |
if len(line) != 2: |
|
610 |
raise Exception("Invaild user entry in auth file") |
|
611 |
|
|
612 |
user = line[0] |
|
613 |
password = line[1] |
|
614 |
|
|
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") |
|
621 |
|
|
622 |
if user in users: |
|
623 |
raise Exception("Duplicate user entry in auth file") |
|
624 |
|
|
625 |
users[user] = password |
|
626 |
|
|
627 |
return users |
|
628 |
|
|
629 |
|
|
569 | 630 |
def parse_arguments(args): |
570 | 631 |
from optparse import OptionParser |
571 | 632 |
|
... | ... | |
625 | 686 |
help=("The maximum port number to use for automatically-" |
626 | 687 |
"allocated ephemeral ports (default: %s)" % |
627 | 688 |
DEFAULT_MAX_PORT)) |
689 |
parser.add_option('--no-ssl', dest="no_ssl", |
|
690 |
default=False, action='store_true', |
|
691 |
help=("Disable SSL/TLS for control connections " |
|
692 |
"(default: False")) |
|
628 | 693 |
parser.add_option('--cert-file', dest="cert_file", |
629 | 694 |
default=DEFAULT_CERT_FILE, |
630 | 695 |
metavar='CERTFILE', |
... | ... | |
635 | 700 |
metavar='KEYFILE', |
636 | 701 |
help=("SSL key (default: %s)" % |
637 | 702 |
DEFAULT_KEY_FILE)) |
703 |
parser.add_option('--auth-file', dest="auth_file", |
|
704 |
default=DEFAULT_AUTH_FILE, |
|
705 |
metavar='AUTHFILE', |
|
706 |
help=("Authentication file (default: %s)" % |
|
707 |
DEFAULT_AUTH_FILE)) |
|
638 | 708 |
|
639 | 709 |
(opts, args) = parser.parse_args(args) |
640 | 710 |
|
... | ... | |
712 | 782 |
VncAuthProxy.ports = ports |
713 | 783 |
|
714 | 784 |
try: |
785 |
VncAuthProxy.authdb = parse_auth_file(opts.auth_file) |
|
715 | 786 |
sockets = get_listening_sockets(logger, opts.listen_port, |
716 | 787 |
opts.listen_address, reuse_addr=True) |
717 |
except socket.error: |
|
788 |
except socket.error as err: |
|
789 |
logger.exception(err) |
|
718 | 790 |
logger.critical("Error binding control socket") |
719 | 791 |
sys.exit(1) |
792 |
except Exception as err: |
|
793 |
logger.exception(err) |
|
794 |
logger.critical("Unexpected error: %s", err.args) |
|
795 |
sys.exit(1) |
|
720 | 796 |
|
721 | 797 |
while True: |
722 | 798 |
try: |
723 | 799 |
client = None |
724 |
client_sock = None |
|
725 | 800 |
rlist, _, _ = select(sockets, [], []) |
726 | 801 |
for ctrl in rlist: |
727 |
client_sock, _ = ctrl.accept() |
|
728 |
client = ssl.wrap_socket(client_sock, |
|
729 |
server_side=True, |
|
730 |
keyfile=opts.key_file, |
|
731 |
certfile=opts.cert_file, |
|
732 |
ssl_version=ssl.PROTOCOL_TLSv1) |
|
802 |
client, _ = ctrl.accept() |
|
803 |
if not no_ssl: |
|
804 |
client = ssl.wrap_socket(client, |
|
805 |
server_side=True, |
|
806 |
keyfile=opts.key_file, |
|
807 |
certfile=opts.cert_file, |
|
808 |
ssl_version=ssl.PROTOCOL_TLSv1) |
|
733 | 809 |
logger.info("New control connection") |
734 | 810 |
|
735 | 811 |
VncAuthProxy.spawn(logger, client) |
... | ... | |
738 | 814 |
logger.exception(e) |
739 | 815 |
if client: |
740 | 816 |
client.close() |
741 |
elif client_sock: |
|
742 |
client_sock.close() |
|
743 | 817 |
continue |
744 | 818 |
except SystemExit: |
745 | 819 |
break |
Also available in: Unified diff