Hide keyboard shortcuts

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. 

5 

6#authentication from a file containing a list of entries of the form: 

7# username|password|uid|gid|displays|env_options|session_options 

8 

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 

14 

15 

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 

39 

40 

41class Authenticator(FileAuthenticatorBase): 

42 def __init__(self, username, **kwargs): 

43 super().__init__(username, **kwargs) 

44 self.sessions = None 

45 

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 

73 

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)) 

79 

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] 

86 

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 

108 

109 def get_sessions(self): 

110 return self.sessions 

111 

112 def __repr__(self): 

113 return "multi password file"