Merge lp:~widelands-dev/widelands/net-pwd-security into lp:widelands
- net-pwd-security
- Merge into trunk
Status: | Merged |
---|---|
Merged at revision: | 8617 |
Proposed branch: | lp:~widelands-dev/widelands/net-pwd-security |
Merge into: | lp:widelands |
Diff against target: |
483 lines (+168/-70) 7 files modified
src/network/internet_gaming.cc (+89/-34) src/network/internet_gaming.h (+3/-0) src/network/internet_gaming_protocol.h (+49/-29) src/network/relay_protocol.h (+2/-1) src/ui_fsmenu/multiplayer.cc (+12/-3) src/wlapplication.cc (+13/-2) src/wui/login_box.cc (+0/-1) |
To merge this branch: | bzr merge lp:~widelands-dev/widelands/net-pwd-security |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
GunChleoc | Approve | ||
Review via email: mp+340540@code.launchpad.net |
Commit message
Increasing password security by no longer storing and transmitting it in plaintext.
Description of the change
Increasing password security by using CHAP for transmitting the password instead of sending it in plaintext. With CHAP, a challenge (random value) is send to the connecting client and the client responds by sending back the cryptographic hash of the challenge concatenated with its secret password. A cryptographic hash function converts an input to a fixed length output while it is practically impossible to convert the output back to the input. Since the server also knows the password, it can also calculate the hash and can check the hashes whether they are equal. An attacker which captures the send hash can not do anything with it: Extracting the password is not possible and sending the hash to the server does not work since the server will send another challenge on next connect.
To reduce login time, moved TELL_IP functionality for transmitting the IPv4 address to the metaserver to an own thread.
Passwords are now stored as a cryptographic hash in the configuration file. For now, the plaintext password is left in the configuration file if it exists to allow parallel usage of build19 and trunk builds.
Also, removed "max. number of players" network command parameter when opening a game, which has long been obsolete.
This change should only affect new clients, build19 clients should still work with plaintext passwords. This branch should be merged parallel to the respective metaserver branch.
bunnybot (widelandsofficial) wrote : | # |
GunChleoc (gunchleoc) wrote : | # |
2 little nits, LGTM otherwise.
Which algorithm would sou recommend instead of sha-1? If it's available on Django, we should consider changing the website too.
bunnybot (widelandsofficial) wrote : | # |
Continuous integration builds have changed state:
Travis build 3271. State: passed. Details: https:/
Appveyor build 3080. State: success. Details: https:/
Notabilis (notabilis27) wrote : | # |
Thanks for the review. Reading the config has been fixed. Dropping the close() call is on purpose, it is done automatically in the desctructor of the class.
As secure hash algorithms we could use SHA-2 (over 10 years old but unbroken) or SHA-3 (relatively new, uses different approach). For the website we are using hashlib which seems to support both. Updating the database would probably be messy, though.
A C++ implementation is more of a problem. There are some small repositories where the algorithms are implemented in single files which could easily be integrated into widelands. The problem here is that I guess they never received any review so it might not be secure to use them. Preferably would be a tested library like OpenSSL but that would mean a further dependency for widelands. I haven't tried to extract the needed files out of it yet. (One advantage of SHA-1: It is included in boost.)
GunChleoc (gunchleoc) wrote : | # |
OK, let's go with sha-1 for now then. We can always revisit this at some other time.
Changing it on the website would mean that everybody would need to use the "I have lost my password" function, I guess.
kaputtnik (franku) wrote : | # |
I guess the password in this case is the password for Online gaming: https:/
The related model is in wlggz: https:/
Django provides some default hashers for passwords, see https:/
I have no idea about creating passwords..., but it think the online gaming password could also be encrypted using django: https:/
Maybe there is also a possibility to automatically convert old, not encrypted, passwords to encrypted ones?
Notabilis (notabilis27) wrote : | # |
Forcing all users to reset their passwords would be a clean but uncomfortable migration step. Other possibilities would include keeping the old password until it is changed (which would probably be not anytime soon) or hashing the already hashed passwords again (would require more complicated code and no idea how secure this is).
Thanks for the links. The password I am talking about is indeed the one for online gaming, though we could consider only using one password for gaming and the website when we store it securely in the configuration file.
Based on https:/
From your link, Django seems to support SHA-1 and SHA-2/SHA256 but is using an different storage format from what we are using currently. We are simply storing SHA1(password) while the Django-Methods are storing "SHA1"|
A short explanation: Encryption and hashing are two completely different things. With Encryption you have a secret key and can later on decrypt the encrypted message by again using the key. So no information is lost by encryption, but the encrypted data is protected against everyone not owning the secret key.
With hashing, you are "compressing" your data into a byte sequence of fixed length. Naturally, information is lost this way and for cryptographic hash functions it is considered (practically) impossible to find some data which results in the same hash value. So you can calculate hash("text")=>abc but you can't do magic(abc)=>"text". Additionally, it is not feasible to find a string so hash("text1532"
At least the passwords for internet gaming are stored as SHA1 hashes in the database and I guess the passwords for the forum will also be hashed. So it is not possible to extract the password the user entered from the database and hash it again with a more secure algorithm.
What would be possible: The user is entering its password in plaintext when logging in (forum as well as lobby). We could use this plaintext password to update the database entry. This wouldn't help with inactive users, so we would need to run a ... hybrid ... system for quite some time.
Notabilis (notabilis27) wrote : | # |
@bunnybot merge
Preview Diff
1 | === modified file 'src/network/internet_gaming.cc' |
2 | --- src/network/internet_gaming.cc 2018-02-04 11:41:45 +0000 |
3 | +++ src/network/internet_gaming.cc 2018-03-11 10:02:09 +0000 |
4 | @@ -20,6 +20,7 @@ |
5 | #include "network/internet_gaming.h" |
6 | |
7 | #include <memory> |
8 | +#include <thread> |
9 | |
10 | #include <boost/algorithm/string.hpp> |
11 | #include <boost/format.hpp> |
12 | @@ -159,7 +160,7 @@ |
13 | s.string(clientname_); |
14 | s.string(build_id()); |
15 | s.string(bool2str(reg_)); |
16 | - s.string(authenticator_); |
17 | + s.string(reg_ ? "" : authenticator_); |
18 | net->send(s); |
19 | |
20 | // Now let's see, whether the metaserver is answering |
21 | @@ -337,34 +338,76 @@ |
22 | } |
23 | |
24 | void InternetGaming::create_second_connection() { |
25 | - NetAddress addr; |
26 | - net->get_remote_address(&addr); |
27 | - if (!addr.is_ipv6()) { |
28 | + NetAddress addr_net; |
29 | + net->get_remote_address(&addr_net); |
30 | + if (!addr_net.is_ipv6()) { |
31 | // Primary connection already is IPv4, abort |
32 | return; |
33 | } |
34 | |
35 | - if (!NetAddress::resolve_to_v4(&addr, meta_, port_)) { |
36 | - // Could not get the IPv4 address of the metaserver? Strange :-/ |
37 | - return; |
38 | - } |
39 | - |
40 | - std::unique_ptr<NetClient> tmpNet = NetClient::connect(addr); |
41 | - if (!tmpNet || !tmpNet->is_connected()) { |
42 | - // Connecting by IPv4 doesn't work? Well, nothing to do then |
43 | - return; |
44 | - } |
45 | - |
46 | - // Okay, we have a connection. Send the login message and terminate the connection |
47 | - SendPacket s; |
48 | - s.string(IGPCMD_TELL_IP); |
49 | - s.string(boost::lexical_cast<std::string>(kInternetGamingProtocolVersion)); |
50 | - s.string(clientname_); |
51 | - s.string(authenticator_); |
52 | - tmpNet->send(s); |
53 | - |
54 | - // Close the connection |
55 | - tmpNet->close(); |
56 | + // Do real work in thread to reduce freezing of GUI |
57 | + // $this cannot become invalid since it is a global variable |
58 | + // Member variables of $this might change their values but it does not really matter if the thread fails |
59 | + std::thread([this]() { |
60 | + |
61 | + NetAddress addr; |
62 | + if (!NetAddress::resolve_to_v4(&addr, meta_, port_)) { |
63 | + // Could not get the IPv4 address of the metaserver? Strange :-/ |
64 | + return; |
65 | + } |
66 | + |
67 | + std::unique_ptr<NetClient> tmpNet = NetClient::connect(addr); |
68 | + if (!tmpNet || !tmpNet->is_connected()) { |
69 | + // Connecting by IPv4 doesn't work? Well, nothing to do then |
70 | + return; |
71 | + } |
72 | + |
73 | + // Okay, we have a connection. Send the login message and terminate the connection |
74 | + SendPacket s; |
75 | + s.string(IGPCMD_TELL_IP); |
76 | + s.string(boost::lexical_cast<std::string>(kInternetGamingProtocolVersion)); |
77 | + s.string(clientname_); |
78 | + s.string(reg_ ? "" : authenticator_); |
79 | + tmpNet->send(s); |
80 | + if (!reg_) { |
81 | + // Not registered: We are done here |
82 | + return; |
83 | + } |
84 | + |
85 | + // Wait for the challenge |
86 | + uint32_t const secs = time(nullptr); |
87 | + try { |
88 | + while (kInternetGamingTimeout > time(nullptr) - secs) { |
89 | + // Check if the connection is still open |
90 | + if (!tmpNet->is_connected()) { |
91 | + return; |
92 | + } |
93 | + // Try to get a packet |
94 | + std::unique_ptr<RecvPacket> packet = tmpNet->try_receive(); |
95 | + if (!packet) { |
96 | + continue; |
97 | + } |
98 | + const std::string cmd = packet->string(); |
99 | + if (cmd != IGPCMD_PWD_CHALLENGE) { |
100 | + // Wrong command, abort |
101 | + return; |
102 | + } |
103 | + const std::string challenge = packet->string(); |
104 | + // Got a challenge. Calculate the response and send it |
105 | + SendPacket s2; |
106 | + s2.string(IGPCMD_PWD_CHALLENGE); |
107 | + s2.string(crypto::sha1(challenge + authenticator_)); |
108 | + tmpNet->send(s2); |
109 | + // Our work is done |
110 | + return; |
111 | + } |
112 | + } catch (const std::exception& e) { |
113 | + log("InternetGaming: Error when trying to transmit secondary IP.\n"); |
114 | + return; |
115 | + } |
116 | + |
117 | + log("InternetGaming: Timeout when trying to transmit secondary IP.\n"); |
118 | + }).detach(); |
119 | } |
120 | |
121 | /// Handle one packet received from the metaserver. |
122 | @@ -389,7 +432,15 @@ |
123 | |
124 | // Are we already online? |
125 | if (state_ == CONNECTING) { |
126 | - if (cmd == IGPCMD_LOGIN) { |
127 | + if (cmd == IGPCMD_PWD_CHALLENGE) { |
128 | + const std::string nonce = packet.string(); |
129 | + SendPacket s; |
130 | + s.string(IGPCMD_PWD_CHALLENGE); |
131 | + s.string(crypto::sha1(nonce + authenticator_)); |
132 | + net->send(s); |
133 | + return; |
134 | + |
135 | + } else if (cmd == IGPCMD_LOGIN) { |
136 | // Clients request to login was granted |
137 | const std::string assigned_name = packet.string(); |
138 | if (clientname_ != assigned_name) { |
139 | @@ -401,10 +452,11 @@ |
140 | } |
141 | clientname_ = assigned_name; |
142 | clientrights_ = packet.string(); |
143 | - if (clientrights_ == INTERNET_CLIENT_UNREGISTERED) { |
144 | - // Might happen that we log in with less rights than we wanted to. |
145 | + if (reg_ && clientrights_ == INTERNET_CLIENT_UNREGISTERED) { |
146 | + // Permission downgrade: We logged in with less rights than we wanted to. |
147 | // Happens when we are already logged in with another client. |
148 | reg_ = false; |
149 | + authenticator_ = crypto::sha1(clientname_ + authenticator_); |
150 | } |
151 | state_ = LOBBY; |
152 | log("InternetGaming: Client %s logged in.\n", clientname_.c_str()); |
153 | @@ -412,7 +464,7 @@ |
154 | |
155 | } else if (cmd == IGPCMD_ERROR) { |
156 | std::string errortype = packet.string(); |
157 | - if (errortype != "LOGIN" && errortype != "RELOGIN") { |
158 | + if (errortype != IGPCMD_LOGIN && errortype != IGPCMD_PWD_CHALLENGE) { |
159 | log("InternetGaming: Strange ERROR in connecting state: %s\n", errortype.c_str()); |
160 | throw WLWarning( |
161 | _("Mixed up"), _("The metaserver sent a strange ERROR during connection")); |
162 | @@ -425,11 +477,12 @@ |
163 | } else { |
164 | logout(); |
165 | set_error(); |
166 | + log("InternetGaming: Expected a LOGIN, PWD_CHALLENGE or ERROR packet from server, but received " |
167 | + "command %s. Maybe the metaserver is using a different protocol version?", cmd.c_str()); |
168 | throw WLWarning( |
169 | _("Unexpected packet"), |
170 | - _("Expected a LOGIN, RELOGIN or REJECTED packet from server, but received command " |
171 | - "%s. Maybe the metaserver is using a different protocol version ?"), |
172 | - cmd.c_str()); |
173 | + _("Received an unexpected network packet from the metaserver. The metaserver could be " |
174 | + "using a different protocol version. If the error persists, try updating your game.")); |
175 | } |
176 | } |
177 | try { |
178 | @@ -589,6 +642,9 @@ |
179 | if (waitcmd_ == IGPCMD_GAME_OPEN) { |
180 | waitcmd_ = ""; |
181 | } |
182 | + // Get the challenge |
183 | + std::string challenge = packet.string(); |
184 | + relay_password_ = crypto::sha1(challenge + authenticator_); |
185 | // Save the received IP(s), so the client can connect to the game |
186 | NetAddress::parse_ip(&gameips_.first, packet.string(), kInternetRelayPort); |
187 | // If the next value is true, a secondary IP follows |
188 | @@ -666,7 +722,7 @@ |
189 | } |
190 | |
191 | const std::string InternetGaming::relay_password() { |
192 | - return authenticator_; |
193 | + return relay_password_; |
194 | } |
195 | |
196 | /// called by a client to join the game \arg gamename |
197 | @@ -701,7 +757,6 @@ |
198 | SendPacket s; |
199 | s.string(IGPCMD_GAME_OPEN); |
200 | s.string(gamename_); |
201 | - s.string("1024"); // Used to be maxclients, no longer used. |
202 | net->send(s); |
203 | log("InternetGaming: Client opened a game with the name %s.\n", gamename_.c_str()); |
204 | |
205 | |
206 | === modified file 'src/network/internet_gaming.h' |
207 | --- src/network/internet_gaming.h 2017-11-28 20:39:35 +0000 |
208 | +++ src/network/internet_gaming.h 2018-03-11 10:02:09 +0000 |
209 | @@ -218,6 +218,9 @@ |
210 | std::string authenticator_; |
211 | bool reg_; |
212 | |
213 | + /// Password for connecting as host to a game on the relay server |
214 | + std::string relay_password_; |
215 | + |
216 | std::string meta_; |
217 | uint16_t port_; |
218 | |
219 | |
220 | === modified file 'src/network/internet_gaming_protocol.h' |
221 | --- src/network/internet_gaming_protocol.h 2017-11-29 20:43:35 +0000 |
222 | +++ src/network/internet_gaming_protocol.h 2018-03-11 10:02:09 +0000 |
223 | @@ -36,9 +36,10 @@ |
224 | * 2: Between build 19 and build 20 - Added UUID to allow reconnect with same username after |
225 | * crashes. When logging twice with a registered account, the second connection gets a free |
226 | * username assigned. Dropping RELOGIN command. |
227 | - * 3: Between build 19 and build 20 - Added network relay for internet games [supported] |
228 | + * 3: Between build 19 and build 20 - Added network relay for internet games |
229 | + * 4: Between build 19 and build 20 - Using CHAP for password authentication [supported] |
230 | */ |
231 | -constexpr unsigned int kInternetGamingProtocolVersion = 3; |
232 | +constexpr unsigned int kInternetGamingProtocolVersion = 4; |
233 | |
234 | /** |
235 | * The default timeout time after which the client tries to resend a package or even finally closes |
236 | @@ -110,10 +111,8 @@ |
237 | * 1) Linking connections with IPv4 and IPv6 |
238 | * The UUID is used on the metaserver to link multiple connections by the same client. This |
239 | * normally happens when the client supports IPv4 and IPv6 and connects with both protocol versions. |
240 | - * This |
241 | - * way, the metaserver knows that the client supports both versions and can show games / offer its |
242 | - * game |
243 | - * of/for clients with both protocol versions. |
244 | + * This way, the metaserver knows that the client supports both versions and can show games / offer its |
245 | + * game of/for clients with both protocol versions. |
246 | * |
247 | * When a network client connects to the metaserver with (RE)LOGIN it also sends the UUID. |
248 | * When "another" netclient connects to the metaserver and sends TELL_IP containing the same UUID, |
249 | @@ -122,11 +121,9 @@ |
250 | * |
251 | * 2) Reconnect after crash / network problems. |
252 | * When Widelands breaks the connection without logging out, the server still assumes that the old |
253 | - * connection is active. So when the player reconnects, another name is chosen. Sending the UUID |
254 | - * allows |
255 | + * connection is active. So when the player reconnects, another name is chosen. Sending the UUID allows |
256 | * to reclaim the old name, since the server recognizes that there isn't a second player trying to |
257 | - * use |
258 | - * the same name. |
259 | + * use the same name. |
260 | */ |
261 | |
262 | /** |
263 | @@ -144,8 +141,7 @@ |
264 | * |
265 | * \note If you want to change the payload of this command, change it only by appending new items. |
266 | * The reason is that this is the only command that can be sent by the metaserver even when |
267 | - * the |
268 | - * protocol versions differ. |
269 | + * the protocol versions differ. |
270 | * |
271 | */ |
272 | static const std::string IGPCMD_DISCONNECT = "DISCONNECT"; |
273 | @@ -153,25 +149,30 @@ |
274 | /** |
275 | * Initiate a connection. |
276 | * |
277 | - * The first communication across the network stream is a LOGIN command |
278 | + * The first communication across the network stream is a IGPCMD_LOGIN command |
279 | * sent by the client, with the following payload: |
280 | - * \li string: protocol version |
281 | + * \li string: protocol version (see kInternetGamingProtocolVersion) |
282 | * \li string: client name |
283 | * \li string: build_id of the client |
284 | * \li string: whether the client wants to login in to a registered account |
285 | * ("true" or "false" as string) |
286 | - * \li string: for registered accounts: password in clear text |
287 | + * \li string: for registered accounts: string of length 0 |
288 | * for unregistered users the UUID to recognize the matching IPv4 and IPv6 |
289 | * connections or to reclaim the username after a unintended disconnect. |
290 | * For an explanation of the UUID, see above. |
291 | * |
292 | - * If the metaserver accepts, it replies with a LOGIN command with the following payload: |
293 | - * \li string: client name (might be different to the previously chosen one, if the client did |
294 | - * NOT login to a registered account and either the chosen is registered or already |
295 | - * used.) |
296 | - * \li string: clients rights (see client rights section above) |
297 | - * |
298 | - * If no answer is received in \ref kInternetGamingTimeout s the client will again try to login |
299 | + * If the user tries to login to a registered account, a IGPCMD_PWD_CHALLENGE exchange follows before |
300 | + * the server replies with a IGPCMD_LOGIN or IGPCMD_ERROR message. |
301 | + * |
302 | + * If the metaserver accepts, it replies with a IGPCMD_LOGIN command with the following payload: |
303 | + * \li string: client name. Might be different to the previously chosen one, if the chosen |
304 | + * name already used or is registered (and the connecting client is not registered). |
305 | + * \li string: clients rights (see client rights section above) |
306 | + * |
307 | + * When the client is downgraded to an unregistered user on login, a special UUID value |
308 | + * of sha1(assignedName | passwordHash) has to be used on reconnects. |
309 | + * |
310 | + * If no answer is received in \ref kInternetGamingTimeout seconds the client will again try to login |
311 | * \ref INTERNET_GAMING_RETRIES times until it finally bails out something like "server does not |
312 | * answer" |
313 | * |
314 | @@ -184,19 +185,37 @@ |
315 | * |
316 | * Assuming the client already has a connection over IPv6 and tries to establish a secondary |
317 | * connection over IPv4, this is the only message sent. |
318 | - * It should be sent as soon as a connection is established, immediately followed by closing |
319 | - * the connection. No answer from the server should be expected. |
320 | + * It should be sent as soon as a connection is established. |
321 | + * For unregistered users, the connection should be closed immediately following the command. No |
322 | + * answer from the server should be expected. |
323 | + * For registered users, an exchange of IGPCMD_PWD_CHALLENGE commands is done before closing the |
324 | + * connection. |
325 | * |
326 | * Is sent by the client, with the following payload: |
327 | - * \li string: protocol version |
328 | + * \li string: protocol version (see kInternetGamingProtocolVersion) |
329 | * \li string: client name - the one the metaserver replied at the first login |
330 | - * \li string: for registered accounts: password in clear text |
331 | + * \li string: for registered accounts: string of length 0 |
332 | * for unregistered users the UUID used on login |
333 | * for an explanation of the UUID, see above. |
334 | */ |
335 | static const std::string IGPCMD_TELL_IP = "TELL_IP"; |
336 | |
337 | /** |
338 | + * This is sent by the metaserver after a IGPCMD_LOGIN or IGPCMD_TELL_IP by a registered client. |
339 | + * This is the first message of the a protocol similar to the challenge handshake authentication |
340 | + * protocol (CHAP) for secure transmission of the users password. |
341 | + * The server sends the nonce for hashing: |
342 | + * \li string: a nonce for hashing |
343 | + * |
344 | + * The client should answer the message by an own IGPCMD_PWD_CHALLENGE containing the hashed password: |
345 | + * \li string: HASH_SHA1(nonce | HASH_SHA1(password)) |
346 | + * |
347 | + * If the transmitted value is correct, the normal IGPCMD_LOGIN/IGPCMD_TELL_IP sequence continues. If the |
348 | + * value is wrong (e.g., wrong password) the connection is terminated by the servers IGPCMD_DISCONNECT. |
349 | + */ |
350 | +static const std::string IGPCMD_PWD_CHALLENGE = "PWD_CHALLENGE"; |
351 | + |
352 | +/** |
353 | * This command is sent by the metaserver if something went wrong. |
354 | * At least the following payload: |
355 | * \li string: IGPCMD code of the message that lead to the ERROR message or ERROR |
356 | @@ -321,11 +340,12 @@ |
357 | |
358 | /** |
359 | * Sent by the client to announce the startup of a game with following payload: |
360 | - * \li string: name |
361 | - * \li string: number of maximal clients |
362 | - * \note build_id is not necessary, as this is in every way the build_id of the hosting client. |
363 | + * \li string: name of the game |
364 | + * \note build_id is not necessary, as this is the build_id of the hosting client anyway. |
365 | * |
366 | * Sent by the metaserver to acknowledge the startup of a new game with the following payload: |
367 | + * \li string: a challenge that has to be "solved" to work as host of the new game. |
368 | + * See IGPCMD_PWD_CHALLENGE. The response is send to the relay |
369 | * \li string: primary ip of relay server for the game. |
370 | * \li string: whether a secondary ip for the relay follows ("true" or "false" as string) |
371 | * \li string: secondary ip of the relay - only valid if previous was true |
372 | |
373 | === modified file 'src/network/relay_protocol.h' |
374 | --- src/network/relay_protocol.h 2017-12-17 14:45:23 +0000 |
375 | +++ src/network/relay_protocol.h 2018-03-11 10:02:09 +0000 |
376 | @@ -123,7 +123,8 @@ |
377 | * Has the following payload: |
378 | * \li unsigned_8: Protocol version. |
379 | * \li string: Game name. |
380 | - * \li string: For the host: Password that was set on start of the relay. |
381 | + * \li string: For the host: Password that was set on start of the relay. Is the "solution" |
382 | + * for the challenge send by the metaserver on IGPCMD_GAME_OPEN |
383 | * For clients/observers: String "client". |
384 | * |
385 | * Is answered by kWelcome or kDisconnect (if a parameter is wrong/unknown). |
386 | |
387 | === modified file 'src/ui_fsmenu/multiplayer.cc' |
388 | --- src/ui_fsmenu/multiplayer.cc 2017-11-28 20:54:16 +0000 |
389 | +++ src/ui_fsmenu/multiplayer.cc 2018-03-11 10:02:09 +0000 |
390 | @@ -105,13 +105,22 @@ |
391 | Section& s = g_options.pull_section("global"); |
392 | if (auto_log_) { |
393 | nickname_ = s.get_string("nickname", _("nobody")); |
394 | - password_ = s.get_string("password", "nobody"); |
395 | + password_ = s.get_string("password_sha1", "nobody"); |
396 | register_ = s.get_bool("registered", false); |
397 | } else { |
398 | LoginBox lb(*this); |
399 | if (lb.run<UI::Panel::Returncodes>() == UI::Panel::Returncodes::kOk) { |
400 | nickname_ = lb.get_nickname(); |
401 | - password_ = lb.get_password(); |
402 | + /// NOTE: The password is only stored (in memory and on disk) and transmitted (over the network |
403 | + /// to the metaserver) as cryptographic hash. This does NOT mean that the password is stored |
404 | + /// securely on the local disk. While the password should be secure while transmitted to the |
405 | + /// metaserver (no-one can use the transmitted data to log in as the user) this is not the case |
406 | + /// for local storage. The stored hash of the password makes it hard to look at the configuration |
407 | + /// file and figure out the plaintext password to, e.g., log in on the forum. However, the |
408 | + /// stored hash can be copied to another system and used to log in as the user on the metaserver. |
409 | + // Further note: SHA-1 is considered broken and shouldn't be used anymore. But since the |
410 | + // passwords on the server are protected by SHA-1 we have to use it here, too |
411 | + password_ = crypto::sha1(lb.get_password()); |
412 | register_ = lb.registered(); |
413 | |
414 | s.set_bool("registered", lb.registered()); |
415 | @@ -139,7 +148,7 @@ |
416 | |
417 | // Reset InternetGaming and passwort and show internet login again |
418 | InternetGaming::ref().reset(); |
419 | - s.set_string("password", ""); |
420 | + s.set_string("password_sha1", ""); |
421 | show_internet_login(); |
422 | } |
423 | } |
424 | |
425 | === modified file 'src/wlapplication.cc' |
426 | --- src/wlapplication.cc 2017-12-14 09:02:31 +0000 |
427 | +++ src/wlapplication.cc 2018-03-11 10:02:09 +0000 |
428 | @@ -71,6 +71,7 @@ |
429 | #include "logic/single_player_game_controller.h" |
430 | #include "logic/single_player_game_settings_provider.h" |
431 | #include "map_io/map_loader.h" |
432 | +#include "network/crypto.h" |
433 | #include "network/gameclient.h" |
434 | #include "network/gamehost.h" |
435 | #include "network/internet_gaming.h" |
436 | @@ -747,7 +748,10 @@ |
437 | s.get_string("uuid"); |
438 | s.get_string("registered"); |
439 | s.get_string("nickname"); |
440 | + // TODO(Notabilis): Remove next line after build 20. |
441 | + // Currently left in to avoid removing stored passwords for users of both build 19 and trunk |
442 | s.get_string("password"); |
443 | + s.get_string("password_sha1"); |
444 | s.get_string("emailadd"); |
445 | s.get_string("auto_log"); |
446 | s.get_string("lasthost"); |
447 | @@ -765,6 +769,12 @@ |
448 | } |
449 | s.set_int("last_start", time(nullptr)); |
450 | |
451 | + // Replace the stored plaintext password with its SHA-1 hashed version |
452 | + // Used to upgrade the stored password when upgrading widelands |
453 | + if (strlen(s.get_string("password", "")) > 0 && strlen(s.get_string("password_sha1", "")) == 0) { |
454 | + s.set_string("password_sha1", crypto::sha1(s.get_string("password"))); |
455 | + } |
456 | + |
457 | // Save configuration now. Otherwise, the UUID is not saved |
458 | // when the game crashes, loosing part of its advantage |
459 | try { |
460 | @@ -1175,8 +1185,9 @@ |
461 | Section& s = g_options.pull_section("global"); |
462 | s.set_string("nickname", playername); |
463 | // Only change the password if we use a registered account |
464 | - if (registered) |
465 | - s.set_string("password", password); |
466 | + if (registered) { |
467 | + s.set_string("password_sha1", password); |
468 | + } |
469 | |
470 | // reinitalise in every run, else graphics look strange |
471 | FullscreenMenuInternetLobby ns(playername.c_str(), password.c_str(), registered); |
472 | |
473 | === modified file 'src/wui/login_box.cc' |
474 | --- src/wui/login_box.cc 2017-02-26 12:16:09 +0000 |
475 | +++ src/wui/login_box.cc 2018-03-11 10:02:09 +0000 |
476 | @@ -63,7 +63,6 @@ |
477 | |
478 | Section& s = g_options.pull_section("global"); |
479 | eb_nickname->set_text(s.get_string("nickname", _("nobody"))); |
480 | - eb_password->set_text(s.get_string("password", "")); |
481 | cb_register->set_state(s.get_bool("registered", false)); |
482 | eb_nickname->focus(); |
483 | } |
Continuous integration builds have changed state:
Travis build 3266. State: errored. Details: https:/ /travis- ci.org/ widelands/ widelands/ builds/ 348394484. /ci.appveyor. com/project/ widelands- dev/widelands/ build/_ widelands_ dev_widelands_ net_pwd_ security- 3075.
Appveyor build 3075. State: success. Details: https:/