Beispiel #1
0
 def authenticate_hmac(self, challenge_response, client_salt):
     self.sessions = None
     if not self.salt:
         log.error("Error: illegal challenge response received - salt cleared or unset")
         return None
     #ensure this salt does not get re-used:
     if client_salt is None:
         salt = self.salt
     else:
         salt = xor(self.salt, client_salt)
     self.salt = None
     entry = self.get_auth_info()
     log("authenticate(%s) auth-info=%s", self.username, entry)
     if entry is None:
         log.error("Error: authentication failed")
         log.error(" no password for '%s' in '%s'", self.username, self.password_filename)
         return None
     fpassword, uid, gid, displays, env_options, session_options = entry
     verify = hmac.HMAC(strtobytes(fpassword), strtobytes(salt), digestmod=hashlib.md5).hexdigest()
     log("authenticate(%s) password='******', hex(salt)=%s, hash=%s", challenge_response, fpassword, binascii.hexlify(strtobytes(salt)), verify)
     if not hmac.compare_digest(verify, challenge_response):
         log("expected '%s' but got '%s'", verify, challenge_response)
         log.error("Error: hmac password challenge for '%s' does not match", self.username)
         return False
     self.sessions = uid, gid, displays, env_options, session_options
     return True
Beispiel #2
0
	def _test_file_auth(self, name, module, genauthdata):
		#no file, no go:
		a = self._init_auth(module)
		assert a.requires_challenge()
		p = a.get_password()
		assert not p, "got a password from %s: %s" % (a, p) 
		#challenge twice is a fail
		assert a.get_challenge()
		assert not a.get_challenge()
		assert not a.get_challenge()
		for muck in (0, 1):
			f = tempfile.NamedTemporaryFile(prefix=name)
			filename = f.name
			with f:
				a = self._init_auth(module, {"password_file" : filename})
				password, filedata = genauthdata(a)
				print("saving password file data='%s' to '%s'", filedata, filename)
				f.write(strtobytes(filedata))
				f.flush()
				assert a.requires_challenge()
				salt, mac = a.get_challenge()
				assert salt
				assert mac=="hmac"
				password = strtobytes(password)
				client_salt = strtobytes(uuid.uuid4().hex+uuid.uuid4().hex)
				auth_salt = strtobytes(xor(salt, client_salt))
				if muck==0:
					verify = hmac.HMAC(password, auth_salt, digestmod=hashlib.md5).hexdigest()
					assert a.authenticate(verify, client_salt)
					assert not a.authenticate(verify, client_salt)
					assert a.get_password()==password
				elif muck==1:
					for verify in ("whatever", None, "bad"):
						assert not a.authenticate(verify, client_salt)
Beispiel #3
0
 def authenticate_hmac(self, challenge_response, client_salt):
     if not self.salt:
         log.error("Error: illegal challenge response received - salt cleared or unset")
         return None
     #ensure this salt does not get re-used:
     if client_salt is None:
         salt = self.salt
     else:
         salt = xor(self.salt, client_salt)
     self.salt = None
     password = self.get_password()
     if not password:
         log.error("Error: %s authentication failed", self)
         log.error(" no password defined for '%s'", self.username)
         return False
     verify = hmac.HMAC(strtobytes(password), strtobytes(salt), digestmod=hashlib.md5).hexdigest()
     log("authenticate(%s) password=%s, hex(salt)=%s, hash=%s", challenge_response, password, binascii.hexlify(strtobytes(salt)), verify)
     if hasattr(hmac, "compare_digest"):
         eq = hmac.compare_digest(verify, challenge_response)
     else:
         eq = verify==challenge_response
     if not eq:
         log("expected '%s' but got '%s'", verify, challenge_response)
         log.error("Error: hmac password challenge for '%s' does not match", self.username)
         return False
     return True
Beispiel #4
0
def get_key(password, key_salt, block_size, iterations):
    global backend
    from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC
    from cryptography.hazmat.primitives import hashes
    kdf = PBKDF2HMAC(algorithm=hashes.SHA1(), length=block_size, salt=strtobytes(key_salt), iterations=iterations, backend=backend)
    key = kdf.derive(strtobytes(password))
    return key
Beispiel #5
0
 def test_large_xor_speed(self):
     start = time.time()
     size = 1*1024*1024       #1MB
     zeroes  = strtobytes(chr(0)*size)
     ones    = strtobytes(chr(1)*size)
     count = 10
     for _ in range(count):
         self.check_xor(zeroes, ones, ones)
     end = time.time()
     speed = size/(end-start)/1024/1024
     #print("%iMB/s: took %ims on average (%s iterations)" % (speed, 1000*(end-start)/count, count))
     assert speed>0, "running the xor speed test took too long"
Beispiel #6
0
 def _process_challenge(self, packet):
     authlog("processing challenge: %s", packet[1:])
     def warn_server_and_exit(code, message, server_message="authentication failed"):
         authlog.error("Error: authentication failed:")
         authlog.error(" %s", message)
         self.disconnect_and_quit(code, server_message)
     if not self.has_password():
         warn_server_and_exit(EXIT_PASSWORD_REQUIRED, "this server requires authentication, please provide a password", "no password available")
         return
     password = self.load_password()
     if not password:
         warn_server_and_exit(EXIT_PASSWORD_FILE_ERROR, "failed to load password from file %s" % self.password_file, "no password available")
         return
     salt = packet[1]
     if self.encryption:
         assert len(packet)>=3, "challenge does not contain encryption details to use for the response"
         server_cipher = typedict(packet[2])
         key = self.get_encryption_key()
         if key is None:
             warn_server_and_exit(EXIT_ENCRYPTION, "the server does not use any encryption", "client requires encryption")
             return
         if not self.set_server_encryption(server_cipher, key):
             return
     #all server versions support a client salt,
     #they also tell us which digest to use:
     digest = packet[3]
     client_salt = get_hex_uuid()+get_hex_uuid()
     #TODO: use some key stretching algorigthm? (meh)
     try:
         from xpra.codecs.xor.cyxor import xor_str           #@UnresolvedImport
         salt = xor_str(salt, client_salt)
     except:
         salt = xor(salt, client_salt)
     if digest==b"hmac":
         import hmac, hashlib
         password = strtobytes(password)
         salt = strtobytes(salt)
         challenge_response = hmac.HMAC(password, salt, digestmod=hashlib.md5).hexdigest()
     elif digest==b"xor":
         #don't send XORed password unencrypted:
         if not self._protocol.cipher_out and not ALLOW_UNENCRYPTED_PASSWORDS:
             warn_server_and_exit(EXIT_ENCRYPTION, "server requested digest %s, cowardly refusing to use it without encryption" % digest, "invalid digest")
             return
         challenge_response = xor(password, salt)
     else:
         warn_server_and_exit(EXIT_PASSWORD_REQUIRED, "server requested an unsupported digest: %s" % digest, "invalid digest")
         return
     if digest:
         authlog("%s(%s, %s)=%s", digest, binascii.hexlify(password), binascii.hexlify(salt), challenge_response)
     self.password_sent = True
     self.remove_packet_handlers("challenge")
     self.send_hello(challenge_response, client_salt)
Beispiel #7
0
	def _test_hmac_auth(self, auth_class, password, **kwargs):
		for x in (password, "somethingelse"):
			a = self._init_auth(auth_class, **kwargs)
			assert a.requires_challenge()
			assert a.get_password()
			salt, mac = a.get_challenge()
			assert salt
			assert mac=="hmac", "invalid mac: %s" % mac
			client_salt = strtobytes(uuid.uuid4().hex+uuid.uuid4().hex)
			auth_salt = strtobytes(xor(salt, client_salt))
			verify = hmac.HMAC(x, auth_salt, digestmod=hashlib.md5).hexdigest()
			passed = a.authenticate(verify, client_salt)
			assert passed == (x==password), "expected authentication to %s with %s vs %s" % (["fail", "succeed"][x==password], x, password)
			assert not a.authenticate(verify, client_salt)
Beispiel #8
0
 def send_hello(self, challenge_response=None, client_salt=None):
     try:
         hello = self.make_hello_base()
         if self.has_password() and not challenge_response:
             #avoid sending the full hello: tell the server we want
             #a packet challenge first
             hello["challenge"] = True
         else:
             hello.update(self.make_hello())
     except InitExit as e:
         log.error("error preparing connection:")
         log.error(" %s", e)
         self.quit(EXIT_INTERNAL_ERROR)
         return
     except Exception as e:
         log.error("error preparing connection: %s", e, exc_info=True)
         self.quit(EXIT_INTERNAL_ERROR)
         return
     if challenge_response:
         assert self.has_password(), "got a password challenge response but we don't have a password! (malicious or broken server?)"
         hello["challenge_response"] = challenge_response
         if client_salt:
             hello["challenge_client_salt"] = client_salt
     log("send_hello(%s) packet=%s", binascii.hexlify(strtobytes(challenge_response or "")), hello)
     self.send("hello", hello)
Beispiel #9
0
def get_pixbuf_from_data(rgb_data, has_alpha, w, h, rowstride):
    if is_gtk3():
        data = array.array('B', strtobytes(rgb_data))
        return GdkPixbuf.Pixbuf.new_from_data(data, GdkPixbuf.Colorspace.RGB,
                                         True, 8, w, h, rowstride,
                                         None, None)
    return gdk.pixbuf_new_from_data(rgb_data, gdk.COLORSPACE_RGB, has_alpha, 8, w, h, rowstride)
Beispiel #10
0
 def test_xor_str(self):
     zeroes  = strtobytes(chr(0)*16)
     ones    = strtobytes(chr(1)*16)
     ff      = strtobytes(chr(255)*16)
     fe      = strtobytes(chr(254)*16)
     empty   = b""
     lstr    = b"\0x80"*64
     self.check_xor(zeroes, zeroes, zeroes)
     self.check_xor(ones, ones, zeroes)
     self.check_xor(ff, ones, fe)
     self.check_xor(fe, ones, ff)
     #feed some invalid data:
     self.fail_xor(ones, empty)
     self.fail_xor(empty, zeroes)
     self.fail_xor(lstr, ff)
     self.fail_xor(bool, int)
Beispiel #11
0
 def send_hello(self, challenge_response=None, client_salt=None):
     try:
         hello = self.make_hello_base()
         if (self.password_file or os.environ.get('XPRA_PASSWORD')) and not challenge_response:
             #avoid sending the full hello: tell the server we want
             #a packet challenge first
             hello["challenge"] = True
         else:
             hello.update(self.make_hello())
     except InitExit as e:
         log.error("error preparing connection:")
         log.error(" %s", e)
         self.quit(EXIT_INTERNAL_ERROR)
         return
     except Exception as e:
         log.error("error preparing connection: %s", e, exc_info=True)
         self.quit(EXIT_INTERNAL_ERROR)
         return
     if challenge_response:
         assert self.password_file or os.environ.get('XPRA_PASSWORD')
         hello["challenge_response"] = challenge_response
         if client_salt:
             hello["challenge_client_salt"] = client_salt
     log("send_hello(%s) packet=%s", binascii.hexlify(strtobytes(challenge_response or "")), hello)
     self.send("hello", hello)
Beispiel #12
0
def validate_backend(try_backend):
    import binascii
    from xpra.os_util import strtobytes
    try_backend.init()
    message = b"some message1234"
    password = "******"
    key_salt = DEFAULT_SALT
    iterations = DEFAULT_ITERATIONS
    block_size = DEFAULT_BLOCKSIZE
    key = try_backend.get_key(password, key_salt, block_size, iterations)
    log("validate_backend(%s) key=%s", try_backend, binascii.hexlify(key))
    assert key is not None, "backend %s failed to generate a key" % try_backend
    enc = try_backend.get_encryptor(key, DEFAULT_IV)
    log("validate_backend(%s) encryptor=%s", try_backend, enc)
    assert enc is not None, "backend %s failed to generate an encryptor" % enc
    dec = try_backend.get_decryptor(key, DEFAULT_IV)
    log("validate_backend(%s) decryptor=%s", try_backend, dec)
    assert dec is not None, "backend %s failed to generate a decryptor" % enc
    ev = enc.encrypt(message)
    evs = binascii.hexlify(strtobytes(ev))
    log("validate_backend(%s) encrypted(%s)=%s", try_backend, message, evs)
    dv = dec.decrypt(ev)
    log("validate_backend(%s) decrypted(%s)=%s", try_backend, evs, dv)
    assert dv==message
    log("validate_backend(%s) passed", try_backend)
Beispiel #13
0
 def nasty_rgb_via_png_paint(self, cairo_format, has_alpha, img_data, x, y, width, height, rowstride, rgb_format):
     log("nasty_rgb_via_png_paint%s", (cairo_format, has_alpha, len(img_data), x, y, width, height, rowstride, rgb_format))
     #PIL fallback
     PIL = get_codec("PIL")
     if has_alpha:
         oformat = "RGBA"
     else:
         oformat = "RGB"
     #use frombytes rather than frombuffer to be compatible with python3 new-style buffers
     #this is slower, but since this codepath is already dreadfully slow, we don't care
     bdata = strtobytes(memoryview_to_bytes(img_data))
     try:
         img = PIL.Image.frombytes(oformat, (width,height), bdata, "raw", rgb_format.replace("X", "A"), rowstride, 1)
     except ValueError as e:
         raise Exception("failed to parse raw %s data to %s: %s" % (rgb_format, oformat, e))
     #This is insane, the code below should work, but it doesn't:
     # img_data = bytearray(img.tostring('raw', oformat, 0, 1))
     # pixbuf = pixbuf_new_from_data(img_data, COLORSPACE_RGB, True, 8, width, height, rowstride)
     # success = self.cairo_paint_pixbuf(pixbuf, x, y)
     #So we still rountrip via PNG:
     png = BytesIOClass()
     img.save(png, format="PNG")
     reader = BytesIOClass(png.getvalue())
     png.close()
     img = cairo.ImageSurface.create_from_png(reader)
     self.cairo_paint_surface(img, x, y)
     return True
Beispiel #14
0
 def capsget(self, key, default=None):
     v = self.get(key)
     #py3k and bytes as keys...
     if v is None and type(key)==str:
         v = self.get(strtobytes(key), default)
     if sys.version >= '3' and type(v)==bytes:
         v = bytestostr(v)
     return v
Beispiel #15
0
def get_default_systemd_run():
    #don't use systemd-run on CentOS / RedHat
    #(it causes failures with "Failed to create bus connection: No such file or directory")
    from xpra.os_util import load_binary_file, strtobytes
    data = strtobytes(load_binary_file("/etc/redhat-release") or "")
    if data and (data.find(b"RedHat")>=0 or data.find(b"CentOS")>=0):
        return "no"
    return "auto"
Beispiel #16
0
	def test_env(self):
		for var_name in ("XPRA_PASSWORD", "SOME_OTHER_VAR_NAME"):
			password = strtobytes(uuid.uuid4().hex)
			os.environ[var_name] = password
			try:
				kwargs = {}
				if var_name!="XPRA_PASSWORD":
					kwargs["name"] = var_name
				self._test_hmac_auth(env_auth, password, name=var_name)
			finally:
				del os.environ[var_name]
Beispiel #17
0
 def check(self, str_value):
     b = strtobytes(str_value)
     assert b
     s = bytestostr(b)
     assert s
     assert s==str_value
     if not _memoryview:
         return
     mv = _memoryview(b)
     mvb = memoryview_to_bytes(mv)
     mvs = bytestostr(mvb)
     assert mvs==str_value
Beispiel #18
0
 def _add_chunks_to_queue(self,
                          chunks,
                          proto_flags,
                          start_send_cb=None,
                          end_send_cb=None):
     """ the write_lock must be held when calling this function """
     counter = 0
     items = []
     for index, level, data in chunks:
         scb, ecb = None, None
         #fire the start_send_callback just before the first packet is processed:
         if counter == 0:
             scb = start_send_cb
         #fire the end_send callback when the last packet (index==0) makes it out:
         if index == 0:
             ecb = end_send_cb
         payload_size = len(data)
         actual_size = payload_size
         if self.cipher_out:
             proto_flags |= Protocol.FLAGS_CIPHER
             #note: since we are padding: l!=len(data)
             padding = (self.cipher_out_block_size -
                        len(data) % self.cipher_out_block_size) * " "
             if len(padding) == 0:
                 padded = data
             else:
                 padded = data + padding
             actual_size = payload_size + len(padding)
             assert len(padded) == actual_size
             data = self.cipher_out.encrypt(padded)
             assert len(data) == actual_size
             log("sending %s bytes encrypted with %s padding", payload_size,
                 len(padding))
         if proto_flags & Protocol.FLAGS_NOHEADER:
             #for plain/text packets (ie: gibberish response)
             items.append((data, scb, ecb))
         elif pack_header_and_data is not None and actual_size < PACKET_JOIN_SIZE:
             if type(data) == unicode:
                 data = str(data)
             header_and_data = pack_header_and_data(
                 actual_size, proto_flags, level, index, payload_size, data)
             items.append((header_and_data, scb, ecb))
         else:
             header = pack_header(proto_flags, level, index, payload_size)
             items.append((header, scb, None))
             items.append((strtobytes(data), None, ecb))
         counter += 1
     self._write_queue.put(items)
     self.output_packetcount += 1
