Coverage for /home/antoine/projects/xpra-git/dist/python3/lib64/python/xpra/server/auth/multifile_auth.py : 100%
Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1# This file is part of Xpra.
2# Copyright (C) 2013-2019 Antoine Martin <antoine@xpra.org>
3# Xpra is released under the terms of the GNU GPL v2, or, at your option, any
4# later version. See the file COPYING for details.
6#authentication from a file containing a list of entries of the form:
7# username|password|uid|gid|displays|env_options|session_options
9from xpra.server.auth.sys_auth_base import parse_uid, parse_gid
10from xpra.server.auth.file_auth_base import log, FileAuthenticatorBase
11from xpra.os_util import strtobytes, bytestostr, hexstr
12from xpra.util import parse_simple_dict
13from xpra.net.digest import verify_digest
16def parse_auth_line(line):
17 ldata = line.split(b"|")
18 assert len(ldata)>=2, "not enough fields: %i" % (len(ldata))
19 log("found %s fields", len(ldata))
20 #parse fields:
21 username = ldata[0]
22 password = ldata[1]
23 if len(ldata)>=5:
24 uid = parse_uid(bytestostr(ldata[2]))
25 gid = parse_gid(bytestostr(ldata[3]))
26 displays = bytestostr(ldata[4]).split(",")
27 else:
28 #this will use the default value, usually "nobody":
29 uid = parse_uid(None)
30 gid = parse_gid(None)
31 displays = []
32 env_options = {}
33 session_options = {}
34 if len(ldata)>=6:
35 env_options = parse_simple_dict(ldata[5], b";")
36 if len(ldata)>=7:
37 session_options = parse_simple_dict(ldata[6], b";")
38 return username, password, uid, gid, displays, env_options, session_options
41class Authenticator(FileAuthenticatorBase):
42 def __init__(self, username, **kwargs):
43 super().__init__(username, **kwargs)
44 self.sessions = None
46 def parse_filedata(self, data):
47 if not data:
48 return {}
49 auth_data = {}
50 i = 0
51 for line in data.splitlines():
52 i += 1
53 line = line.strip()
54 log("line %s: %s", i, line)
55 if not line or line.startswith(b"#"):
56 continue
57 try:
58 v = parse_auth_line(line)
59 if v:
60 username, password, uid, gid, displays, env_options, session_options = v
61 if username in auth_data:
62 log.error("Error: duplicate entry for username '%s' in '%s'", username, self.password_filename)
63 else:
64 auth_data[username] = password, uid, gid, displays, env_options, session_options
65 except Exception as e:
66 log("parsing error", exc_info=True)
67 log.error("Error parsing password file '%s' at line %i:", self.password_filename, i)
68 log.error(" '%s'", bytestostr(line))
69 log.error(" %s", e)
70 continue
71 log("parsed auth data from file %s: %s", self.password_filename, auth_data)
72 return auth_data
74 def get_auth_info(self):
75 self.load_password_file()
76 if not self.password_filedata:
77 return None
78 return self.password_filedata.get(strtobytes(self.username))
80 def get_password(self):
81 entry = self.get_auth_info()
82 log("get_password() found entry=%s", entry)
83 if entry is None:
84 return None
85 return entry[0]
87 def authenticate_hmac(self, challenge_response, client_salt=None) -> bool:
88 log("authenticate_hmac(%r, %r)", challenge_response, client_salt)
89 self.sessions = None
90 if not self.salt:
91 log.error("Error: illegal challenge response received - salt cleared or unset")
92 return None
93 #ensure this salt does not get re-used:
94 salt = self.get_response_salt(client_salt)
95 entry = self.get_auth_info()
96 if entry is None:
97 log.warn("Warning: authentication failed")
98 log.warn(" no password for '%s' in '%s'", self.username, self.password_filename)
99 return None
100 log("authenticate: auth-info(%s)=%s", self.username, entry)
101 fpassword, uid, gid, displays, env_options, session_options = entry
102 log("multifile authenticate_hmac password='%r', hex(salt)=%s", fpassword, hexstr(salt))
103 if not verify_digest(self.digest, fpassword, salt, challenge_response):
104 log.warn("Warning: %s challenge for '%s' does not match", self.digest, self.username)
105 return False
106 self.sessions = uid, gid, displays, env_options, session_options
107 return True
109 def get_sessions(self):
110 return self.sessions
112 def __repr__(self):
113 return "multi password file"