-
Notifications
You must be signed in to change notification settings - Fork 2
/
README
321 lines (219 loc) · 11.2 KB
/
README
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
/*
* Programming Assignment 3 – Secured chat application
*
* Team:
* Einar Örn Gissurarson <[email protected]>
* Óskar Örn <[email protected]>
* Peter Hostačný <[email protected]>
*/
===============================================================================
=======================
|| PROTOCOL ||
=======================
Our chat application is implemented as a client/server architecture.
Designed protocol is very simple - packets consist only of opcode (4 bytes)
and data. Opcode is stored in binary form inside packet. Data are created by
user and thus it will be probably ASCII sequence of characters but server must
be able to handle also binary data.
Detailed description of possible opcodes is bellow. All opcodes are listed
inside enumeration types in chat_common.h.
Packet example:
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 ... MAX_PACKET_SIZE
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|opcode | data |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
==============================
|| CLIENT_TO_SERVER_OPCODES ||
==============================
### Query packets:
OPCODE: WHO
COMMAND: /who # print list of all users
PACKET: <WHO>
OPCODE: LIST
COMMAND: /list # print list of all rooms
PACKET: <LIST>
### Control packets:
OPCODE: JOIN
COMMAND: /join <room> # join the room
PACKET: <JOIN> + <room>
OPCODE: USER
COMMAND: /user <username> # log in
PACKET: <USER> + <username> + WHITESPACE + <password>
### Message packets:
OPCODE: SAY
COMMAND: /say <username> <msg> # private message
PACKET: <SAY> + <username> + WHITESPACE + <message>
OPCODE: MSG
COMMAND: <message> # room message
PACKET: <MSG> + <message>
### Game packets:
OPCODE: GAME
COMMAND: /game <username> # challenge user to play a game
PACKET: <GAME> + <username>
OPCODE: ROLL
COMMAND: /roll # roll a number
PACKET: <ROLL>
OPCODE: ACCEPT
COMMAND: /roll # accept game
PACKET: <ACCEPT>
OPCODE: DECLINE
COMMAND: /decline # decline game
PACKET: <DECLINE>
Commands /bye and /quit just close the connection. No packet is sent to server.
==============================
|| SERVER_TO_CLIENT_OPCODES ||
==============================
OPCODE: ERROR // error message from server
PACKET: <ERROR> + <message>
OPCODE: INFO // info message from server
PACKET: <INFO> + <message>
OPCODE: CHANGE_ROOM // user's room was successfully changed
PACKET: <CHANGE_ROOM> + <room>
OPCODE: LOGGED_IN // user was successfully logged into existing account
PACKET: <LOGGED_IN> + <username>
OPCODE: USER_CREATED // same as LOGGED_IN but new user was created
PACKET: <USER_CREATED> + <username>
OPCODE: ROOM_MESSAGE // new chat message inside room
PACKET: <ROOM_MESSAGE> + <sender_username> + <message>
OPCODE: PRIVATE_MESSAGE_SENT // notification about delivered message
// sent to the sender
PACKET: <PRIVATE_MESSAGE_SENT> + <recipient_username> + " " + <message>
OPCODE: PRIVATE_MESSAGE_RECEIVED // private message sent to the recipient
PACKET: <PRIVATE_MESSAGE_RECEIVED> + <sender_username> + " " + <message>
OPCODE: CHALLENGE // challenge from another user (game)
PACKET: <CHALLENGE> + <challenger>
// this opcode is not part of communication protocol
// it is just for internal use of chat client
OPCODE: CLIENT_ERROR // error message from the client
PACKET: <CLIENT_ERROR> + <message>
================================================================================
=======================
|| Answers ||
=======================
6.2. Answers:
Passwords are stored in a hashed form together with salt strings inside
'passwords.ini' file.
Client application sends password to the server in a plain text form. Since
communication is encrypted, possible eavesdropper cannot retrieve it from
packets. He could do that only in the case, he would get access to client's
device and replaces the CA certificate before starting client application and
do man-in-the-middle attack or get the private key of the server.
Password is then processed through hash function (openssl/ssl.h:
PKCS5_PBKDF2_HMAC_SHA1) together with salt string and result hash string
is stored inside passwords file 'passwords.ini' (there is also salt string
stored for each password).
Parameters used for hashing passwords (chat_common.h)
#define HASH_ITERATION 10000
#define SIZE_OF_HASH 128
#define SALT_LENGTH 32
7.2. Answers:
Question about logging of private messages is in my opinion question about
ethics. In this world, where we have enough of memory to store whole
communication, so we can decide if we want to have possibility of searching
inside history of client's messages. I can imagine that in some countries
could be laws/rules for this (example: terrorist group used my server for
communication about assassination/attack). In case I am using self-signed
certificate even NSA probably cannot see the communication :)
Another possibility is log only some "headers" - who communicated with who,
what time from what IP (without storing messages).
After all, this can be included inside Terms of Use and then it is ok to
decide how I want.
8.2. Answers:
If we didn't use idle and client would left his device unattended, his good
colleagues could post some messges on his behalf. We could use some KEEP-ALIVE
packets from client for making sure that idle time out happens on time.
These packets would be sent only in case of device activity (moving mouse,
typing on keyboard, etc.)
9.2. Answers:
Our server is secure and it is not possible to cheat in this game :D
Every check and action is performed by server and server doesn't allow users
to use commands in bad order or cheat. Also messages about game (roll numbers,
win message) are sent by server and since we made nice colored output, clients
can easily differ between messages from other clients and server (example: if
somebody loged in as SERVER his name will be grey inside chat, while server's
messages are green/red according to their role).
================================================================================
========================
|| IMPLEMENTATION ||
========================
Data inside packets are probably ASCII text but since should be able to handle
also binary data and we didn't want to rely on such an assumption
we implemented it in the way it can process almost any packet (with few
restrictions preventing clients from trying to break the server).
We use non-blocking sockets for client connections so no client can block
the server by stucking at SSL handshake or by any message.
Server uses select() and complicated internal structure for handling multiple
connections. Connections are stored inside double-ended queue (glibc).
Certificate is self-signed but until we can distribute it to all clients
in a secure way, it provides the same level of security as CA-signed
certificates do.
Maximal size of one packet is specified in chat_common.h:
#define MAX_PACKET_SIZE 1200
SERVER RESTRICTIONS:
- User is allowed to max. 3 unsuccessful tries
MAX_FAILED_PASSWD_TRIES constant (chat_common.h)
- User is allowed to try next password after static delay (after failed
attempt to login) ... FAILED_LOGIN_DELAY_TIME (chat_common.h)
- Client is kicked out by server (connection is closed) in case of:
- client sent unknown opcode
- client sent packet smaller than size of opcode (4 bytes)
- client sent packet bigger than MAX_PACKET_SIZE
- client haven't sent any packet for MAX_IDLE_TIME (chat_common.h)
CHAT CLIENT RESTRICTIONS:
- Chat client will split message into more messages if it exceed
MAX_MESSAGE_SIZE (chat_common.h)
- User is forced to use only these characters in username: "A-Za-z0-9._-|"
- Maximal length of username is MAX_USERNAME_LENGTH (chat_common.h)
Maximal length of password is MAX_PASSWORD_LENGTH (chat_common.h)
(server uses dynamic data structures for these strings and
MAX_PACKET_SIZE so he doesn't have to care about security of this and
it is just up to client implementation)
Interesting functions in chat.c:
- clean_and_die() // frees memory and ends the application
- log_msg() // takes care of all server's logging (printed to output)
- send_packet() // takes care of transmitting all outgoing packets
If packet was not successfully transmitted, it is added into
write queue and server will try to transmit it right after socket
is ready.
- initialize_openssl() // initialize openssl structures and loads keys
- run_loop() // server loop for serving clients
- parse_client_message() // parse packet received from a client
- build_and_send_packet() // build packet from opcode and message + send it
Interesting functions in chatd.c:
- clean_and_die() // frees memory and ends the application
- build_and_send_packet() // build packet from opcode and message + send it
- print_message() // print message from server in nice format
(colored, with timestamp)
- readline_callback() // function callback for handling user's input
- process_server_message() // read packet from socket and process it
- initialize_openssl() // initialize openssl structures and loads CA-cert
Interesting functions in chat_common.c:
- recv_packet() // read packet from SSL connection and store into GString
================================================================================
Possible attacks:
- Generate thousands of users (this could be prevented by solid database
of users and force users to create new "accounts" by another interface -
either by new command or through some web interface, probably using
captcha and/or mail verification.
- Overload server by sending too many requests/packets (for example
spamming /who command).
This could be prevented by disabling query commands for not authenticated
users and/or use for these commands some limitation -
for example min. 5 seconds delay between queries or disconnecting user
after detecting such an activity
- Spamming in the room could be prevented by limiting users for max.
of 5 messages in 5 seconds.
======================
Used commands (internal notes)
======================
- generating private key (-aes128 option for key encrypted by passphrase)
openssl genrsa -out server.key 2048
- generating public key from private key (not needed)
openssl rsa -in server.key -pubout -out server-public.key
- generating certificate
openssl req -new -x509 -days 365 -key server.key -out server.crt
- printing info about certificate
openssl x509 -text -in server.crt -noout
- checking if key matches the certificate (compare the values)
openssl x509 -noout -modulus -in server.crt | openssl md5
openssl rsa -noout -modulus -in server.key | openssl md5