Beispiel #19
0
def init_client_mmap(token, mmap_group=None, socket_filename=None, size=128*1024*1024):
    """
        Initializes an mmap area, writes the token in it and returns:
            (success flag, mmap_area, mmap_size, temp_file, mmap_filename)
        The caller must keep hold of temp_file to ensure it does not get deleted!
        This is used by the client.
    """
    if not can_use_mmap():
        log.error("cannot use mmap: python version is too old?")
        return False, None, 0, None, None
    log("init_mmap(%s, %s, %s)", token, mmap_group, socket_filename)
    try:
        import mmap
        import tempfile
        from stat import S_IRUSR,S_IWUSR,S_IRGRP,S_IWGRP
        mmap_dir = os.getenv("TMPDIR", "/tmp")
        if not os.path.exists(mmap_dir):
            raise Exception("TMPDIR %s does not exist!" % mmap_dir)
        #create the mmap file, the mkstemp that is called via NamedTemporaryFile ensures
        #that the file is readable and writable only by the creating user ID
        temp = tempfile.NamedTemporaryFile(prefix="xpra.", suffix=".mmap", dir=mmap_dir)
        #keep a reference to it so it does not disappear!
        mmap_temp_file = temp
        mmap_filename = temp.name
        fd = temp.file.fileno()
        #set the group permissions and gid if the mmap-group option is specified
        if mmap_group and type(socket_filename)==str and os.path.exists(socket_filename):
            s = os.stat(socket_filename)
            os.fchown(fd, -1, s.st_gid)
            os.fchmod(fd, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP)
        assert size>=1024*1024, "mmap size is too small: %s (minimum is 1MB)" % to_std_unit(size)
        assert size<=1024*1024*1024, "mmap is too big: %s (maximum is 1GB)" % to_std_unit(size)
        unit = max(4096, mmap.PAGESIZE)
        mmap_size = roundup(size + 8, unit)
        log("using mmap file %s, fd=%s, size=%s", mmap_filename, fd, mmap_size)
        SEEK_SET = 0        #os.SEEK_SET==0 but this is not available in python2.4
        os.lseek(fd, mmap_size-1, SEEK_SET)
        assert os.write(fd, strtobytes('\x00'))
        os.lseek(fd, 0, SEEK_SET)
        mmap_area = mmap.mmap(fd, length=mmap_size)
        write_mmap_token(mmap_area, token)
        return True, mmap_area, mmap_size, mmap_temp_file, mmap_filename
    except Exception, e:
        log.error("failed to setup mmap: %s", e, exc_info=True)
        clean_mmap(mmap_filename)
        return False, None, 0, None, None
Beispiel #20
0
 def _add_chunks_to_queue(self, chunks, start_send_cb=None, end_send_cb=None):
     """ the write_lock must be held when calling this function """
     counter = 0
     items = []
     for proto_flags,index,level,data in chunks:
         scb, ecb = None, None
         #fire the start_send_callback just before the first packet is processed:
         if counter==0:
             scb = start_send_cb
         #fire the end_send callback when the last packet (index==0) makes it out:
         if index==0:
             ecb = end_send_cb
         payload_size = len(data)
         actual_size = payload_size
         if self.cipher_out:
             proto_flags |= FLAGS_CIPHER
             #note: since we are padding: l!=len(data)
             padding_size = self.cipher_out_block_size - (payload_size % self.cipher_out_block_size)
             if padding_size==0:
                 padded = data
             else:
                 # pad byte value is number of padding bytes added
                 padded = data + pad(self.cipher_out_padding, padding_size)
                 actual_size += padding_size
             assert len(padded)==actual_size, "expected padded size to be %i, but got %i" % (len(padded), actual_size)
             data = self.cipher_out.encrypt(padded)
             assert len(data)==actual_size, "expected encrypted size to be %i, but got %i" % (len(data), actual_size)
             cryptolog("sending %s bytes %s encrypted with %s padding", payload_size, self.cipher_out_name, padding_size)
         if proto_flags & FLAGS_NOHEADER:
             assert not self.cipher_out
             #for plain/text packets (ie: gibberish response)
             log("sending %s bytes without header", payload_size)
             items.append((data, scb, ecb))
         elif actual_size<PACKET_JOIN_SIZE:
             if type(data) not in JOIN_TYPES:
                 data = bytes(data)
             header_and_data = pack_header(proto_flags, level, index, payload_size) + data
             items.append((header_and_data, scb, ecb))
         else:
             header = pack_header(proto_flags, level, index, payload_size)
             items.append((header, scb, None))
             items.append((strtobytes(data), None, ecb))
         counter += 1
     self._write_queue.put(items)
     self.output_packetcount += 1
Beispiel #21
0
 def do_save(self, filename):
     log("do_save(%s)", filename)
     if not filename.lower().endswith(".zip"):
         filename = filename+".zip"
     basenoext, _ = os.path.splitext(os.path.basename(filename))
     data = self.get_text_data()
     import zipfile
     zf = zipfile.ZipFile(filename, mode='w', compression=zipfile.ZIP_DEFLATED)
     try:
         for title, tooltip, dtype, s in data:
             cfile = os.path.join(basenoext, title.replace(" ", "_")+"."+dtype)
             info = zipfile.ZipInfo(cfile, date_time=time.localtime(time.time()))
             info.compress_type = zipfile.ZIP_DEFLATED
             #very poorly documented:
             info.external_attr = 0o644 << 16
             info.comment = strtobytes(tooltip)
             zf.writestr(info, s)
     finally:
         zf.close()
Beispiel #22
0
def _cmp(o, r):
    #our own deep compare function,
    #ignores tuple vs list differences,
    #and gives us a clue about where the problem is
    if type(o)==type(r) and o==r:
        return
    if type(r) in (tuple, list) and type(o) in (tuple, list):
        assert len(r)==len(o), "list/tuple differs in length: expected %s but got %s" % (o, r)
        for i in range(len(r)):
            _cmp(o[i], r[i])
        return
    if type(r)==dict and type(o)==dict:
        for k,ov in o.items():
            #with py3k, the key can end up being bytes instead of string...
            rv = r.get(k, r.get(bytestostr(k), r.get(strtobytes(k))))
            assert rv is not None, "restored dict is missing %s: %s" % (k, r)
            _cmp(ov, rv)
        return
    import sys
    if sys.version_info[0]<3 and type(o)==unicode and type(r)==str:
        o = o.encode("utf-8")
    elif type(o)==bytes and type(r)==str:
        o = o.decode("utf-8")
    elif type(o)==str and type(r)==bytes:
        r = r.decode("utf-8")
    if o==r:
        return
    print("")
    print("original %s:" % type(o))
    print("returned %s:" % type(r))
    try:
        print("original: %s" % binascii.hexlify(str(o)))
        print("returned: %s" % binascii.hexlify(str(r)))
    except:
        pass
    assert False, "value does not match: expected %s (%s) but got %s (%s)" % (o, type(o), r, type(r))
Beispiel #23
0
def run_server(error_cb,
               opts,
               mode,
               xpra_file,
               extra_args,
               desktop_display=None):
    try:
        cwd = os.getcwd()
    except:
        cwd = os.path.expanduser("~")
        warn("current working directory does not exist, using '%s'\n" % cwd)
    validate_encryption(opts)
    if opts.encoding == "help" or "help" in opts.encodings:
        return show_encoding_help(opts)

    from xpra.server.socket_util import parse_bind_tcp, parse_bind_vsock
    bind_tcp = parse_bind_tcp(opts.bind_tcp)
    bind_ssl = parse_bind_tcp(opts.bind_ssl)
    bind_vsock = parse_bind_vsock(opts.bind_vsock)

    assert mode in ("start", "start-desktop", "upgrade", "shadow", "proxy")
    starting = mode == "start"
    starting_desktop = mode == "start-desktop"
    upgrading = mode == "upgrade"
    shadowing = mode == "shadow"
    proxying = mode == "proxy"
    clobber = upgrading or opts.use_display
    start_vfb = not shadowing and not proxying and not clobber

    if upgrading or shadowing:
        #there should already be one running
        opts.pulseaudio = False

    #get the display name:
    if shadowing and len(extra_args) == 0:
        if WIN32 or OSX:
            #just a virtual name for the only display available:
            display_name = ":0"
        else:
            from xpra.scripts.main import guess_X11_display
            dotxpra = DotXpra(opts.socket_dir, opts.socket_dirs)
            display_name = guess_X11_display(dotxpra)
    elif upgrading and len(extra_args) == 0:
        display_name = guess_xpra_display(opts.socket_dir, opts.socket_dirs)
    else:
        if len(extra_args) > 1:
            error_cb(
                "too many extra arguments (%i): only expected a display number"
                % len(extra_args))
        if len(extra_args) == 1:
            display_name = extra_args[0]
            if not shadowing and not proxying and not opts.use_display:
                display_name_check(display_name)
        else:
            if proxying:
                #find a free display number:
                dotxpra = DotXpra(opts.socket_dir, opts.socket_dirs)
                all_displays = dotxpra.sockets()
                #ie: [("LIVE", ":100"), ("LIVE", ":200"), ...]
                displays = [v[1] for v in all_displays]
                display_name = None
                for x in range(1000, 20000):
                    v = ":%s" % x
                    if v not in displays:
                        display_name = v
                        break
                if not display_name:
                    error_cb(
                        "you must specify a free virtual display name to use with the proxy server"
                    )
            elif opts.use_display:
                #only use automatic guess for xpra displays and not X11 displays:
                display_name = guess_xpra_display(opts.socket_dir,
                                                  opts.socket_dirs)
            else:
                # We will try to find one automaticaly
                # Use the temporary magic value 'S' as marker:
                display_name = 'S' + str(os.getpid())

    if not shadowing and not proxying and not upgrading and opts.exit_with_children and not opts.start_child:
        error_cb(
            "--exit-with-children specified without any children to spawn; exiting immediately"
        )

    atexit.register(run_cleanups)

    # Generate the script text now, because os.getcwd() will
    # change if/when we daemonize:
    from xpra.server.server_util import xpra_runner_shell_script, write_runner_shell_scripts, write_pidfile, find_log_dir
    script = xpra_runner_shell_script(xpra_file, cwd, opts.socket_dir)

    uid = int(opts.uid)
    gid = int(opts.gid)
    username = get_username_for_uid(uid)
    home = get_home_for_uid(uid)
    xauth_data = None
    if start_vfb:
        xauth_data = get_hex_uuid()
    ROOT = POSIX and getuid() == 0

    stdout = sys.stdout
    stderr = sys.stderr
    # Daemonize:
    if POSIX and opts.daemon:
        #daemonize will chdir to "/", so try to use an absolute path:
        if opts.password_file:
            opts.password_file = os.path.abspath(opts.password_file)
        from xpra.server.server_util import daemonize
        daemonize()

    # if pam is present, try to create a new session:
    pam = None
    protected_fds = []
    protected_env = {}
    PAM_OPEN = POSIX and envbool("XPRA_PAM_OPEN", ROOT and uid != 0)
    if PAM_OPEN:
        try:
            from xpra.server.pam import pam_session  #@UnresolvedImport
        except ImportError as e:
            stderr.write("Error: failed to import pam module\n")
            stderr.write(" %s" % e)
            PAM_OPEN = False
    if PAM_OPEN:
        fdc = FDChangeCaptureContext()
        with fdc:
            pam = pam_session(username)
            env = {
                #"XDG_SEAT"               : "seat1",
                #"XDG_VTNR"               : "0",
                "XDG_SESSION_TYPE": "x11",
                #"XDG_SESSION_CLASS"      : "user",
                "XDG_SESSION_DESKTOP": "xpra",
            }
            #maybe we should just bail out instead?
            if pam.start():
                pam.set_env(env)
                items = {}
                if display_name.startswith(":"):
                    items["XDISPLAY"] = display_name
                if xauth_data:
                    items["XAUTHDATA"] = xauth_data
                pam.set_items(items)
                if pam.open():
                    #we can't close it, because we're not going to be root any more,
                    #but since we're the process leader for the session,
                    #terminating will also close the session
                    #add_cleanup(pam.close)
                    protected_env = pam.get_envlist()
                    os.environ.update(protected_env)
        #closing the pam fd causes the session to be closed,
        #and we don't want that!
        protected_fds = fdc.get_new_fds()

    xrd = create_runtime_dir(uid, gid)

    if opts.pidfile:
        write_pidfile(opts.pidfile, uid, gid)

    if POSIX and not ROOT:
        # Write out a shell-script so that we can start our proxy in a clean
        # environment:
        write_runner_shell_scripts(script)

    if start_vfb or opts.daemon:
        #we will probably need a log dir
        #either for the vfb, or for our own log file
        log_dir = opts.log_dir or ""
        if not log_dir or log_dir.lower() == "auto":
            log_dir = find_log_dir(username, uid=uid, gid=gid)
            if not log_dir:
                raise InitException(
                    "cannot find or create a logging directory")
        #expose the log-dir as "XPRA_LOG_DIR",
        #this is used by Xdummy for the Xorg log file
        if "XPRA_LOG_DIR" not in os.environ:
            os.environ["XPRA_LOG_DIR"] = log_dir

        if opts.daemon:
            from xpra.server.server_util import select_log_file, open_log_file, redirect_std_to_log
            log_filename0 = select_log_file(log_dir, opts.log_file,
                                            display_name)
            logfd = open_log_file(log_filename0)
            if ROOT and (uid > 0 or gid > 0):
                try:
                    os.fchown(logfd, uid, gid)
                except:
                    pass
            stdout, stderr = redirect_std_to_log(logfd, *protected_fds)
            stderr.write("Entering daemon mode; " +
                         "any further errors will be reported to:\n" +
                         ("  %s\n" % log_filename0))

    #warn early about this:
    if (starting or starting_desktop) and desktop_display:
        print_DE_warnings(desktop_display, opts.pulseaudio, opts.notifications,
                          opts.dbus_launch)

    from xpra.log import Logger
    log = Logger("server")
    netlog = Logger("network")

    mdns_recs = []
    sockets = []

    #SSL sockets:
    wrap_socket_fn = None
    need_ssl = False
    ssl_opt = opts.ssl.lower()
    if ssl_opt in TRUE_OPTIONS or bind_ssl:
        need_ssl = True
    if opts.bind_tcp:
        if ssl_opt == "auto" and opts.ssl_cert:
            need_ssl = True
        elif ssl_opt == "tcp":
            need_ssl = True
        elif ssl_opt == "www":
            need_ssl = True
    if need_ssl:
        from xpra.scripts.main import ssl_wrap_socket_fn
        try:
            wrap_socket_fn = ssl_wrap_socket_fn(opts, server_side=True)
            netlog("wrap_socket_fn=%s", wrap_socket_fn)
        except Exception as e:
            netlog("SSL error", exc_info=True)
            cpaths = csv("'%s'" % x for x in (opts.ssl_cert, opts.ssl_key)
                         if x)
            raise InitException(
                "cannot create SSL socket, check your certificate paths (%s): %s"
                % (cpaths, e))

    from xpra.server.socket_util import setup_tcp_socket, setup_vsock_socket, setup_local_sockets
    min_port = int(opts.min_port)
    netlog("setting up SSL sockets: %s", bind_ssl)
    for host, iport in bind_ssl:
        if iport < min_port:
            error_cb("invalid %s port number %i (minimum value is %i)" %
                     (socktype, iport, min_port))
        _, tcp_socket, host_port = setup_tcp_socket(host, iport, "SSL")
        socket = ("SSL", wrap_socket_fn(tcp_socket), host_port)
        sockets.append(socket)
        rec = "ssl", [(host, iport)]
        netlog("%s : %s", rec, socket)
        mdns_recs.append(rec)

    # Initialize the TCP sockets before the display,
    # That way, errors won't make us kill the Xvfb
    # (which may not be ours to kill at that point)
    tcp_ssl = ssl_opt in TRUE_OPTIONS or (ssl_opt == "auto" and opts.ssl_cert)

    def add_tcp_mdns_rec(host, iport):
        rec = "tcp", [(host, iport)]
        netlog("%s : %s", rec, socket)
        mdns_recs.append(rec)
        if tcp_ssl:
            #SSL is also available on this TCP socket:
            rec = "ssl", [(host, iport)]
            netlog("%s : %s", rec, socket)
            mdns_recs.append(rec)

    netlog("setting up TCP sockets: %s", bind_tcp)
    for host, iport in bind_tcp:
        if iport < min_port:
            error_cb("invalid %s port number %i (minimum value is %i)" %
                     (socktype, iport, min_port))
        socket = setup_tcp_socket(host, iport)
        sockets.append(socket)
        add_tcp_mdns_rec(host, iport)

    # VSOCK:
    netlog("setting up vsock sockets: %s", bind_vsock)
    for cid, iport in bind_vsock:
        socket = setup_vsock_socket(cid, iport)
        sockets.append(socket)
        rec = "vsock", [("", iport)]
        netlog("%s : %s", rec, socket)
        mdns_recs.append(rec)

    # systemd socket activation:
    try:
        from xpra.server.sd_listen import get_sd_listen_sockets
    except ImportError:
        pass
    else:
        sd_sockets = get_sd_listen_sockets()
        netlog("systemd sockets: %s", sd_sockets)
        for stype, socket, addr in sd_sockets:
            sockets.append((stype, socket, addr))
            netlog("%s : %s", (stype, [addr]), socket)
            if stype == "tcp":
                host, iport = addr
                add_tcp_mdns_rec(host, iport)

    sanitize_env()
    if POSIX:
        if xrd:
            os.environ["XDG_RUNTIME_DIR"] = xrd
        os.environ["XDG_SESSION_TYPE"] = "x11"
        if not starting_desktop:
            os.environ["XDG_CURRENT_DESKTOP"] = opts.wm_name
        configure_imsettings_env(opts.input_method)
    if display_name[0] != 'S':
        os.environ["DISPLAY"] = display_name
        os.environ["CKCON_X11_DISPLAY"] = display_name
    else:
        try:
            del os.environ["DISPLAY"]
        except:
            pass
    os.environ.update(protected_env)
    log("env=%s", os.environ)

    # Start the Xvfb server first to get the display_name if needed
    from xpra.server.vfb_util import start_Xvfb, check_xvfb_process, verify_display_ready, verify_gdk_display, set_initial_resolution
    odisplay_name = display_name
    xvfb = None
    xvfb_pid = None
    if start_vfb:
        assert not proxying and xauth_data
        pixel_depth = validate_pixel_depth(opts.pixel_depth)
        xvfb, display_name = start_Xvfb(opts.xvfb, pixel_depth, display_name,
                                        cwd, uid, gid, xauth_data)
        xvfb_pid = xvfb.pid
        #always update as we may now have the "real" display name:
        os.environ["DISPLAY"] = display_name
        os.environ["CKCON_X11_DISPLAY"] = display_name
        os.environ.update(protected_env)
        if display_name != odisplay_name and pam:
            pam.set_items({"XDISPLAY": display_name})

    close_display = None
    if not proxying:

        def close_display():
            # Close our display(s) first, so the server dying won't kill us.
            # (if gtk has been loaded)
            gtk_mod = sys.modules.get("gtk")
            if gtk_mod:
                for d in gtk_mod.gdk.display_manager_get().list_displays():
                    d.close()
            if xvfb_pid:
                log.info("killing xvfb with pid %s", xvfb_pid)
                try:
                    os.kill(xvfb_pid, signal.SIGTERM)
                except OSError as e:
                    log.info("failed to kill xvfb process with pid %s:",
                             xvfb_pid)
                    log.info(" %s", e)

        add_cleanup(close_display)

    if opts.daemon:

        def noerr(fn, *args):
            try:
                fn(*args)
            except:
                pass

        log_filename1 = select_log_file(log_dir, opts.log_file, display_name)
        if log_filename0 != log_filename1:
            # we now have the correct log filename, so use it:
            os.rename(log_filename0, log_filename1)
            if odisplay_name != display_name:
                #this may be used by scripts, let's try not to change it:
                noerr(stderr.write, "Actual display used: %s\n" % display_name)
            noerr(stderr.write,
                  "Actual log file name is now: %s\n" % log_filename1)
            noerr(stderr.flush)
        noerr(stdout.close)
        noerr(stderr.close)
    #we should not be using stdout or stderr from this point:
    del stdout
    del stderr

    if not check_xvfb_process(xvfb):
        #xvfb problem: exit now
        return 1

    if ROOT and (uid != 0 or gid != 0):
        log("root: switching to uid=%i, gid=%i", uid, gid)
        setuidgid(uid, gid)
        os.environ.update({
            "HOME": home,
            "USER": username,
            "LOGNAME": username,
        })
        shell = get_shell_for_uid(uid)
        if shell:
            os.environ["SHELL"] = shell
        os.environ.update(protected_env)

    if opts.chdir:
        os.chdir(opts.chdir)

    display = None
    if not proxying:
        no_gtk()
        if POSIX and (starting or starting_desktop or shadowing):
            #check that we can access the X11 display:
            if not verify_display_ready(xvfb, display_name, shadowing):
                return 1
            display = verify_gdk_display(display_name)
            if not display:
                return 1
            import gtk  #@Reimport
            assert gtk
        #on win32, this ensures that we get the correct screen size to shadow:
        from xpra.platform.gui import init as gui_init
        gui_init()

    #setup unix domain socket:
    if not opts.socket_dir and not opts.socket_dirs:
        #we always need at least one valid socket dir
        from xpra.platform.paths import get_socket_dirs
        opts.socket_dirs = get_socket_dirs()
    local_sockets = setup_local_sockets(opts.bind, opts.socket_dir,
                                        opts.socket_dirs, display_name,
                                        clobber, opts.mmap_group,
                                        opts.socket_permissions, username, uid,
                                        gid)
    netlog("setting up local sockets: %s", local_sockets)
    for rec, cleanup_socket in local_sockets:
        socktype, socket, sockpath = rec
        #ie: ("unix-domain", sock, sockpath), cleanup_socket
        sockets.append(rec)
        netlog("%s : %s", (socktype, [sockpath]), socket)
        add_cleanup(cleanup_socket)
        if opts.mdns:
            ssh_port = get_ssh_port()
            rec = "ssh", [("", ssh_port)]
            netlog("%s : %s", rec, socket)
            if ssh_port and rec not in mdns_recs:
                mdns_recs.append(rec)

    kill_dbus = None
    if shadowing:
        from xpra.platform.shadow_server import ShadowServer
        app = ShadowServer()
        server_type_info = "shadow"
    elif proxying:
        from xpra.server.proxy.proxy_server import ProxyServer
        app = ProxyServer()
        server_type_info = "proxy"
    else:
        assert starting or starting_desktop or upgrading
        from xpra.x11.gtk2.gdk_display_source import init_gdk_display_source
        init_gdk_display_source()
        #(now we can access the X11 server)

        if xvfb_pid is not None:
            #save the new pid (we should have one):
            save_xvfb_pid(xvfb_pid)

        if POSIX:
            dbus_pid = -1
            dbus_env = {}
            if clobber:
                #get the saved pids and env
                dbus_pid = get_dbus_pid()
                dbus_env = get_dbus_env()
                log("retrieved existing dbus attributes")
            else:
                assert starting or starting_desktop
                if xvfb_pid is not None:
                    #save the new pid (we should have one):
                    save_xvfb_pid(xvfb_pid)
                bus_address = protected_env.get("DBUS_SESSION_BUS_ADDRESS")
                log("dbus_launch=%s, current DBUS_SESSION_BUS_ADDRESS=%s",
                    opts.dbus_launch, bus_address)
                if opts.dbus_launch and not bus_address:
                    #start a dbus server:
                    def kill_dbus():
                        log("kill_dbus: dbus_pid=%s" % dbus_pid)
                        if dbus_pid <= 0:
                            return
                        try:
                            os.kill(dbus_pid, signal.SIGINT)
                        except Exception as e:
                            log.warn(
                                "Warning: error trying to stop dbus with pid %i:",
                                dbus_pid)
                            log.warn(" %s", e)

                    add_cleanup(kill_dbus)
                    #this also updates os.environ with the dbus attributes:
                    dbus_pid, dbus_env = start_dbus(opts.dbus_launch)
                    if dbus_pid > 0:
                        save_dbus_pid(dbus_pid)
                    if dbus_env:
                        save_dbus_env(dbus_env)
            log("dbus attributes: pid=%s, env=%s", dbus_pid, dbus_env)
            if dbus_env:
                os.environ.update(dbus_env)
                os.environ.update(protected_env)

        log("env=%s", os.environ)
        try:
            # This import is delayed because the module depends on gtk:
            from xpra.x11.bindings.window_bindings import X11WindowBindings
            X11Window = X11WindowBindings()
            if (starting or
                    starting_desktop) and not clobber and opts.resize_display:
                set_initial_resolution(starting_desktop)
        except ImportError as e:
            log.error(
                "Failed to load Xpra server components, check your installation: %s"
                % e)
            return 1
        if starting or upgrading:
            if not X11Window.displayHasXComposite():
                log.error(
                    "Xpra 'start' subcommand runs as a compositing manager")
                log.error(
                    " it cannot use a display which lacks the XComposite extension!"
                )
                return 1
            if starting:
                #check for an existing window manager:
                from xpra.x11.gtk2.wm import wm_check
                if not wm_check(display, opts.wm_name, upgrading):
                    return 1
            log("XShape=%s", X11Window.displayHasXShape())
            from xpra.x11.server import XpraServer
            app = XpraServer(clobber)
            server_type_info = "xpra"
        else:
            assert starting_desktop
            from xpra.x11.desktop_server import XpraDesktopServer
            app = XpraDesktopServer()
            server_type_info = "xpra desktop"

    #publish mdns records:
    if opts.mdns:
        from xpra.os_util import strtobytes
        from xpra.platform.info import get_username
        from xpra.server.socket_util import mdns_publish
        mdns_info = {
            "display": display_name,
            "username": get_username(),
            "uuid": strtobytes(app.uuid),
            "platform": sys.platform,
            "type": {
                "xpra": "seamless",
                "xpra desktop": "desktop"
            }.get(server_type_info, server_type_info),
        }
        if opts.session_name:
            mdns_info["session"] = opts.session_name
        for mode, listen_on in mdns_recs:
            mdns_publish(display_name, mode, listen_on, mdns_info)

    try:
        app._ssl_wrap_socket = wrap_socket_fn
        app.original_desktop_display = desktop_display
        app.exec_cwd = opts.chdir or cwd
        app.init(opts)
        app.init_components(opts)
    except InitException as e:
        log.error("xpra server initialization error:")
        log.error(" %s", e)
        return 1
    except Exception as e:
        log.error("Error: cannot start the %s server",
                  server_type_info,
                  exc_info=True)
        log.error(str(e))
        log.info("")
        return 1

    #honour start child, html webserver, and setup child reaper
    if not proxying and not upgrading:
        if opts.exit_with_children:
            assert opts.start_child, "exit-with-children was specified but start-child is missing!"
        app.start_commands = opts.start
        app.start_child_commands = opts.start_child
        app.start_after_connect = opts.start_after_connect
        app.start_child_after_connect = opts.start_child_after_connect
        app.start_on_connect = opts.start_on_connect
        app.start_child_on_connect = opts.start_child_on_connect
        app.exec_start_commands()
    del opts

    log("%s(%s)", app.init_sockets, sockets)
    app.init_sockets(sockets)
    log("%s(%s)", app.init_when_ready, _when_ready)
    app.init_when_ready(_when_ready)

    try:
        #from here on, we own the vfb, even if we inherited one:
        if (starting or starting_desktop or upgrading) and clobber:
            #and it will be killed if exit cleanly:
            xvfb_pid = get_xvfb_pid()

        log("running %s", app.run)
        e = app.run()
        log("%s()=%s", app.run, e)
    except KeyboardInterrupt:
        log.info("stopping on KeyboardInterrupt")
        e = 0
    except Exception as e:
        log.error("server error", exc_info=True)
        e = -128
    if e > 0:
        # Upgrading/exiting, so leave X and dbus servers running
        if close_display:
            _cleanups.remove(close_display)
        if kill_dbus:
            _cleanups.remove(kill_dbus)
        from xpra.server.server_core import ServerCore
        if e == ServerCore.EXITING_CODE:
            log.info("exiting: not cleaning up Xvfb")
        else:
            log.info("upgrading: not cleaning up Xvfb")
        log("cleanups=%s", _cleanups)
        e = 0
    return e
Beispiel #24
0
 def get_password(self):
     file_data = self.load_password_file()
     if file_data is None:
         return None
     return strtobytes(file_data)
Beispiel #25
0
    def _process_challenge(self, packet):
        authlog("processing challenge: %s", packet[1:])

        def warn_server_and_exit(code,
                                 message,
                                 server_message="authentication failed"):
            authlog.error("Error: authentication failed:")
            authlog.error(" %s", message)
            self.disconnect_and_quit(code, server_message)

        if not self.has_password():
            warn_server_and_exit(
                EXIT_PASSWORD_REQUIRED,
                "this server requires authentication, please provide a password",
                "no password available")
            return
        password = self.load_password()
        if not password:
            warn_server_and_exit(
                EXIT_PASSWORD_FILE_ERROR,
                "failed to load password from file %s" % self.password_file,
                "no password available")
            return
        salt = packet[1]
        if self.encryption:
            assert len(
                packet
            ) >= 3, "challenge does not contain encryption details to use for the response"
            server_cipher = typedict(packet[2])
            key = self.get_encryption_key()
            if key is None:
                warn_server_and_exit(EXIT_ENCRYPTION,
                                     "the server does not use any encryption",
                                     "client requires encryption")
                return
            if not self.set_server_encryption(server_cipher, key):
                return
        #all server versions support a client salt,
        #they also tell us which digest to use:
        digest = packet[3]
        client_salt = get_salt(len(salt))
        #TODO: use some key stretching algorigthm? (meh)
        salt = xor(salt, client_salt)
        if digest == b"hmac":
            import hmac, hashlib
            password = strtobytes(password)
            salt = strtobytes(salt)
            challenge_response = hmac.HMAC(password,
                                           salt,
                                           digestmod=hashlib.md5).hexdigest()
        elif digest == b"xor":
            #don't send XORed password unencrypted:
            encrypted = self._protocol.cipher_out or self._protocol.get_info(
            ).get("type") == "ssl"
            if not encrypted and not ALLOW_UNENCRYPTED_PASSWORDS:
                warn_server_and_exit(
                    EXIT_ENCRYPTION,
                    "server requested digest %s, cowardly refusing to use it without encryption"
                    % digest, "invalid digest")
                return
            salt = salt[:len(password)]
            challenge_response = strtobytes(xor(password, salt))
        else:
            warn_server_and_exit(
                EXIT_PASSWORD_REQUIRED,
                "server requested an unsupported digest: %s" % digest,
                "invalid digest")
            return
        if digest:
            authlog("%s(%s, %s)=%s", digest, binascii.hexlify(password),
                    binascii.hexlify(salt), challenge_response)
        self.password_sent = True
        self.remove_packet_handlers("challenge")
        self.send_hello(challenge_response, client_salt)
Beispiel #26
0
 def get_auth_info(self):
     self.load_password_file()
     if not self.password_filedata:
         return None
     return self.password_filedata.get(strtobytes(self.username))
 def process_server_packet(self, proto, packet):
     packet_type = packet[0]
     log("process_server_packet: %s", packet_type)
     if packet_type==Protocol.CONNECTION_LOST:
         self.stop("server connection lost", proto)
         return
     elif packet_type=="disconnect":
         log("got disconnect from server: %s", packet[1])
         if self.exit:
             self.server_protocol.close()
         else:
             self.stop("disconnect from server: %s" % packet[1])
     elif packet_type=="hello":
         c = typedict(packet[1])
         maxw, maxh = c.intpair("max_desktop_size", (4096, 4096))
         caps = self.filter_server_caps(c)
         #add new encryption caps:
         if self.cipher:
             from xpra.net.crypto import crypto_backend_init, new_cipher_caps, DEFAULT_PADDING
             crypto_backend_init()
             padding_options = self.caps.strlistget("cipher.padding.options", [DEFAULT_PADDING])
             auth_caps = new_cipher_caps(self.client_protocol, self.cipher, self.encryption_key, padding_options)
             caps.update(auth_caps)
         #may need to bump packet size:
         proto.max_packet_size = maxw*maxh*4*4
         file_transfer = self.caps.boolget("file-transfer") and c.boolget("file-transfer")
         file_size_limit = max(self.caps.intget("file-size-limit"), c.intget("file-size-limit"))
         file_max_packet_size = int(file_transfer) * (1024 + file_size_limit*1024*1024)
         self.client_protocol.max_packet_size = max(self.client_protocol.max_packet_size, file_max_packet_size)
         self.server_protocol.max_packet_size = max(self.server_protocol.max_packet_size, file_max_packet_size)
         packet = ("hello", caps)
     elif packet_type=="info-response":
         #adds proxy info:
         #note: this is only seen by the client application
         #"xpra info" is a new connection, which talks to the proxy server...
         info = packet[1]
         info.update(self.get_proxy_info(proto))
     elif packet_type=="lost-window":
         wid = packet[1]
         #mark it as lost so we can drop any current/pending frames
         self.lost_windows.add(wid)
         #queue it so it gets cleaned safely (for video encoders mostly):
         self.encode_queue.put(packet)
         #and fall through so tell the client immediately
     elif packet_type=="draw":
         #use encoder thread:
         self.encode_queue.put(packet)
         #which will queue the packet itself when done:
         return
     #we do want to reformat cursor packets...
     #as they will have been uncompressed by the network layer already:
     elif packet_type=="cursor":
         #packet = ["cursor", x, y, width, height, xhot, yhot, serial, pixels, name]
         #or:
         #packet = ["cursor", "png", x, y, width, height, xhot, yhot, serial, pixels, name]
         #or:
         #packet = ["cursor", ""]
         if len(packet)>=8:
             #hard to distinguish png cursors from normal cursors...
             try:
                 int(packet[1])
                 self._packet_recompress(packet, 8, "cursor")
             except:
                 self._packet_recompress(packet, 9, "cursor")
     elif packet_type=="window-icon":
         self._packet_recompress(packet, 5, "icon")
     elif packet_type=="send-file":
         if packet[6]:
             packet[6] = Compressed("file-data", packet[6])
     elif packet_type=="send-file-chunk":
         if packet[3]:
             packet[3] = Compressed("file-chunk-data", packet[3])
     elif packet_type=="challenge":
         from xpra.net.crypto import get_salt
         #client may have already responded to the challenge,
         #so we have to handle authentication from this end
         salt = packet[1]
         digest = packet[3]
         client_salt = get_salt(len(salt))
         salt = xor_str(salt, client_salt)
         if digest!=b"hmac":
             self.stop("digest mode '%s' not supported", std(digest))
             return
         password = self.session_options.get("password")
         if not password:
             self.stop("authentication requested by the server, but no password available for this session")
             return
         import hmac, hashlib
         password = strtobytes(password)
         salt = strtobytes(salt)
         challenge_response = hmac.HMAC(password, salt, digestmod=hashlib.md5).hexdigest()
         log.info("sending %s challenge response", digest)
         self.send_hello(challenge_response, client_salt)
         return
     self.queue_client_packet(packet)
Beispiel #28
0
    def _process_send_file_chunk(self, packet):
        chunk_id, chunk, file_data, has_more = packet[1:5]
        chunk_id = net_utf8(chunk_id)
        filelog("_process_send_file_chunk%s", (chunk_id, chunk, "%i bytes" % len(file_data), has_more))
        chunk_state = self.receive_chunks_in_progress.get(chunk_id)
        if not chunk_state:
            filelog.error("Error: cannot find the file transfer id '%r'", chunk_id)
            self.cancel_file(chunk_id, "file transfer id %r not found" % chunk_id, chunk)
            return
        if chunk_state[-4]:
            filelog("got chunk for a cancelled file transfer, ignoring it")
            return
        def progress(position, error=None):
            start = chunk_state[0]
            send_id = chunk_state[-3]
            filesize = chunk_state[6]
            self.transfer_progress_update(False, send_id, monotonic()-start, position, filesize, error)
        fd = chunk_state[1]
        if chunk_state[-1]+1!=chunk:
            filelog.error("Error: chunk number mismatch, expected %i but got %i", chunk_state[-1]+1, chunk)
            self.cancel_file(chunk_id, "chunk number mismatch", chunk)
            osclose(fd)
            progress(-1, "chunk no mismatch")
            return
        file_data = strtobytes(file_data)
        #update chunk number:
        chunk_state[-1] = chunk
        digest = chunk_state[8]
        written = chunk_state[9]
        try:
            os.write(fd, file_data)
            digest.update(file_data)
            written += len(file_data)
            chunk_state[9] = written
        except OSError as e:
            filelog.error("Error: cannot write file chunk")
            filelog.error(" %s", e)
            self.cancel_file(chunk_id, "write error: %s" % e, chunk)
            osclose(fd)
            progress(-1, "write error (%s)" % e)
            return
        self.send("ack-file-chunk", chunk_id, True, "", chunk)
        if has_more:
            progress(written)
            timer = chunk_state[-2]
            if timer:
                self.source_remove(timer)
            #remote end will send more after receiving the ack
            timer = self.timeout_add(CHUNK_TIMEOUT, self._check_chunk_receiving, chunk_id, chunk)
            chunk_state[-2] = timer
            return
        self.receive_chunks_in_progress.pop(chunk_id, None)
        osclose(fd)
        #check file size and digest then process it:
        filename, mimetype, printit, openit, filesize, options = chunk_state[2:8]
        if written!=filesize:
            filelog.error("Error: expected a file of %i bytes, got %i", filesize, written)
            progress(-1, "file size mismatch")
            return
        expected_digest = options.strget("sha1")
        if expected_digest and digest.hexdigest()!=expected_digest:
            progress(-1, "checksum mismatch")
            self.digest_mismatch(filename, digest, expected_digest, "sha1")
            return

        progress(written)
        start_time = chunk_state[0]
        elapsed = monotonic()-start_time
        mimetype = bytestostr(mimetype)
        filelog("%i bytes received in %i chunks, took %ims", filesize, chunk, elapsed*1000)
        self.process_downloaded_file(filename, mimetype, printit, openit, filesize, options)
Beispiel #29
0
 def capsget(self, capabilities, key, default):
     v = capabilities.get(strtobytes(key), default)
     if sys.version >= '3' and type(v) == bytes:
         v = bytestostr(v)
     return v
Beispiel #30
0
def run_server(error_cb,
               opts,
               mode,
               xpra_file,
               extra_args,
               desktop_display=None):
    try:
        cwd = os.getcwd()
    except:
        cwd = os.path.expanduser("~")
        warn("current working directory does not exist, using '%s'\n" % cwd)
    validate_encryption(opts)
    if opts.encoding == "help" or "help" in opts.encodings:
        return show_encoding_help(opts)

    from xpra.server.socket_util import parse_bind_ip, parse_bind_vsock, get_network_logger
    bind_tcp = parse_bind_ip(opts.bind_tcp)
    bind_udp = parse_bind_ip(opts.bind_udp)
    bind_ssl = parse_bind_ip(opts.bind_ssl)
    bind_ws = parse_bind_ip(opts.bind_ws)
    bind_wss = parse_bind_ip(opts.bind_wss)
    bind_rfb = parse_bind_ip(opts.bind_rfb, 5900)
    bind_vsock = parse_bind_vsock(opts.bind_vsock)

    assert mode in ("start", "start-desktop", "upgrade", "shadow", "proxy")
    starting = mode == "start"
    starting_desktop = mode == "start-desktop"
    upgrading = mode == "upgrade"
    shadowing = mode == "shadow"
    proxying = mode == "proxy"
    clobber = upgrading or opts.use_display
    start_vfb = not shadowing and not proxying and not clobber

    if upgrading or shadowing:
        #there should already be one running
        opts.pulseaudio = False

    #get the display name:
    if shadowing and len(extra_args) == 0:
        if WIN32 or OSX:
            #just a virtual name for the only display available:
            display_name = ":0"
        else:
            from xpra.scripts.main import guess_X11_display
            dotxpra = DotXpra(opts.socket_dir, opts.socket_dirs)
            display_name = guess_X11_display(dotxpra, desktop_display)
    elif upgrading and len(extra_args) == 0:
        display_name = guess_xpra_display(opts.socket_dir, opts.socket_dirs)
    else:
        if len(extra_args) > 1:
            error_cb(
                "too many extra arguments (%i): only expected a display number"
                % len(extra_args))
        if len(extra_args) == 1:
            display_name = extra_args[0]
            if not shadowing and not proxying and not opts.use_display:
                display_name_check(display_name)
        else:
            if proxying:
                #find a free display number:
                dotxpra = DotXpra(opts.socket_dir, opts.socket_dirs)
                all_displays = dotxpra.sockets()
                #ie: [("LIVE", ":100"), ("LIVE", ":200"), ...]
                displays = [v[1] for v in all_displays]
                display_name = None
                for x in range(1000, 20000):
                    v = ":%s" % x
                    if v not in displays:
                        display_name = v
                        break
                if not display_name:
                    error_cb(
                        "you must specify a free virtual display name to use with the proxy server"
                    )
            elif opts.use_display:
                #only use automatic guess for xpra displays and not X11 displays:
                display_name = guess_xpra_display(opts.socket_dir,
                                                  opts.socket_dirs)
            else:
                # We will try to find one automaticaly
                # Use the temporary magic value 'S' as marker:
                display_name = 'S' + str(os.getpid())

    if not shadowing and not proxying and not upgrading and opts.exit_with_children and not opts.start_child:
        error_cb(
            "--exit-with-children specified without any children to spawn; exiting immediately"
        )

    atexit.register(run_cleanups)

    # Generate the script text now, because os.getcwd() will
    # change if/when we daemonize:
    from xpra.server.server_util import xpra_runner_shell_script, write_runner_shell_scripts, write_pidfile, find_log_dir, create_input_devices
    script = xpra_runner_shell_script(xpra_file, cwd, opts.socket_dir)

    uid = int(opts.uid)
    gid = int(opts.gid)
    username = get_username_for_uid(uid)
    home = get_home_for_uid(uid)
    xauth_data = None
    if start_vfb:
        xauth_data = get_hex_uuid()
    ROOT = POSIX and getuid() == 0

    protected_fds = []
    protected_env = {}
    stdout = sys.stdout
    stderr = sys.stderr
    # Daemonize:
    if POSIX and opts.daemon:
        #daemonize will chdir to "/", so try to use an absolute path:
        if opts.password_file:
            opts.password_file = tuple(
                os.path.abspath(x) for x in opts.password_file)
        from xpra.server.server_util import daemonize
        daemonize()

    displayfd = 0
    if POSIX and opts.displayfd:
        try:
            displayfd = int(opts.displayfd)
            if displayfd > 0:
                protected_fds.append(displayfd)
        except ValueError as e:
            stderr.write("Error: invalid displayfd '%s':\n" % opts.displayfd)
            stderr.write(" %s\n" % e)
            del e

    # if pam is present, try to create a new session:
    pam = None
    PAM_OPEN = POSIX and envbool("XPRA_PAM_OPEN", ROOT and uid != 0)
    if PAM_OPEN:
        try:
            from xpra.server.pam import pam_session  #@UnresolvedImport
        except ImportError as e:
            stderr.write("Error: failed to import pam module\n")
            stderr.write(" %s" % e)
            del e
            PAM_OPEN = False
    if PAM_OPEN:
        fdc = FDChangeCaptureContext()
        with fdc:
            pam = pam_session(username)
            env = {
                #"XDG_SEAT"               : "seat1",
                #"XDG_VTNR"               : "0",
                "XDG_SESSION_TYPE": "x11",
                #"XDG_SESSION_CLASS"      : "user",
                "XDG_SESSION_DESKTOP": "xpra",
            }
            #maybe we should just bail out instead?
            if pam.start():
                pam.set_env(env)
                items = {}
                if display_name.startswith(":"):
                    items["XDISPLAY"] = display_name
                if xauth_data:
                    items["XAUTHDATA"] = xauth_data
                pam.set_items(items)
                if pam.open():
                    #we can't close it, because we're not going to be root any more,
                    #but since we're the process leader for the session,
                    #terminating will also close the session
                    #add_cleanup(pam.close)
                    protected_env = pam.get_envlist()
                    os.environ.update(protected_env)
        #closing the pam fd causes the session to be closed,
        #and we don't want that!
        protected_fds += fdc.get_new_fds()

    #get XDG_RUNTIME_DIR from env options,
    #which may not be have updated os.environ yet when running as root with "--uid="
    xrd = os.path.abspath(parse_env(opts.env).get("XDG_RUNTIME_DIR", ""))
    if ROOT and (uid > 0 or gid > 0):
        #we're going to chown the directory if we create it,
        #ensure this cannot be abused, only use "safe" paths:
        if not any(x for x in ("/run/user/%i" % uid, "/tmp", "/var/tmp")
                   if xrd.startswith(x)):
            xrd = ""
        #these paths could cause problems if we were to create and chown them:
        if xrd.startswith("/tmp/.X11-unix") or xrd.startswith(
                "/tmp/.XIM-unix"):
            xrd = ""
    if not xrd:
        xrd = os.environ.get("XDG_RUNTIME_DIR")
    xrd = create_runtime_dir(xrd, uid, gid)
    if xrd:
        #this may override the value we get from pam
        #with the value supplied by the user:
        protected_env["XDG_RUNTIME_DIR"] = xrd

    if opts.pidfile:
        write_pidfile(opts.pidfile, uid, gid)

    if POSIX and not ROOT:
        # Write out a shell-script so that we can start our proxy in a clean
        # environment:
        write_runner_shell_scripts(script)

    if start_vfb or opts.daemon:
        #we will probably need a log dir
        #either for the vfb, or for our own log file
        log_dir = opts.log_dir or ""
        if not log_dir or log_dir.lower() == "auto":
            log_dir = find_log_dir(username, uid=uid, gid=gid)
            if not log_dir:
                raise InitException(
                    "cannot find or create a logging directory")
        #expose the log-dir as "XPRA_LOG_DIR",
        #this is used by Xdummy for the Xorg log file
        if "XPRA_LOG_DIR" not in os.environ:
            os.environ["XPRA_LOG_DIR"] = log_dir

        if opts.daemon:
            from xpra.server.server_util import select_log_file, open_log_file, redirect_std_to_log
            log_filename0 = osexpand(
                select_log_file(log_dir, opts.log_file, display_name),
                username, uid, gid)
            logfd = open_log_file(log_filename0)
            if ROOT and (uid > 0 or gid > 0):
                try:
                    os.fchown(logfd, uid, gid)
                except:
                    pass
            stdout, stderr = redirect_std_to_log(logfd, *protected_fds)
            try:
                stderr.write("Entering daemon mode; " +
                             "any further errors will be reported to:\n" +
                             ("  %s\n" % log_filename0))
            except:
                #we tried our best, logging another error won't help
                pass

    #warn early about this:
    if (starting or starting_desktop
        ) and desktop_display and opts.notifications and not opts.dbus_launch:
        print_DE_warnings()

    log = get_util_logger()
    netlog = get_network_logger()

    mdns_recs = {}
    sockets = []

    #SSL sockets:
    wrap_socket_fn = None
    need_ssl = False
    ssl_opt = opts.ssl.lower()
    if ssl_opt in TRUE_OPTIONS or bind_ssl or bind_wss:
        need_ssl = True
    if opts.bind_tcp or opts.bind_ws:
        if ssl_opt == "auto" and opts.ssl_cert:
            need_ssl = True
        elif ssl_opt == "tcp" and opts.bind_tcp:
            need_ssl = True
        elif ssl_opt == "www":
            need_ssl = True
    if need_ssl:
        from xpra.scripts.main import ssl_wrap_socket_fn
        try:
            wrap_socket_fn = ssl_wrap_socket_fn(opts, server_side=True)
            netlog("wrap_socket_fn=%s", wrap_socket_fn)
        except Exception as e:
            netlog("SSL error", exc_info=True)
            cpaths = csv("'%s'" % x for x in (opts.ssl_cert, opts.ssl_key)
                         if x)
            raise InitException(
                "cannot create SSL socket, check your certificate paths (%s): %s"
                % (cpaths, e))

    from xpra.server.socket_util import setup_tcp_socket, setup_udp_socket, setup_vsock_socket, setup_local_sockets, has_dual_stack
    min_port = int(opts.min_port)

    def hosts(host_str):
        if host_str == "*":
            if has_dual_stack():
                #IPv6 will also listen for IPv4:
                return ["::"]
            #no dual stack, so we have to listen on both IPv4 and IPv6 explicitly:
            return ["0.0.0.0", "::"]
        return [host_str]

    def add_mdns(socktype, host_str, port):
        recs = mdns_recs.setdefault(socktype.lower(), [])
        for host in hosts(host_str):
            rec = (host, port)
            if rec not in recs:
                recs.append(rec)

    def add_tcp_socket(socktype, host_str, iport):
        if iport < min_port:
            error_cb("invalid %s port number %i (minimum value is %i)" %
                     (socktype, iport, min_port))
        for host in hosts(host_str):
            socket = setup_tcp_socket(host, iport, socktype)
            sockets.append(socket)
            add_mdns(socktype, host, iport)

    def add_udp_socket(socktype, host_str, iport):
        if iport < min_port:
            error_cb("invalid %s port number %i (minimum value is %i)" %
                     (socktype, iport, min_port))
        for host in hosts(host_str):
            socket = setup_udp_socket(host, iport, socktype)
            sockets.append(socket)
            add_mdns(socktype, host, iport)

    # Initialize the TCP sockets before the display,
    # That way, errors won't make us kill the Xvfb
    # (which may not be ours to kill at that point)
    ws_upgrades = opts.html and (os.path.isabs(opts.html) or opts.html.lower()
                                 in TRUE_OPTIONS + ["auto"])
    netlog("setting up SSL sockets: %s", csv(bind_ssl))
    for host, iport in bind_ssl:
        add_tcp_socket("ssl", host, iport)
        if ws_upgrades:
            add_tcp_socket("wss", host, iport)
    netlog("setting up https / wss (secure websockets): %s", csv(bind_wss))
    for host, iport in bind_wss:
        add_tcp_socket("wss", host, iport)
    tcp_ssl = ssl_opt in TRUE_OPTIONS or (ssl_opt == "auto" and opts.ssl_cert)
    netlog("setting up TCP sockets: %s", csv(bind_tcp))
    for host, iport in bind_tcp:
        add_tcp_socket("tcp", host, iport)
        if tcp_ssl:
            add_mdns("ssl", host, iport)
        if ws_upgrades:
            add_mdns("ws", host, iport)
        if ws_upgrades and tcp_ssl:
            add_mdns("wss", host, iport)
    netlog("setting up UDP sockets: %s", csv(bind_udp))
    for host, iport in bind_udp:
        add_udp_socket("udp", host, iport)
    netlog("setting up http / ws (websockets): %s", csv(bind_ws))
    for host, iport in bind_ws:
        add_tcp_socket("ws", host, iport)
        if tcp_ssl:
            add_mdns("wss", host, iport)
    if bind_rfb and (proxying or starting):
        log.warn("Warning: bind-rfb sockets cannot be used with '%s' mode" %
                 mode)
    else:
        netlog("setting up rfb sockets: %s", csv(bind_rfb))
        for host, iport in bind_rfb:
            add_tcp_socket("rfb", host, iport)
    netlog("setting up vsock sockets: %s", csv(bind_vsock))
    for cid, iport in bind_vsock:
        socket = setup_vsock_socket(cid, iport)
        sockets.append(socket)
        #add_mdns("vsock", str(cid), iport)

    # systemd socket activation:
    try:
        from xpra.platform.xposix.sd_listen import get_sd_listen_sockets
    except ImportError:
        pass
    else:
        sd_sockets = get_sd_listen_sockets()
        netlog("systemd sockets: %s", sd_sockets)
        for stype, socket, addr in sd_sockets:
            sockets.append((stype, socket, addr))
            netlog("%s : %s", (stype, [addr]), socket)
            if stype == "tcp":
                host, iport = addr
                add_mdns("tcp", host, iport)

    sanitize_env()
    if POSIX:
        if xrd:
            os.environ["XDG_RUNTIME_DIR"] = xrd
        os.environ["XDG_SESSION_TYPE"] = "x11"
        if not starting_desktop:
            os.environ["XDG_CURRENT_DESKTOP"] = opts.wm_name
        configure_imsettings_env(opts.input_method)
    if display_name[0] != 'S':
        os.environ["DISPLAY"] = display_name
        os.environ["CKCON_X11_DISPLAY"] = display_name
    else:
        try:
            del os.environ["DISPLAY"]
        except:
            pass
    os.environ.update(protected_env)
    log("env=%s", os.environ)

    UINPUT_UUID_LEN = 12
    UINPUT_UUID_MIN_LEN = 12
    UINPUT_UUID_MAX_LEN = 32
    # Start the Xvfb server first to get the display_name if needed
    odisplay_name = display_name
    xvfb = None
    xvfb_pid = None
    uinput_uuid = None
    if start_vfb:
        assert not proxying and xauth_data
        pixel_depth = validate_pixel_depth(opts.pixel_depth, starting_desktop)
        from xpra.x11.vfb_util import start_Xvfb, check_xvfb_process
        from xpra.server.server_util import has_uinput
        uinput_uuid = None
        if has_uinput() and opts.input_devices.lower() in (
                "uinput", "auto") and not shadowing:
            from xpra.os_util import get_rand_chars
            uinput_uuid = get_rand_chars(UINPUT_UUID_LEN)
        xvfb, display_name, cleanups = start_Xvfb(opts.xvfb, pixel_depth,
                                                  display_name, cwd, uid, gid,
                                                  username, xauth_data,
                                                  uinput_uuid)
        for f in cleanups:
            add_cleanup(f)
        xvfb_pid = xvfb.pid
        #always update as we may now have the "real" display name:
        os.environ["DISPLAY"] = display_name
        os.environ["CKCON_X11_DISPLAY"] = display_name
        os.environ.update(protected_env)
        if display_name != odisplay_name and pam:
            pam.set_items({"XDISPLAY": display_name})

        def check_xvfb():
            return check_xvfb_process(xvfb)
    else:
        if POSIX and clobber:
            #if we're meant to be using a private XAUTHORITY file,
            #make sure to point to it:
            from xpra.x11.vfb_util import get_xauthority_path
            xauthority = get_xauthority_path(display_name, username, uid, gid)
            if os.path.exists(xauthority):
                os.environ["XAUTHORITY"] = xauthority

        def check_xvfb():
            return True

    if POSIX and not OSX and displayfd > 0:
        from xpra.platform.displayfd import write_displayfd
        try:
            display = display_name[1:]
            log("writing display='%s' to displayfd=%i", display, displayfd)
            assert write_displayfd(displayfd, display), "timeout"
        except Exception as e:
            log.error("write_displayfd failed", exc_info=True)
            log.error("Error: failed to write '%s' to fd=%s", display_name,
                      displayfd)
            log.error(" %s", str(e) or type(e))
            del e
        try:
            os.close(displayfd)
        except:
            pass

    kill_display = None
    if not proxying:
        add_cleanup(close_gtk_display)
    if not proxying and not shadowing:

        def kill_display():
            if xvfb_pid:
                kill_xvfb(xvfb_pid)

        add_cleanup(kill_display)

    if opts.daemon:

        def noerr(fn, *args):
            try:
                fn(*args)
            except:
                pass

        log_filename1 = osexpand(
            select_log_file(log_dir, opts.log_file, display_name), username,
            uid, gid)
        if log_filename0 != log_filename1:
            # we now have the correct log filename, so use it:
            os.rename(log_filename0, log_filename1)
            if odisplay_name != display_name:
                #this may be used by scripts, let's try not to change it:
                noerr(stderr.write, "Actual display used: %s\n" % display_name)
            noerr(stderr.write,
                  "Actual log file name is now: %s\n" % log_filename1)
            noerr(stderr.flush)
        noerr(stdout.close)
        noerr(stderr.close)
    #we should not be using stdout or stderr from this point:
    del stdout
    del stderr

    if not check_xvfb():
        #xvfb problem: exit now
        return 1

    #create devices for vfb if needed:
    devices = {}
    if not start_vfb and not proxying and not shadowing:
        #try to find the existing uinput uuid:
        #use a subprocess to avoid polluting our current process
        #with X11 connections before we get a chance to change uid
        cmd = ["xprop", "-display", display_name, "-root", "_XPRA_UINPUT_ID"]
        try:
            code, out, err = get_status_output(cmd)
        except Exception as e:
            log("failed to get existing uinput id: %s", e)
            del e
        else:
            log("Popen(%s)=%s", cmd, (code, out, err))
            if code == 0 and out.find("=") > 0:
                uinput_uuid = out.split("=", 1)[1]
                log("raw uinput uuid=%s", uinput_uuid)
                uinput_uuid = strtobytes(uinput_uuid.strip('\n\r"\\ '))
                if uinput_uuid:
                    if len(uinput_uuid) > UINPUT_UUID_MAX_LEN or len(
                            uinput_uuid) < UINPUT_UUID_MIN_LEN:
                        log.warn("Warning: ignoring invalid uinput id:")
                        log.warn(" '%s'", uinput_uuid)
                        uinput_uuid = None
                    else:
                        log.info("retrieved existing uinput id: %s",
                                 bytestostr(uinput_uuid))
    if uinput_uuid:
        devices = create_input_devices(uinput_uuid, uid)

    if ROOT and (uid != 0 or gid != 0):
        log("root: switching to uid=%i, gid=%i", uid, gid)
        setuidgid(uid, gid)
        os.environ.update({
            "HOME": home,
            "USER": username,
            "LOGNAME": username,
        })
        shell = get_shell_for_uid(uid)
        if shell:
            os.environ["SHELL"] = shell
        #now we've changed uid, it is safe to honour all the env updates:
        configure_env(opts.env)
        os.environ.update(protected_env)

    if opts.chdir:
        os.chdir(opts.chdir)

    display = None
    if not proxying:
        no_gtk()
        if POSIX and not OSX and (starting or starting_desktop or shadowing):
            #check that we can access the X11 display:
            from xpra.x11.vfb_util import verify_display_ready
            if not verify_display_ready(xvfb, display_name, shadowing):
                return 1
            if not PYTHON3:
                from xpra.x11.gtk2.gdk_display_util import verify_gdk_display  #@UnusedImport
            else:
                from xpra.x11.gtk3.gdk_display_util import verify_gdk_display  #@Reimport
            display = verify_gdk_display(display_name)
            if not display:
                return 1
        #on win32, this ensures that we get the correct screen size to shadow:
        from xpra.platform.gui import init as gui_init
        gui_init()

    #setup unix domain socket:
    if not opts.socket_dir and not opts.socket_dirs:
        #we always need at least one valid socket dir
        from xpra.platform.paths import get_socket_dirs
        opts.socket_dirs = get_socket_dirs()
    local_sockets = setup_local_sockets(opts.bind, opts.socket_dir,
                                        opts.socket_dirs, display_name,
                                        clobber, opts.mmap_group,
                                        opts.socket_permissions, username, uid,
                                        gid)
    netlog("setting up local sockets: %s", local_sockets)
    for rec, cleanup_socket in local_sockets:
        socktype, socket, sockpath = rec
        #ie: ("unix-domain", sock, sockpath), cleanup_socket
        sockets.append(rec)
        netlog("%s %s : %s", socktype, sockpath, socket)
        add_cleanup(cleanup_socket)
        if opts.mdns:
            ssh_port = get_ssh_port()
            netlog("ssh %s:%s : %s", "", ssh_port, socket)
            if ssh_port:
                add_mdns("ssh", "", ssh_port)

    kill_dbus = None
    if shadowing:
        from xpra.platform.shadow_server import ShadowServer
        app = ShadowServer()
    elif proxying:
        from xpra.server.proxy.proxy_server import ProxyServer
        app = ProxyServer()
    else:
        if not check_xvfb():
            return 1
        assert starting or starting_desktop or upgrading
        from xpra.x11.gtk2.gdk_display_source import init_gdk_display_source, close_gdk_display_source
        init_gdk_display_source()
        insert_cleanup(close_gdk_display_source)
        #(now we can access the X11 server)

        #make sure the pid we save is the real one:
        if not check_xvfb():
            return 1
        if xvfb_pid is not None:
            #save the new pid (we should have one):
            save_xvfb_pid(xvfb_pid)

        if POSIX:
            save_uinput_id(uinput_uuid or "")
            dbus_pid = -1
            dbus_env = {}
            if clobber:
                #get the saved pids and env
                dbus_pid = get_dbus_pid()
                dbus_env = get_dbus_env()
                log("retrieved existing dbus attributes")
            else:
                assert starting or starting_desktop
                if xvfb_pid is not None:
                    #save the new pid (we should have one):
                    save_xvfb_pid(xvfb_pid)
                bus_address = protected_env.get("DBUS_SESSION_BUS_ADDRESS")
                log("dbus_launch=%s, current DBUS_SESSION_BUS_ADDRESS=%s",
                    opts.dbus_launch, bus_address)
                if opts.dbus_launch and not bus_address:
                    #start a dbus server:
                    def kill_dbus():
                        log("kill_dbus: dbus_pid=%s" % dbus_pid)
                        if dbus_pid <= 0:
                            return
                        try:
                            os.kill(dbus_pid, signal.SIGINT)
                        except Exception as e:
                            log.warn(
                                "Warning: error trying to stop dbus with pid %i:",
                                dbus_pid)
                            log.warn(" %s", e)

                    add_cleanup(kill_dbus)
                    #this also updates os.environ with the dbus attributes:
                    dbus_pid, dbus_env = start_dbus(opts.dbus_launch)
                    if dbus_pid > 0:
                        save_dbus_pid(dbus_pid)
                    if dbus_env:
                        save_dbus_env(dbus_env)
            log("dbus attributes: pid=%s, env=%s", dbus_pid, dbus_env)
            if dbus_env:
                os.environ.update(dbus_env)
                os.environ.update(protected_env)

        if opts.forward_xdg_open and os.environ.get(
                "DISPLAY") and local_sockets:
            #find one xdg-open can use to talk back to us:
            udpaths = [
                sockpath for (stype, _, sockpath), _ in local_sockets
                if stype == "unix-domain"
            ]
            if udpaths:
                os.environ["XPRA_XDG_OPEN_SERVER_SOCKET"] = udpaths[0]
                for x in ("/usr/libexec/xpra", "/usr/lib/xpra"):
                    xdg_override = os.path.join(x, "xdg-open")
                    if os.path.exists(xdg_override):
                        os.environ["PATH"] = x + os.pathsep + os.environ.get(
                            "PATH", "")
                        break

        log("env=%s", os.environ)
        try:
            # This import is delayed because the module depends on gtk:
            from xpra.x11.bindings.window_bindings import X11WindowBindings
            X11Window = X11WindowBindings()
            if (starting or
                    starting_desktop) and not clobber and opts.resize_display:
                from xpra.x11.vfb_util import set_initial_resolution
                set_initial_resolution(starting_desktop)
        except ImportError as e:
            log.error(
                "Failed to load Xpra server components, check your installation: %s"
                % e)
            return 1
        if starting or upgrading:
            if not X11Window.displayHasXComposite():
                log.error(
                    "Xpra 'start' subcommand runs as a compositing manager")
                log.error(
                    " it cannot use a display which lacks the XComposite extension!"
                )
                return 1
            assert not PYTHON3
            if starting:
                #check for an existing window manager:
                from xpra.x11.gtk2.wm import wm_check
                if not wm_check(display, opts.wm_name, upgrading):
                    return 1
            log("XShape=%s", X11Window.displayHasXShape())
            from xpra.x11.server import XpraServer
            app = XpraServer(clobber)
        else:
            assert starting_desktop and not PYTHON3
            from xpra.x11.desktop_server import XpraDesktopServer
            app = XpraDesktopServer()
        app.init_virtual_devices(devices)

    if proxying or upgrading:
        #when proxying or upgrading, don't exec any plain start commands:
        opts.start = opts.start_child = []
    elif opts.exit_with_children:
        assert opts.start_child, "exit-with-children was specified but start-child is missing!"
    elif opts.start_child:
        log.warn("Warning: the 'start-child' option is used,")
        log.warn(" but 'exit-with-children' is not enabled,")
        log.warn(" use 'start' instead")

    try:
        app._ssl_wrap_socket = wrap_socket_fn
        app.original_desktop_display = desktop_display
        app.exec_cwd = opts.chdir or cwd
        app.init(opts)
        app.setup()
    except InitException as e:
        log.error("xpra server initialization error:")
        log.error(" %s", e)
        return 1
    except Exception as e:
        log.error("Error: cannot start the %s server",
                  app.session_type,
                  exc_info=True)
        log.error(str(e))
        log.info("")
        return 1

    #publish mdns records:
    if opts.mdns:
        from xpra.platform.info import get_username
        from xpra.server.socket_util import mdns_publish
        mdns_info = {
            "display": display_name,
            "username": get_username(),
            "uuid": app.uuid,
            "platform": sys.platform,
            "type": app.session_type,
        }
        MDNS_EXPOSE_NAME = envbool("XPRA_MDNS_EXPOSE_NAME", True)
        if MDNS_EXPOSE_NAME and app.session_name:
            mdns_info["name"] = app.session_name
        for mode, listen_on in mdns_recs.items():
            mdns_publish(display_name, mode, listen_on, mdns_info)

    del opts

    log("%s(%s)", app.init_sockets, sockets)
    app.init_sockets(sockets)
    log("%s(%s)", app.init_when_ready, _when_ready)
    app.init_when_ready(_when_ready)

    try:
        #from here on, we own the vfb, even if we inherited one:
        if (starting or starting_desktop or upgrading) and clobber:
            #and it will be killed if exit cleanly:
            xvfb_pid = get_xvfb_pid()

        log("running %s", app.run)
        r = app.run()
        log("%s()=%s", app.run, r)
    except KeyboardInterrupt:
        log.info("stopping on KeyboardInterrupt")
        r = 0
    except Exception:
        log.error("server error", exc_info=True)
        r = -128
    if r > 0:
        # Upgrading/exiting, so leave X and dbus servers running
        if kill_display:
            _cleanups.remove(kill_display)
        if kill_dbus:
            _cleanups.remove(kill_dbus)
        from xpra.server import EXITING_CODE
        if r == EXITING_CODE:
            log.info("exiting: not cleaning up Xvfb")
        else:
            log.info("upgrading: not cleaning up Xvfb")
        log("cleanups=%s", _cleanups)
        r = 0
    try:
        return r
    finally:
        run_cleanups()
        import gc
        gc.collect()
Beispiel #31
0
 def test_password(self):
     password = strtobytes(uuid.uuid4().hex)
     self._test_hmac_auth("password", password, value=password)
Beispiel #32
0
def _get_cipher(key, iv):
    global backend
    from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
    return Cipher(algorithms.AES(key),
                  modes.CBC(strtobytes(iv)),
                  backend=backend)
Beispiel #33
0
 def get(key, default=None):
     return capabilities.get(strtobytes(key), default)
Beispiel #34
0
 def enc(x):
     try:
         return x.encode("utf8")
     except:
         return strtobytes(x)
Beispiel #35
0
    def start_win32_shadow(self, username, password, new_session_dict):
        log("start_win32_shadow%s", (username, "..", new_session_dict))
        from xpra.platform.win32.wtsapi import find_session
        session_info = find_session(username)
        if not session_info:
            #first, Logon:
            try:
                from xpra.platform.win32.desktoplogon_lib import Logon
                Logon(strtobytes(username), strtobytes(password))
            except Exception:
                log.error("Error: failed to logon as '%s'",
                          username,
                          exc_info=True)
        #hwinstaold = set_window_station("winsta0")
        app_dir = get_app_dir()
        shadow_command = os.path.join(app_dir, "Xpra-Shadow.exe")
        paexec = os.path.join(app_dir, "paexec.exe")
        named_pipe = username.replace(" ", "_")
        cmd = []
        exe = shadow_command

        #use paexec to access the GUI session:
        if envbool("XPRA_PAEXEC",
                   True) and os.path.exists(paexec) and os.path.isfile(paexec):
            #find the session-id to shadow:
            if not session_info:
                session_info = find_session(username)
            if session_info:
                cmd = [
                    "paexec.exe",
                    "-i",
                    str(session_info["SessionID"]),
                    "-s",
                ]
                exe = paexec
            else:
                log.warn("Warning: session not found for username '%s'",
                         username)
        else:
            log.warn("Warning: starting without paexec, expect a black screen")

        cmd += [
            shadow_command,
            "--bind=%s" % named_pipe,
            #"--tray=no",
        ]
        #unless explicitly stated otherwise, exit with client:
        if new_session_dict.get("exit-with-client", None) is not False:
            cmd.append("--exit-with-client=yes")
        from xpra.log import debug_enabled_categories
        if debug_enabled_categories:
            cmd += ["-d", ",".join(tuple(debug_enabled_categories))]
        env = self.get_proxy_env()
        env["XPRA_REDIRECT_OUTPUT"] = "1"
        #env["XPRA_LOG_FILENAME"] = "E:\\Shadow-Instance.log"
        proc = exec_command(username, cmd, exe, app_dir, env)
        from xpra.platform.win32.dotxpra import DotXpra
        dotxpra = DotXpra()
        for t in range(10):
            r = pollwait(proc, 1)
            if r is not None:
                log("pollwait=%s", r)
                try:
                    log("stdout=%s", proc.stdout.read())
                    log("stderr=%s", proc.stderr.read())
                except (OSError, AttributeError):
                    log("failed to read stdout / stderr of subprocess",
                        exc_info=True)
                if r != 0:
                    raise Exception(
                        "shadow subprocess failed with exit code %s" % r)
                else:
                    raise Exception("shadow subprocess has already terminated")
            if t >= 4:
                state = dotxpra.get_display_state(named_pipe)
                log("get_display_state(%s)=%s", state)
                if state == DotXpra.LIVE:
                    #TODO: test the named pipe
                    sleep(2)
                    break
        self.child_reaper.add_process(proc, "server-%s" % username,
                                      "xpra shadow", True, True)
        return proc, "named-pipe://%s" % named_pipe, named_pipe
Beispiel #36
0
 def _process_send_file(self, packet):
     #the remote end is sending us a file
     basefilename, mimetype, printit, openit, filesize, file_data, options = packet[1:8]
     send_id = ""
     if len(packet)>=9:
         send_id = packet[8]
     if not self.accept_data(send_id, b"file", basefilename, printit, openit):
         filelog.warn("Warning: file transfer rejected for file '%s'", bytestostr(basefilename))
         return
     options = typedict(options)
     if printit:
         l = printlog
         assert self.printing
     else:
         l = filelog
         assert self.file_transfer
     l("receiving file: %s",
       [basefilename, mimetype, printit, openit, filesize, "%s bytes" % len(file_data), options])
     assert filesize>0, "invalid file size: %s" % filesize
     if filesize>self.file_size_limit*1024*1024:
         l.error("Error: file '%s' is too large:", basefilename)
         l.error(" %iMB, the file size limit is %iMB", filesize//1024//1024, self.file_size_limit)
         return
     #basefilename should be utf8:
     try:
         base = basefilename.decode("utf8")
     except:
         base = bytestostr(basefilename)
     filename, fd = safe_open_download_file(base, mimetype)
     self.file_descriptors.add(fd)
     chunk_id = options.strget("file-chunk-id")
     if chunk_id:
         chunk_id = strtobytes(chunk_id)
         if len(self.receive_chunks_in_progress)>=MAX_CONCURRENT_FILES:
             self.send("ack-file-chunk", chunk_id, False, "too many file transfers in progress", 0)
             os.close(fd)
             return
         digest = hashlib.sha1()
         chunk = 0
         timer = self.timeout_add(CHUNK_TIMEOUT, self._check_chunk_receiving, chunk_id, chunk)
         chunk_state = [
             monotonic_time(),
             fd, filename, mimetype,
             printit, openit, filesize,
             options, digest, 0, timer, chunk,
             ]
         self.receive_chunks_in_progress[chunk_id] = chunk_state
         self.send("ack-file-chunk", chunk_id, True, "", chunk)
         return
     #not chunked, full file:
     assert file_data, "no data, got %s" % (file_data,)
     if len(file_data)!=filesize:
         l.error("Error: invalid data size for file '%s'", basefilename)
         l.error(" received %i bytes, expected %i bytes", len(file_data), filesize)
         return
     #check digest if present:
     def check_digest(algo="sha1", libfn=hashlib.sha1):
         digest = options.get(algo)
         if digest:
             u = libfn()
             u.update(file_data)
             l("%s digest: %s - expected: %s", algo, u.hexdigest(), digest)
             self.check_digest(basefilename, u.hexdigest(), digest, algo)
     check_digest("sha1", hashlib.sha1)
     check_digest("md5", hashlib.md5)
     try:
         os.write(fd, file_data)
     finally:
         os.close(fd)
     self.do_process_downloaded_file(filename, mimetype, printit, openit, filesize, options)
Beispiel #37
0
def get_cursor_data(hCursor):
    #w, h = get_fixed_cursor_size()
    if not hCursor:
        return None
    x, y = 0, 0
    dc = None
    memdc = None
    bitmap = None
    old_handle = None
    pixels = None
    try:
        ii = ICONINFO()
        ii.cbSize = ctypes.sizeof(ICONINFO)
        if not GetIconInfo(hCursor, ctypes.byref(ii)):
            raise WindowsError()  #@UndefinedVariable
        x = ii.xHotspot
        y = ii.yHotspot
        cursorlog(
            "get_cursor_data(%#x) hotspot at %ix%i, hbmColor=%#x, hbmMask=%#x",
            hCursor, x, y, ii.hbmColor or 0, ii.hbmMask or 0)
        if not ii.hbmColor:
            #FIXME: we don't handle black and white cursors
            return None
        iie = ICONINFOEXW()
        iie.cbSize = ctypes.sizeof(ICONINFOEXW)
        if not GetIconInfoExW(hCursor, ctypes.byref(iie)):
            raise WindowsError()  #@UndefinedVariable
        name = iie.szResName[:MAX_PATH]
        cursorlog("wResID=%i, sxModName=%s, szResName=%s", iie.wResID,
                  iie.sxModName[:MAX_PATH], name)
        bm = Bitmap()
        if not GetObjectA(ii.hbmColor, ctypes.sizeof(Bitmap),
                          ctypes.byref(bm)):
            raise WindowsError()  #@UndefinedVariable
        cursorlog(
            "cursor bitmap: type=%i, width=%i, height=%i, width bytes=%i, planes=%i, bits pixel=%i, bits=%#x",
            bm.bmType, bm.bmWidth, bm.bmHeight, bm.bmWidthBytes, bm.bmPlanes,
            bm.bmBitsPixel, bm.bmBits or 0)
        w = bm.bmWidth
        h = bm.bmHeight
        dc = GetDC(None)
        assert dc, "failed to get a drawing context"
        memdc = CreateCompatibleDC(dc)
        assert memdc, "failed to get a compatible drawing context from %s" % dc
        bitmap = CreateCompatibleBitmap(dc, w, h)
        assert bitmap, "failed to get a compatible bitmap from %s" % dc
        old_handle = SelectObject(memdc, bitmap)

        #check if icon is animated:
        UINT_MAX = 2**32 - 1
        if not DrawIconEx(memdc, 0, 0, hCursor, w, h, UINT_MAX, 0, 0):
            cursorlog("cursor is animated!")

        #if not DrawIcon(memdc, 0, 0, hCursor):
        if not DrawIconEx(memdc, 0, 0, hCursor, w, h, 0, 0,
                          win32con.DI_NORMAL):
            raise WindowsError()  #@UndefinedVariable

        buf_size = bm.bmWidthBytes * h
        buf = ctypes.create_string_buffer(b"", buf_size)
        r = GetBitmapBits(bitmap, buf_size, ctypes.byref(buf))
        cursorlog("get_cursor_data(%#x) GetBitmapBits(%#x, %#x, %#x)=%i",
                  hCursor, bitmap, buf_size, ctypes.addressof(buf), r)
        if r == 0:
            cursorlog.error("Error: failed to copy screen bitmap data")
            return None
        elif r != buf_size:
            cursorlog.warn(
                "Warning: invalid cursor buffer size, got %i bytes but expected %i",
                r, buf_size)
            return None
        else:
            #32-bit data:
            pixels = bytearray(strtobytes(buf.raw))
            has_alpha = False
            has_pixels = False
            for i in range(len(pixels) // 4):
                has_pixels = has_pixels or pixels[i * 4] != 0 or pixels[
                    i * 4 + 1] != 0 or pixels[i * 4 + 2] != 0
                has_alpha = has_alpha or pixels[i * 4 + 3] != 0
                if has_pixels and has_alpha:
                    break
            if has_pixels and not has_alpha:
                #generate missing alpha - don't ask me why
                for i in range(len(pixels) // 4):
                    if pixels[i * 4] != 0 or pixels[i * 4 +
                                                    1] != 0 or pixels[i * 4 +
                                                                      2] != 0:
                        pixels[i * 4 + 3] = 0xff
        return [0, 0, w, h, x, y, hCursor, bytes(pixels), strtobytes(name)]
    except Exception as e:
        cursorlog("get_cursor_data(%#x)", hCursor, exc_info=True)
        cursorlog.error("Error: failed to grab cursor:")
        cursorlog.error(" %s", str(e) or type(e))
        return None
    finally:
        if old_handle:
            SelectObject(memdc, old_handle)
        if bitmap:
            DeleteObject(bitmap)
        if memdc:
            DeleteDC(memdc)
        if dc:
            ReleaseDC(None, dc)
Beispiel #38
0
 def bytesget(self, k, default=None):
     v = self.capsget(k, default)
     if v is None:
         return default
     from xpra.os_util import strtobytes
     return strtobytes(v)
Beispiel #39
0
def hexstr(v):
    return binascii.hexlify(strtobytes(v))
Beispiel #40
0
 def get_password(self) -> str:
     file_data = self.load_password_file()
     if file_data is None:
         return None
     return strtobytes(file_data)
Beispiel #41
0
def make_websocket_accept_hash(key):
    GUID = b"258EAFA5-E914-47DA-95CA-C5AB0DC85B11"
    accept = sha1(strtobytes(key) + GUID).digest()
    return b64encode(accept)
Beispiel #42
0
def do_run_server(error_cb, opts, mode, xpra_file, extra_args, desktop_display=None):
    assert mode in (
        "start", "start-desktop",
        "upgrade", "upgrade-desktop",
        "shadow", "proxy",
        )

    try:
        cwd = os.getcwd()
    except OSError:
        cwd = os.path.expanduser("~")
        warn("current working directory does not exist, using '%s'\n" % cwd)
    validate_encryption(opts)
    if opts.encoding=="help" or "help" in opts.encodings:
        return show_encoding_help(opts)

    #remove anything pointing to dbus from the current env
    #(so we only detect a dbus instance started by pam,
    # and override everything else)
    for k in tuple(os.environ.keys()):
        if k.startswith("DBUS_"):
            del os.environ[k]

    starting  = mode == "start"
    starting_desktop = mode == "start-desktop"
    upgrading = mode == "upgrade"
    upgrading_desktop = mode == "upgrade-desktop"
    shadowing = mode == "shadow"
    proxying  = mode == "proxy"
    clobber   = upgrading or upgrading_desktop or opts.use_display
    start_vfb = not (shadowing or proxying or clobber)

    if not proxying and PYTHON3 and POSIX and not OSX:
        #we don't support wayland servers,
        #so make sure GDK will use the X11 backend:
        os.environ["GDK_BACKEND"] = "x11"

    if proxying or upgrading or upgrading_desktop:
        #when proxying or upgrading, don't exec any plain start commands:
        opts.start = opts.start_child = []
    elif opts.exit_with_children:
        assert opts.start_child, "exit-with-children was specified but start-child is missing!"
    elif opts.start_child:
        warn("Warning: the 'start-child' option is used,")
        warn(" but 'exit-with-children' is not enabled,")
        warn(" use 'start' instead")

    if opts.bind_rfb and (proxying or starting):
        get_util_logger().warn("Warning: bind-rfb sockets cannot be used with '%s' mode" % mode)
        opts.bind_rfb = ""

    if not shadowing and not starting_desktop:
        opts.rfb_upgrade = 0

    if upgrading or upgrading_desktop or shadowing:
        #there should already be one running
        opts.pulseaudio = False

    #get the display name:
    if shadowing and not extra_args:
        if WIN32 or OSX:
            #just a virtual name for the only display available:
            display_name = ":0"
        else:
            from xpra.scripts.main import guess_X11_display
            dotxpra = DotXpra(opts.socket_dir, opts.socket_dirs)
            display_name = guess_X11_display(dotxpra, desktop_display)
    elif (upgrading or upgrading_desktop) and not extra_args:
        display_name = guess_xpra_display(opts.socket_dir, opts.socket_dirs)
    else:
        if len(extra_args) > 1:
            error_cb("too many extra arguments (%i): only expected a display number" % len(extra_args))
        if len(extra_args) == 1:
            display_name = extra_args[0]
            if not shadowing and not proxying and not opts.use_display:
                display_name_check(display_name)
        else:
            if proxying:
                #find a free display number:
                dotxpra = DotXpra(opts.socket_dir, opts.socket_dirs)
                all_displays = dotxpra.sockets()
                #ie: [("LIVE", ":100"), ("LIVE", ":200"), ...]
                displays = [v[1] for v in all_displays]
                display_name = None
                for x in range(1000, 20000):
                    v = ":%s" % x
                    if v not in displays:
                        display_name = v
                        break
                if not display_name:
                    error_cb("you must specify a free virtual display name to use with the proxy server")
            elif opts.use_display:
                #only use automatic guess for xpra displays and not X11 displays:
                display_name = guess_xpra_display(opts.socket_dir, opts.socket_dirs)
            else:
                # We will try to find one automaticaly
                # Use the temporary magic value 'S' as marker:
                display_name = 'S' + str(os.getpid())

    if not (shadowing or proxying or upgrading or upgrading_desktop) and \
    opts.exit_with_children and not opts.start_child:
        error_cb("--exit-with-children specified without any children to spawn; exiting immediately")

    atexit.register(run_cleanups)

    # Generate the script text now, because os.getcwd() will
    # change if/when we daemonize:
    from xpra.server.server_util import (
        xpra_runner_shell_script,
        write_runner_shell_scripts,
        find_log_dir,
        create_input_devices,
        )
    script = xpra_runner_shell_script(xpra_file, cwd, opts.socket_dir)

    uid = int(opts.uid)
    gid = int(opts.gid)
    username = get_username_for_uid(uid)
    home = get_home_for_uid(uid)
    xauth_data = None
    if start_vfb:
        xauth_data = get_hex_uuid()
    ROOT = POSIX and getuid()==0

    protected_fds = []
    protected_env = {}
    stdout = sys.stdout
    stderr = sys.stderr
    # Daemonize:
    if POSIX and opts.daemon:
        #daemonize will chdir to "/", so try to use an absolute path:
        if opts.password_file:
            opts.password_file = tuple(os.path.abspath(x) for x in opts.password_file)
        from xpra.server.server_util import daemonize
        daemonize()

    displayfd = 0
    if POSIX and opts.displayfd:
        try:
            displayfd = int(opts.displayfd)
            if displayfd>0:
                protected_fds.append(displayfd)
        except ValueError as e:
            stderr.write("Error: invalid displayfd '%s':\n" % opts.displayfd)
            stderr.write(" %s\n" % e)
            del e

    # if pam is present, try to create a new session:
    pam = None
    PAM_OPEN = POSIX and envbool("XPRA_PAM_OPEN", ROOT and uid!=0)
    if PAM_OPEN:
        try:
            from xpra.server.pam import pam_session #@UnresolvedImport
        except ImportError as e:
            stderr.write("Error: failed to import pam module\n")
            stderr.write(" %s" % e)
            del e
            PAM_OPEN = False
    if PAM_OPEN:
        fdc = FDChangeCaptureContext()
        with fdc:
            pam = pam_session(username)
            env = {
                   #"XDG_SEAT"               : "seat1",
                   #"XDG_VTNR"               : "0",
                   "XDG_SESSION_TYPE"       : "x11",
                   #"XDG_SESSION_CLASS"      : "user",
                   "XDG_SESSION_DESKTOP"    : "xpra",
                   }
            #maybe we should just bail out instead?
            if pam.start():
                pam.set_env(env)
                items = {}
                if display_name.startswith(":"):
                    items["XDISPLAY"] = display_name
                if xauth_data:
                    items["XAUTHDATA"] = xauth_data
                pam.set_items(items)
                if pam.open():
                    #we can't close it, because we're not going to be root any more,
                    #but since we're the process leader for the session,
                    #terminating will also close the session
                    #add_cleanup(pam.close)
                    protected_env = pam.get_envlist()
                    os.environ.update(protected_env)
        #closing the pam fd causes the session to be closed,
        #and we don't want that!
        protected_fds += fdc.get_new_fds()

    #get XDG_RUNTIME_DIR from env options,
    #which may not be have updated os.environ yet when running as root with "--uid="
    xrd = os.path.abspath(parse_env(opts.env).get("XDG_RUNTIME_DIR", ""))
    if ROOT and (uid>0 or gid>0):
        #we're going to chown the directory if we create it,
        #ensure this cannot be abused, only use "safe" paths:
        if not any(x for x in ("/run/user/%i" % uid, "/tmp", "/var/tmp") if xrd.startswith(x)):
            xrd = ""
        #these paths could cause problems if we were to create and chown them:
        if xrd.startswith("/tmp/.X11-unix") or xrd.startswith("/tmp/.XIM-unix"):
            xrd = ""
    if not xrd:
        xrd = os.environ.get("XDG_RUNTIME_DIR")
    xrd = create_runtime_dir(xrd, uid, gid)
    if xrd:
        #this may override the value we get from pam
        #with the value supplied by the user:
        protected_env["XDG_RUNTIME_DIR"] = xrd

    if POSIX and not ROOT:
        # Write out a shell-script so that we can start our proxy in a clean
        # environment:
        write_runner_shell_scripts(script)

    if start_vfb or opts.daemon:
        #we will probably need a log dir
        #either for the vfb, or for our own log file
        log_dir = opts.log_dir or ""
        if not log_dir or log_dir.lower()=="auto":
            log_dir = find_log_dir(username, uid=uid, gid=gid)
            if not log_dir:
                raise InitException("cannot find or create a logging directory")
        #expose the log-dir as "XPRA_LOG_DIR",
        #this is used by Xdummy for the Xorg log file
        if "XPRA_LOG_DIR" not in os.environ:
            os.environ["XPRA_LOG_DIR"] = log_dir

        if opts.daemon:
            from xpra.server.server_util import select_log_file, open_log_file, redirect_std_to_log
            log_filename0 = osexpand(select_log_file(log_dir, opts.log_file, display_name), username, uid, gid)
            logfd = open_log_file(log_filename0)
            if ROOT and (uid>0 or gid>0):
                try:
                    os.fchown(logfd, uid, gid)
                except (OSError, IOError):
                    pass
            stdout, stderr = redirect_std_to_log(logfd, *protected_fds)
            try:
                stderr.write("Entering daemon mode; "
                         + "any further errors will be reported to:\n"
                         + ("  %s\n" % log_filename0))
            except IOError:
                #we tried our best, logging another error won't help
                pass

    #warn early about this:
    if (starting or starting_desktop) and desktop_display and opts.notifications and not opts.dbus_launch:
        print_DE_warnings()

    from xpra.net.socket_util import get_network_logger, setup_local_sockets, create_sockets
    sockets = create_sockets(opts, error_cb)

    sanitize_env()
    if POSIX:
        if xrd:
            os.environ["XDG_RUNTIME_DIR"] = xrd
        if not OSX:
            os.environ["XDG_SESSION_TYPE"] = "x11"
        if not starting_desktop:
            os.environ["XDG_CURRENT_DESKTOP"] = opts.wm_name
        configure_imsettings_env(opts.input_method)
    if display_name[0] != 'S':
        os.environ["DISPLAY"] = display_name
        if POSIX:
            os.environ["CKCON_X11_DISPLAY"] = display_name
    else:
        try:
            del os.environ["DISPLAY"]
        except KeyError:
            pass
    os.environ.update(protected_env)
    from xpra.log import Logger
    log = Logger("server")
    log("env=%s", os.environ)

    UINPUT_UUID_LEN = 12
    UINPUT_UUID_MIN_LEN = 12
    UINPUT_UUID_MAX_LEN = 32
    # Start the Xvfb server first to get the display_name if needed
    odisplay_name = display_name
    xvfb = None
    xvfb_pid = None
    uinput_uuid = None
    if start_vfb:
        assert not proxying and xauth_data
        pixel_depth = validate_pixel_depth(opts.pixel_depth, starting_desktop)
        from xpra.x11.vfb_util import start_Xvfb, check_xvfb_process
        from xpra.server.server_util import has_uinput
        uinput_uuid = None
        if has_uinput() and opts.input_devices.lower() in ("uinput", "auto") and not shadowing:
            from xpra.os_util import get_rand_chars
            uinput_uuid = get_rand_chars(UINPUT_UUID_LEN)
        xvfb, display_name, cleanups = start_Xvfb(opts.xvfb, pixel_depth, display_name, cwd,
                                                  uid, gid, username, xauth_data, uinput_uuid)
        for f in cleanups:
            add_cleanup(f)
        xvfb_pid = xvfb.pid
        #always update as we may now have the "real" display name:
        os.environ["DISPLAY"] = display_name
        os.environ["CKCON_X11_DISPLAY"] = display_name
        os.environ.update(protected_env)
        if display_name!=odisplay_name and pam:
            pam.set_items({"XDISPLAY" : display_name})

        def check_xvfb():
            return check_xvfb_process(xvfb)
    else:
        if POSIX and clobber:
            #if we're meant to be using a private XAUTHORITY file,
            #make sure to point to it:
            from xpra.x11.vfb_util import get_xauthority_path
            xauthority = get_xauthority_path(display_name, username, uid, gid)
            if os.path.exists(xauthority):
                log("found XAUTHORITY=%s", xauthority)
                os.environ["XAUTHORITY"] = xauthority
        def check_xvfb():
            return True

    if POSIX and not OSX and displayfd>0:
        from xpra.platform.displayfd import write_displayfd
        try:
            display_no = display_name[1:]
            #ensure it is a string containing the number:
            display_no = str(int(display_no))
            log("writing display_no='%s' to displayfd=%i", display_no, displayfd)
            assert write_displayfd(displayfd, display_no), "timeout"
        except Exception as e:
            log.error("write_displayfd failed", exc_info=True)
            log.error("Error: failed to write '%s' to fd=%s", display_name, displayfd)
            log.error(" %s", str(e) or type(e))
            del e

    if opts.daemon:
        def noerr(fn, *args):
            try:
                fn(*args)
            except Exception:
                pass
        log_filename1 = osexpand(select_log_file(log_dir, opts.log_file, display_name), username, uid, gid)
        if log_filename0 != log_filename1:
            # we now have the correct log filename, so use it:
            os.rename(log_filename0, log_filename1)
            if odisplay_name!=display_name:
                #this may be used by scripts, let's try not to change it:
                noerr(stderr.write, "Actual display used: %s\n" % display_name)
            noerr(stderr.write, "Actual log file name is now: %s\n" % log_filename1)
            noerr(stderr.flush)
        noerr(stdout.close)
        noerr(stderr.close)
    #we should not be using stdout or stderr from this point:
    del stdout
    del stderr

    if not check_xvfb():
        #xvfb problem: exit now
        return  1

    #create devices for vfb if needed:
    devices = {}
    if not start_vfb and not proxying and not shadowing and envbool("XPRA_UINPUT", True):
        #try to find the existing uinput uuid:
        #use a subprocess to avoid polluting our current process
        #with X11 connections before we get a chance to change uid
        prop = "_XPRA_UINPUT_ID"
        cmd = ["xprop", "-display", display_name, "-root", prop]
        log("looking for '%s' on display '%s' with XAUTHORITY='%s'", prop, display_name, os.environ.get("XAUTHORITY"))
        try:
            code, out, err = get_status_output(cmd)
        except Exception as e:
            log("failed to get existing uinput id: %s", e)
            del e
        else:
            log("Popen(%s)=%s", cmd, (code, out, err))
            if code==0 and out.find("=")>0:
                uinput_uuid = out.split("=", 1)[1]
                log("raw uinput uuid=%s", uinput_uuid)
                uinput_uuid = strtobytes(uinput_uuid.strip('\n\r"\\ '))
                if uinput_uuid:
                    if len(uinput_uuid)>UINPUT_UUID_MAX_LEN or len(uinput_uuid)<UINPUT_UUID_MIN_LEN:
                        log.warn("Warning: ignoring invalid uinput id:")
                        log.warn(" '%s'", uinput_uuid)
                        uinput_uuid = None
                    else:
                        log.info("retrieved existing uinput id: %s", bytestostr(uinput_uuid))
    if uinput_uuid:
        devices = create_input_devices(uinput_uuid, uid)

    if ROOT and (uid!=0 or gid!=0):
        log("root: switching to uid=%i, gid=%i", uid, gid)
        setuidgid(uid, gid)
        os.environ.update({
            "HOME"      : home,
            "USER"      : username,
            "LOGNAME"   : username,
            })
        shell = get_shell_for_uid(uid)
        if shell:
            os.environ["SHELL"] = shell
        #now we've changed uid, it is safe to honour all the env updates:
        configure_env(opts.env)
        os.environ.update(protected_env)

    if opts.chdir:
        log("chdir(%s)", opts.chdir)
        os.chdir(opts.chdir)

    dbus_pid, dbus_env = 0, {}
    if not shadowing and POSIX and not OSX and not clobber:
        no_gtk()
        assert starting or starting_desktop or proxying
        from xpra.server.dbus.dbus_start import start_dbus
        dbus_pid, dbus_env = start_dbus(opts.dbus_launch)
        if dbus_env:
            os.environ.update(dbus_env)

    display = None
    if not proxying:
        if POSIX and not OSX:
            no_gtk()
            if starting or starting_desktop or shadowing:
                #check that we can access the X11 display:
                from xpra.x11.vfb_util import verify_display_ready
                if not verify_display_ready(xvfb, display_name, shadowing):
                    return 1
                log("X11 display is ready")
                no_gtk()
                from xpra.x11.gtk_x11.gdk_display_source import verify_gdk_display
                display = verify_gdk_display(display_name)
                if not display:
                    return 1
                log("GDK can access the display")
        #on win32, this ensures that we get the correct screen size to shadow:
        from xpra.platform.gui import init as gui_init
        log("gui_init()")
        gui_init()

    #setup unix domain socket:
    netlog = get_network_logger()
    local_sockets = setup_local_sockets(opts.bind,
                                        opts.socket_dir, opts.socket_dirs,
                                        display_name, clobber,
                                        opts.mmap_group, opts.socket_permissions,
                                        username, uid, gid)
    netlog("setting up local sockets: %s", local_sockets)
    sockets += local_sockets
    if POSIX and (starting or upgrading or starting_desktop or upgrading_desktop):
        #all unix domain sockets:
        ud_paths = [sockpath for stype, _, sockpath, _ in local_sockets if stype=="unix-domain"]
        if ud_paths:
            #choose one so our xdg-open override script can use to talk back to us:
            if opts.forward_xdg_open:
                for x in ("/usr/libexec/xpra", "/usr/lib/xpra"):
                    xdg_override = os.path.join(x, "xdg-open")
                    if os.path.exists(xdg_override):
                        os.environ["PATH"] = x+os.pathsep+os.environ.get("PATH", "")
                        os.environ["XPRA_SERVER_SOCKET"] = ud_paths[0]
                        break
        else:
            log.warn("Warning: no local server sockets,")
            if opts.forward_xdg_open:
                log.warn(" forward-xdg-open cannot be enabled")
            log.warn(" non-embedded ssh connections will not be available")

    set_server_features(opts)

    if not proxying and POSIX and not OSX:
        if not check_xvfb():
            return  1
        from xpra.x11.gtk_x11.gdk_display_source import init_gdk_display_source
        if os.environ.get("NO_AT_BRIDGE") is None:
            os.environ["NO_AT_BRIDGE"] = "1"
        init_gdk_display_source()
        #(now we can access the X11 server)
        if uinput_uuid:
            save_uinput_id(uinput_uuid)

    if shadowing:
        app = make_shadow_server()
    elif proxying:
        app = make_proxy_server()
    else:
        if starting or upgrading:
            app = make_server(clobber)
        else:
            assert starting_desktop or upgrading_desktop
            app = make_desktop_server(clobber)
        app.init_virtual_devices(devices)

    try:
        app.exec_cwd = opts.chdir or cwd
        app.display_name = display_name
        app.init(opts)
        app.init_sockets(sockets)
        app.init_dbus(dbus_pid, dbus_env)
        if not shadowing and (xvfb_pid or clobber):
            app.init_display_pid(xvfb_pid)
        app.original_desktop_display = desktop_display
        del opts
        if not app.server_ready():
            return 1
        app.server_init()
        app.setup()
        app.init_when_ready(_when_ready)
    except InitException as e:
        log.error("xpra server initialization error:")
        log.error(" %s", e)
        app.cleanup()
        return 1
    except Exception as e:
        log.error("Error: cannot start the %s server", app.session_type, exc_info=True)
        log.error(str(e))
        log.info("")
        app.cleanup()
        return 1

    try:
        log("running %s", app.run)
        r = app.run()
        log("%s()=%s", app.run, r)
    except KeyboardInterrupt:
        log.info("stopping on KeyboardInterrupt")
        app.cleanup()
        return 0
    except Exception:
        log.error("server error", exc_info=True)
        app.cleanup()
        return -128
    else:
        if r>0:
            r = 0
    return r
Beispiel #43
0
 def get_auth_info(self):
     self.load_password_file()
     if not self.password_filedata:
         return None
     return self.password_filedata.get(strtobytes(self.username))
Beispiel #44
0
def hexstr(v):
    return binascii.hexlify(strtobytes(v))
Beispiel #45
0
 def enc(x):
     try:
         return bytestostr(x).encode("utf8")
     except UnicodeEncodeError:
         return strtobytes(x)
Beispiel #46
0
 def b(s):
     try:
         return s.encode("utf8")
     except:
         return strtobytes(s)
Beispiel #47
0
 def _parse_security_result(self, packet):
     self.share  = packet != b"\0"
     log("parse_security_result: sharing=%s, sending ClientInit with session-name=%s", self.share, self.session_name)
     #send ClientInit
     self._packet_parser = self._parse_rfb
     w, h, bpp, depth, bigendian, truecolor, rmax, gmax, bmax, rshift, bshift, gshift = self._get_rfb_pixelformat()
     packet =  struct.pack(b"!HH"+PIXEL_FORMAT+b"I", w, h, bpp, depth, bigendian, truecolor, rmax, gmax, bmax, rshift, bshift, gshift, 0, 0, 0, len(self.session_name))+strtobytes(self.session_name)
     self.send(packet)
     self._process_packet_cb(self, [b"authenticated"])
     return 1
Beispiel #48
0
 def _bell_signaled(self, wm, event):
     log("bell signaled on window %#x", get_xwindow(event.window))
     if not self.bell:
         return
     wid = 0
     if event.window!=get_default_root_window() and event.window_model is not None:
         try:
             wid = self._window_to_id[event.window_model]
         except:
             pass
     log("_bell_signaled(%s,%r) wid=%s", wm, event, wid)
     for ss in self._server_sources.values():
         ss.bell(wid, event.device, event.percent, event.pitch, event.duration, event.bell_class, event.bell_id, strtobytes(event.bell_name or ""))
Beispiel #49
0
    def do_update_server_settings(self,
                                  settings,
                                  reset=False,
                                  dpi=0,
                                  double_click_time=0,
                                  double_click_distance=(-1, -1),
                                  antialias={},
                                  cursor_size=-1):
        if not self._xsettings_enabled:
            log("ignoring xsettings update: %s", settings)
            return
        if reset:
            #FIXME: preserve serial? (what happens when we change values which had the same serial?)
            self.reset_settings()
            self._settings = {}
            if self._default_xsettings:
                #try to parse default xsettings into a dict:
                try:
                    for _, prop_name, value, _ in self._default_xsettings[1]:
                        self._settings[prop_name] = value
                except Exception as e:
                    log("failed to parse %s", self._default_xsettings)
                    log.warn("Warning: failed to parse default XSettings:")
                    log.warn(" %s", e)
        old_settings = dict(self._settings)
        log("server_settings: old=%s, updating with=%s", nonl(old_settings),
            nonl(settings))
        log(
            "overrides: dpi=%s, double click time=%s, double click distance=%s",
            dpi, double_click_time, double_click_distance)
        log("overrides: antialias=%s", antialias)
        self._settings.update(settings)
        from xpra.x11.xsettings_prop import XSettingsTypeInteger, XSettingsTypeString, BLACKLISTED_XSETTINGS
        for k, v in settings.items():
            #cook the "resource-manager" value to add the DPI and/or antialias values:
            if k == b"resource-manager" and (dpi > 0 or antialias
                                             or cursor_size > 0):
                value = bytestostr(v)
                #parse the resources into a dict:
                values = {}
                options = value.split("\n")
                for option in options:
                    if not option:
                        continue
                    parts = option.split(":\t", 1)
                    if len(parts) != 2:
                        log("skipped invalid option: '%s'", option)
                        continue
                    if parts[0] in BLACKLISTED_XSETTINGS:
                        log("skipped blacklisted option: '%s'", option)
                        continue
                    values[parts[0]] = parts[1]
                if cursor_size > 0:
                    values["Xcursor.size"] = cursor_size
                if dpi > 0:
                    values["Xft.dpi"] = dpi
                    values["Xft/DPI"] = dpi * 1024
                    values["gnome.Xft/DPI"] = dpi * 1024
                if antialias:
                    ad = typedict(antialias)
                    subpixel_order = "none"
                    sss = tuple(self._server_sources.values())
                    if len(sss) == 1:
                        #only honour sub-pixel hinting if a single client is connected
                        #and only when it is not using any scaling (or overriden with SCALED_FONT_ANTIALIAS):
                        ss = sss[0]
                        ds_unscaled = getattr(ss, "desktop_size_unscaled",
                                              None)
                        ds_scaled = getattr(ss, "desktop_size", None)
                        if SCALED_FONT_ANTIALIAS or (not ds_unscaled or
                                                     ds_unscaled == ds_scaled):
                            subpixel_order = ad.strget("orientation",
                                                       "none").lower()
                    values.update({
                        "Xft.antialias": ad.intget("enabled", -1),
                        "Xft.hinting": ad.intget("hinting", -1),
                        "Xft.rgba": subpixel_order,
                        "Xft.hintstyle": _get_antialias_hintstyle(ad)
                    })
                log("server_settings: resource-manager values=%s",
                    nonl(values))
                #convert the dict back into a resource string:
                value = ''
                for vk, vv in values.items():
                    value += "%s:\t%s\n" % (vk, vv)
                #record the actual value used
                self._settings[b"resource-manager"] = value
                v = value.encode("utf-8")

            #cook xsettings to add various settings:
            #(as those may not be present in xsettings on some platforms.. like win32 and osx)
            if k==b"xsettings-blob" and \
            (self.double_click_time>0 or self.double_click_distance!=(-1, -1) or antialias or dpi>0):
                #start by removing blacklisted options:
                def filter_blacklisted():
                    serial, values = v
                    new_values = []
                    for _t, _n, _v, _s in values:
                        if bytestostr(_n) in BLACKLISTED_XSETTINGS:
                            log("skipped blacklisted option %s",
                                (_t, _n, _v, _s))
                        else:
                            new_values.append((_t, _n, _v, _s))
                    return serial, new_values

                v = filter_blacklisted()

                def set_xsettings_value(name, value_type, value):
                    #remove existing one, if any:
                    serial, values = v
                    new_values = [(_t, _n, _v, _s)
                                  for (_t, _n, _v, _s) in values if _n != name]
                    new_values.append((value_type, name, value, 0))
                    return serial, new_values

                def set_xsettings_int(name, value):
                    if value < 0:  #not set, return v unchanged
                        return v
                    return set_xsettings_value(name, XSettingsTypeInteger,
                                               value)

                if dpi > 0:
                    v = set_xsettings_int(b"Xft/DPI", dpi * 1024)
                if double_click_time > 0:
                    v = set_xsettings_int(b"Net/DoubleClickTime",
                                          self.double_click_time)
                if antialias:
                    ad = typedict(antialias)
                    v = set_xsettings_int(b"Xft/Antialias",
                                          ad.intget("enabled", -1))
                    v = set_xsettings_int(b"Xft/Hinting",
                                          ad.intget("hinting", -1))
                    v = set_xsettings_value(
                        b"Xft/RGBA", XSettingsTypeString,
                        ad.strget("orientation", "none").lower())
                    v = set_xsettings_value(b"Xft/HintStyle",
                                            XSettingsTypeString,
                                            _get_antialias_hintstyle(ad))
                if double_click_distance != (-1, -1):
                    #some platforms give us a value for each axis,
                    #but X11 only has one, so take the average
                    try:
                        x, y = double_click_distance
                        if x > 0 and y > 0:
                            d = iround((x + y) / 2.0)
                            d = max(1, min(128, d))  #sanitize it a bit
                            v = set_xsettings_int(b"Net/DoubleClickDistance",
                                                  d)
                    except Exception as e:
                        log.warn(
                            "error setting double click distance from %s: %s",
                            double_click_distance, e)

            if k not in old_settings or v != old_settings[k]:
                if k == b"xsettings-blob":
                    self.set_xsettings(v)
                elif k == b"resource-manager":
                    from xpra.x11.gtk_x11.prop import prop_set
                    p = "RESOURCE_MANAGER"
                    log("server_settings: setting %s to %s", nonl(p), nonl(v))
                    prop_set(self.root_window, p, "latin1",
                             strtobytes(v).decode("latin1"))
                else:
                    log.warn("Warning: unexpected setting '%s'", bytestostr(k))
Beispiel #50
0
 def capsget(self, capabilities, key, default):
     v = capabilities.get(strtobytes(key), default)
     if PYTHON3 and isinstance(v, bytes):
         v = bytestostr(v)
     return v
Beispiel #51
0
 def get(key, default=None):
     return capabilities.get(strtobytes(key), default)
Beispiel #52
0
 def _test_file_auth(self, mod_name, genauthdata, display_count=0):
     #no file, no go:
     a = self._init_auth(mod_name)
     assert a.requires_challenge()
     p = a.get_passwords()
     assert not p, "got passwords from %s: %s" % (a, p)
     #challenge twice is a fail
     assert a.get_challenge(get_digests())
     assert not a.get_challenge(get_digests())
     assert not a.get_challenge(get_digests())
     #muck:
     # 0 - OK
     # 1 - bad: with warning about newline
     # 2 - verify bad passwords
     # 3 - verify no password
     for muck in (0, 1, 2, 3):
         with TempFileContext(prefix=mod_name) as context:
             f = context.file
             filename = context.filename
             with f:
                 a = self._init_auth(mod_name, filename=filename)
                 password, filedata = genauthdata(a)
                 #print("saving password file data='%s' to '%s'" % (filedata, filename))
                 if muck != 3:
                     f.write(strtobytes(filedata))
                 if muck == 1:
                     f.write(b"\n")
                 f.flush()
                 assert a.requires_challenge()
                 salt, mac = a.get_challenge(get_digests())
                 assert salt
                 assert mac in get_digests()
                 assert mac != "xor"
                 password = strtobytes(password)
                 client_salt = strtobytes(uuid.uuid4().hex +
                                          uuid.uuid4().hex)[:len(salt)]
                 salt_digest = a.choose_salt_digest(get_digests())
                 assert salt_digest
                 auth_salt = strtobytes(
                     gendigest(salt_digest, client_salt, salt))
                 if muck == 0:
                     digestmod = get_digest_module(mac)
                     verify = hmac.HMAC(password,
                                        auth_salt,
                                        digestmod=digestmod).hexdigest()
                     assert self.capsauth(
                         a, verify,
                         client_salt), "%s failed" % a.authenticate
                     if display_count > 0:
                         sessions = a.get_sessions()
                         assert len(sessions) >= 3
                         displays = sessions[2]
                         assert len(
                             displays
                         ) == display_count, "expected %i displays but got %i : %s" % (
                             display_count, len(sessions), sessions)
                     assert not self.capsauth(
                         a, verify, client_salt), "authenticated twice!"
                     passwords = a.get_passwords()
                     assert len(
                         passwords
                     ) == 1, "expected just one password in file, got %i" % len(
                         passwords)
                     assert password in passwords
                 else:
                     for verify in ("whatever", None, "bad"):
                         assert not self.capsauth(a, verify, client_salt)
     return a
Beispiel #53
0
	def test_password(self):
		password = strtobytes(uuid.uuid4().hex)
		self._test_hmac_auth(password_auth, password, value=password)
Beispiel #54
0
 def test_bytes(self):
     assert strtobytes(b"hello") == b"hello"
Beispiel #55
0
def _get_cipher(key, iv):
    global backend
    from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
    return Cipher(algorithms.AES(key), modes.CBC(strtobytes(iv)), backend=backend)
    def parse_server_capabilities(self):
        for c in CLIENT_BASES:
            if not c.parse_server_capabilities(self):
                log.info("failed to parse server capabilities in %s", c)
                return False
        c = self.server_capabilities
        self.server_session_name = strtobytes(c.rawget("session_name",
                                                       b"")).decode("utf-8")
        set_name("Xpra", self.session_name or self.server_session_name
                 or "Xpra")
        self.server_platform = c.strget("platform")
        self.server_sharing = c.boolget("sharing")
        self.server_sharing_toggle = c.boolget("sharing-toggle")
        self.server_lock = c.boolget("lock")
        self.server_lock_toggle = c.boolget("lock-toggle")
        self.server_keyboard = c.boolget("keyboard", True)
        self.server_toggle_keyboard_sync = self.server_keyboard and c.boolget(
            "toggle_keyboard_sync", True)
        self.server_pointer = c.boolget("pointer", True)
        self.server_start_new_commands = c.boolget("start-new-commands")
        if self.server_start_new_commands:
            self.server_xdg_menu = c.dictget("xdg-menu", None)
        if self.start_new_commands or self.start_child_new_commands:
            if self.server_start_new_commands:
                self.after_handshake(self.send_start_new_commands)
            else:
                log.warn("Warning: cannot start new commands")
                log.warn(" the feature is currently disabled on the server")
        self.server_commands_info = c.boolget("server-commands-info")
        self.server_commands_signals = c.strlistget("server-commands-signals")
        self.server_readonly = c.boolget("readonly")
        if self.server_readonly and not self.readonly:
            log.info("server is read only")
            self.readonly = True
        if not self.server_keyboard and self.keyboard_helper:
            #swallow packets:
            def nosend(*_args):
                pass

            self.keyboard_helper.send = nosend

        i = platform_name(
            self._remote_platform,
            c.strlistget("platform.linux_distribution")
            or c.strget("platform.release", ""))
        r = self._remote_version
        if self._remote_revision:
            r += "-r%s" % self._remote_revision
        mode = c.strget("server.mode", "server")
        bits = c.intget("python.bits", 32)
        log.info("Xpra %s server version %s %i-bit", mode, std(r), bits)
        if i:
            log.info(" running on %s", std(i))
        if c.boolget("proxy"):
            proxy_hostname = c.strget("proxy.hostname")
            proxy_platform = c.strget("proxy.platform")
            proxy_release = c.strget("proxy.platform.release")
            proxy_version = c.strget("proxy.version")
            proxy_version = c.strget("proxy.build.version", proxy_version)
            proxy_distro = c.strget("proxy.linux_distribution")
            msg = "via: %s proxy version %s" % (platform_name(
                proxy_platform, proxy_distro
                or proxy_release), std(proxy_version or "unknown"))
            if proxy_hostname:
                msg += " on '%s'" % std(proxy_hostname)
            log.info(msg)
        return True
Beispiel #57
0
 def capsget(self, capabilities, key, default):
     v = capabilities.get(strtobytes(key), default)
     if sys.version >= '3' and type(v)==bytes:
         v = bytestostr(v)
     return v
Beispiel #58
0
 def get_pixbuf_from_data(rgb_data, has_alpha, w, h, rowstride):
     data = array.array('B', strtobytes(rgb_data))
     return GdkPixbuf.Pixbuf.new_from_data(data, GdkPixbuf.Colorspace.RGB,
                                           has_alpha, 8, w, h, rowstride,
                                           None, None)
Beispiel #59
0
from xpra.gtk_common.gobject_util import no_arg_signal, SIGNAL_RUN_LAST
from xpra.gtk_common.gtk_util import GetClipboard, selection_owner_set, selection_add_target, selectiondata_get_selection, selectiondata_get_target, selectiondata_get_data, selectiondata_get_data_type, selectiondata_get_format, selectiondata_set, clipboard_request_contents, set_clipboard_data, PROPERTY_CHANGE_MASK
from xpra.gtk_common.nested_main import NestedMainLoop
from xpra.net.compression import Compressible
from xpra.os_util import WIN32, POSIX, monotonic_time, strtobytes, bytestostr, hexstr, get_hex_uuid, is_X11, is_Wayland
from xpra.util import csv, envint, envbool, repr_ellipsized, typedict, first_time
from xpra.platform.features import CLIPBOARD_GREEDY

MIN_CLIPBOARD_COMPRESSION_SIZE = 512
MAX_CLIPBOARD_PACKET_SIZE = 4 * 1024 * 1024
MAX_CLIPBOARD_RECEIVE_SIZE = envint("XPRA_MAX_CLIPBOARD_RECEIVE_SIZE", -1)
MAX_CLIPBOARD_SEND_SIZE = envint("XPRA_MAX_CLIPBOARD_SEND_SIZE", -1)

from xpra.platform.features import CLIPBOARDS as PLATFORM_CLIPBOARDS

ALL_CLIPBOARDS = [strtobytes(x) for x in PLATFORM_CLIPBOARDS]
CLIPBOARDS = PLATFORM_CLIPBOARDS
CLIPBOARDS_ENV = os.environ.get("XPRA_CLIPBOARDS")
if CLIPBOARDS_ENV is not None:
    CLIPBOARDS = CLIPBOARDS_ENV.split(",")
    CLIPBOARDS = [strtobytes(x).upper().strip() for x in CLIPBOARDS]
del CLIPBOARDS_ENV

TEST_DROP_CLIPBOARD_REQUESTS = envint("XPRA_TEST_DROP_CLIPBOARD")
STORE_ON_EXIT = envbool("XPRA_CLIPBOARD_STORE_ON_EXIT", True)
DELAY_SEND_TOKEN = envint("XPRA_DELAY_SEND_TOKEN", 100)

LOOP_DISABLE = envbool("XPRA_CLIPBOARD_LOOP_DISABLE", True)
LOOP_PREFIX = os.environ.get("XPRA_CLIPBOARD_LOOP_PREFIX",
                             "Xpra-Clipboard-Loop-Detection:")
Beispiel #60
0
    def parse_encoding_caps(self, c):
        self.set_encoding(c.strget("encoding", None), None)
        #encoding options (filter):
        #1: these properties are special cased here because we
        #defined their name before the "encoding." prefix convention,
        #or because we want to pass default values (zlib/lz4):
        for k, ek in {
                "initial_quality": "initial_quality",
                "quality": "quality",
        }.items():
            if k in c:
                self.encoding_options[ek] = c.intget(k)
        for k, ek in {
                "zlib": "rgb_zlib",
                "lz4": "rgb_lz4",
        }.items():
            if k in c:
                self.encoding_options[ek] = c.boolget(k)
        #2: standardized encoding options:
        for k in c.keys():
            #yaml gives us str..
            k = strtobytes(k)
            if k.startswith(b"theme.") or k.startswith(b"encoding.icons."):
                self.icons_encoding_options[k.replace(b"encoding.icons.",
                                                      b"").replace(
                                                          b"theme.",
                                                          b"")] = c.get(k)
            elif k.startswith(b"encoding."):
                stripped_k = k[len(b"encoding."):]
                if stripped_k in (
                        b"transparency",
                        b"rgb_zlib",
                        b"rgb_lz4",
                        b"rgb_lzo",
                ):
                    v = c.boolget(k)
                elif stripped_k in (b"initial_quality", b"initial_speed",
                                    b"min-quality", b"quality", b"min-speed",
                                    b"speed"):
                    v = c.intget(k)
                else:
                    v = c.get(k)
                self.encoding_options[stripped_k] = v
        log("encoding options: %s", self.encoding_options)
        log("icons encoding options: %s", self.icons_encoding_options)

        #handle proxy video: add proxy codec to video helper:
        pv = self.encoding_options.boolget("proxy.video")
        proxylog("proxy.video=%s", pv)
        if pv:
            #enabling video proxy:
            try:
                self.parse_proxy_video()
            except Exception:
                proxylog.error("failed to parse proxy video", exc_info=True)

        sc = self.encoding_options.get("scaling.control", self.scaling_control)
        if sc is not None:
            #"encoding_options" are exposed via "xpra info",
            #so we can't have None values in there (bencoder would choke)
            self.default_encoding_options["scaling.control"] = sc
        q = self.encoding_options.intget("quality",
                                         self.default_quality)  #0.7 onwards:
        if q > 0:
            self.default_encoding_options["quality"] = q
        mq = self.encoding_options.intget("min-quality",
                                          self.default_min_quality)
        if mq > 0 and (q <= 0 or q > mq):
            self.default_encoding_options["min-quality"] = mq
        s = self.encoding_options.intget("speed", self.default_speed)
        if s > 0:
            self.default_encoding_options["speed"] = s
        ms = self.encoding_options.intget("min-speed", self.default_min_speed)
        if ms > 0 and (s <= 0 or s > ms):
            self.default_encoding_options["min-speed"] = ms
        log("default encoding options: %s", self.default_encoding_options)
        self.auto_refresh_delay = c.intget("auto_refresh_delay", 0)

        #are we going to need a cuda context?
        common_encodings = tuple(x for x in self.encodings
                                 if x in self.server_encodings)
        from xpra.codecs.loader import has_codec
        if "jpeg" in common_encodings and has_codec("enc_nvjpeg"):
            from xpra.codecs.cuda_common.cuda_context import get_device_context
            self.cuda_device_context = get_device_context(
                self.encoding_options)
            log.warn("cuda_device_context=%s", self.cuda_device_context)