5 Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
7 Copyright (C) 2000 Pekka Riikonen
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 2 of the License, or
12 (at your option) any later version.
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
21 #include "clientincludes.h"
23 /* Prints a message with three star (*) sign before the actual message
24 on the current output window. This is used to print command outputs
25 and error messages. */
27 void silc_say(SilcClient client, SilcClientConnection conn,
32 SilcClientInternal app = (SilcClientInternal)client->application;
34 memset(message, 0, sizeof(message));
35 strncat(message, "\n*** ", 5);
38 vsprintf(message + 5, msg, vp);
41 /* Print the message */
42 silc_print_to_window(app->screen->output_win[0], message);
45 /* Message for a channel. The `sender' is the nickname of the sender
46 received in the packet. The `channel_name' is the name of the channel. */
48 void silc_channel_message(SilcClient client, SilcClientConnection conn,
49 char *sender, char *channel_name, char *msg)
51 /* Message from client */
52 if (!strcmp(conn->current_channel->channel_name, channel_name))
53 silc_print(client, "<%s> %s", sender, msg);
55 silc_print(client, "<%s:%s> %s", sender, channel_name, msg);
58 /* Private message to the client. The `sender' is the nickname of the
59 sender received in the packet. */
61 void silc_private_message(SilcClient client, SilcClientConnection conn,
62 char *sender, char *msg)
64 silc_print(client, "*%s* %s", sender, msg);
68 /* Notify message to the client. The `type' is the notify type received
69 from server. The `msg' is a human readable message sent by the server. */
71 void silc_notify(SilcClient client, SilcClientConnection conn,
72 SilcNotifyType type, char *msg)
74 silc_print(client, "*** %s", msg);
77 /* Command handler. This function is called always in the command function.
78 If error occurs it will be called as well. `conn' is the associated
79 client connection. `cmd_context' is the command context that was
80 originally sent to the command. `success' is FALSE if error occured
81 during command. `command' is the command being processed. It must be
82 noted that this is not reply from server. This is merely called just
83 after application has called the command. Just to tell application
84 that the command really was processed. */
86 void silc_command(SilcClient client, SilcClientConnection conn,
87 SilcClientCommandContext cmd_context, int success,
90 SilcClientInternal app = (SilcClientInternal)client->application;
97 case SILC_COMMAND_QUIT:
98 app->screen->bottom_line->channel = NULL;
99 silc_screen_print_bottom_line(app->screen, 0);
102 case SILC_COMMAND_LEAVE:
104 if (!strncmp(conn->current_channel->channel_name, name, strlen(name))) {
105 app->screen->bottom_line->channel = NULL;
106 silc_screen_print_bottom_line(app->screen, 0);
114 /* Command reply handler. This function is called always in the command reply
115 function. If error occurs it will be called as well. Normal scenario
116 is that it will be called after the received command data has been parsed
117 and processed. The function is used to pass the received command data to
120 `conn' is the associated client connection. `cmd_payload' is the command
121 payload data received from server and it can be ignored. It is provided
122 if the application would like to re-parse the received command data,
123 however, it must be noted that the data is parsed already by the library
124 thus the payload can be ignored. `success' is FALSE if error occured.
125 In this case arguments are not sent to the application. `command' is the
126 command reply being processed. The function has variable argument list
127 and each command defines the number and type of arguments it passes to the
128 application (on error they are not sent). */
130 void silc_command_reply(SilcClient client, SilcClientConnection conn,
131 SilcCommandPayload cmd_payload, int success,
132 SilcCommand command, ...)
134 SilcClientInternal app = (SilcClientInternal)client->application;
140 va_start(vp, command);
145 case SILC_COMMAND_JOIN:
146 app->screen->bottom_line->channel = va_arg(vp, char *);
147 silc_screen_print_bottom_line(app->screen, 0);
150 case SILC_COMMAND_NICK:
151 app->screen->bottom_line->nickname = va_arg(vp, char *);
152 silc_screen_print_bottom_line(app->screen, 0);
158 /* Called to indicate that connection was either successfully established
159 or connecting failed. This is also the first time application receives
160 the SilcClientConnection objecet which it should save somewhere. */
162 void silc_connect(SilcClient client, SilcClientConnection conn, int success)
164 SilcClientInternal app = (SilcClientInternal)client->application;
167 app->screen->bottom_line->connection = conn->remote_host;
168 silc_screen_print_bottom_line(app->screen, 0);
173 /* Called to indicate that connection was disconnected to the server. */
175 void silc_disconnect(SilcClient client, SilcClientConnection conn)
177 SilcClientInternal app = (SilcClientInternal)client->application;
179 app->screen->bottom_line->connection = NULL;
180 silc_screen_print_bottom_line(app->screen, 0);
183 /* Asks passphrase from user on the input line. */
185 unsigned char *silc_ask_passphrase(SilcClient client,
186 SilcClientConnection conn)
188 SilcClientInternal app = (SilcClientInternal)conn->client->application;
189 char pass1[256], pass2[256];
196 wattroff(app->screen->input_win, A_INVIS);
197 silc_screen_input_print_prompt(app->screen, "Passphrase: ");
198 wattron(app->screen->input_win, A_INVIS);
201 memset(pass1, 0, sizeof(pass1));
202 wgetnstr(app->screen->input_win, pass1, sizeof(pass1));
204 /* Print retype prompt */
205 wattroff(app->screen->input_win, A_INVIS);
206 silc_screen_input_print_prompt(app->screen, "Retype passphrase: ");
207 wattron(app->screen->input_win, A_INVIS);
210 memset(pass2, 0, sizeof(pass2));
211 wgetnstr(app->screen->input_win, pass2, sizeof(pass2));
213 if (!strncmp(pass1, pass2, strlen(pass2)))
219 ret = silc_calloc(strlen(pass1), sizeof(char));
220 memcpy(ret, pass1, strlen(pass1));
222 memset(pass1, 0, sizeof(pass1));
223 memset(pass2, 0, sizeof(pass2));
225 wattroff(app->screen->input_win, A_INVIS);
226 silc_screen_input_reset(app->screen);
231 /* Verifies received public key. If user decides to trust the key it is
232 saved as trusted server key for later use. If user does not trust the
233 key this returns FALSE. */
235 int silc_verify_server_key(SilcClient client,
236 SilcClientConnection conn,
237 unsigned char *pk, unsigned int pk_len,
238 SilcSKEPKType pk_type)
240 SilcSocketConnection sock = conn->sock;
243 char *hostname, *fingerprint;
247 hostname = sock->hostname ? sock->hostname : sock->ip;
249 if (pk_type != SILC_SKE_PK_TYPE_SILC) {
250 silc_say(client, conn, "We don't support server %s key type", hostname);
254 pw = getpwuid(getuid());
258 memset(filename, 0, sizeof(filename));
259 memset(file, 0, sizeof(file));
260 snprintf(file, sizeof(file) - 1, "serverkey_%s_%d.pub", hostname,
262 snprintf(filename, sizeof(filename) - 1, "%s/.silc/serverkeys/%s",
265 /* Check wheter this key already exists */
266 if (stat(filename, &st) < 0) {
268 fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
269 silc_say(client, conn, "Received server %s public key", hostname);
270 silc_say(client, conn, "Fingerprint for the server %s key is", hostname);
271 silc_say(client, conn, "%s", fingerprint);
272 silc_free(fingerprint);
274 /* Ask user to verify the key and save it */
275 if (silc_client_ask_yes_no(client,
276 "Would you like to accept the key (y/n)? "))
278 /* Save the key for future checking */
279 silc_pkcs_save_public_key_data(filename, pk, pk_len,
284 /* The key already exists, verify it. */
285 SilcPublicKey public_key;
286 unsigned char *encpk;
287 unsigned int encpk_len;
289 /* Load the key file */
290 if (!silc_pkcs_load_public_key(filename, &public_key,
292 if (!silc_pkcs_load_public_key(filename, &public_key,
293 SILC_PKCS_FILE_BIN)) {
294 fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
295 silc_say(client, conn, "Received server %s public key", hostname);
296 silc_say(client, conn, "Fingerprint for the server %s key is", hostname);
297 silc_say(client, conn, "%s", fingerprint);
298 silc_free(fingerprint);
299 silc_say(client, conn, "Could not load your local copy of the server %s key",
301 if (silc_client_ask_yes_no(client,
302 "Would you like to accept the key anyway (y/n)? "))
304 /* Save the key for future checking */
306 silc_pkcs_save_public_key_data(filename, pk, pk_len,
314 /* Encode the key data */
315 encpk = silc_pkcs_public_key_encode(public_key, &encpk_len);
317 fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
318 silc_say(client, conn, "Received server %s public key", hostname);
319 silc_say(client, conn, "Fingerprint for the server %s key is", hostname);
320 silc_say(client, conn, "%s", fingerprint);
321 silc_free(fingerprint);
322 silc_say(client, conn, "Your local copy of the server %s key is malformed",
324 if (silc_client_ask_yes_no(client,
325 "Would you like to accept the key anyway (y/n)? "))
327 /* Save the key for future checking */
329 silc_pkcs_save_public_key_data(filename, pk, pk_len,
337 if (memcmp(encpk, pk, encpk_len)) {
338 fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
339 silc_say(client, conn, "Received server %s public key", hostname);
340 silc_say(client, conn, "Fingerprint for the server %s key is", hostname);
341 silc_say(client, conn, "%s", fingerprint);
342 silc_free(fingerprint);
343 silc_say(client, conn, "Server %s key does not match with your local copy",
345 silc_say(client, conn, "It is possible that the key has expired or changed");
346 silc_say(client, conn, "It is also possible that some one is performing "
347 "man-in-the-middle attack");
349 /* Ask user to verify the key and save it */
350 if (silc_client_ask_yes_no(client,
351 "Would you like to accept the key anyway (y/n)? "))
353 /* Save the key for future checking */
355 silc_pkcs_save_public_key_data(filename, pk, pk_len,
360 silc_say(client, conn, "Will not accept server %s key", hostname);
364 /* Local copy matched */
368 silc_say(client, conn, "Will not accept server %s key", hostname);
372 /* Find authentication method and authentication data by hostname and
373 port. The hostname may be IP address as well. The found authentication
374 method and authentication data is returned to `auth_meth', `auth_data'
375 and `auth_data_len'. The function returns TRUE if authentication method
376 is found and FALSE if not. `conn' may be NULL. */
378 int silc_get_auth_method(SilcClient client, SilcClientConnection conn,
379 char *hostname, unsigned short port,
380 SilcProtocolAuthMeth *auth_meth,
381 unsigned char **auth_data,
382 unsigned int *auth_data_len)
384 SilcClientInternal app = (SilcClientInternal)client->application;
386 if (app->config->conns) {
387 SilcClientConfigSectionConnection *conn = NULL;
389 /* Check if we find a match from user configured connections */
390 conn = silc_client_config_find_connection(app->config,
394 /* Match found. Use the configured authentication method */
395 *auth_meth = conn->auth_meth;
397 if (conn->auth_data) {
398 *auth_data = strdup(conn->auth_data);
399 *auth_data_len = strlen(conn->auth_data);
409 /* SILC client operations */
410 SilcClientOperations ops = {
412 channel_message: silc_channel_message,
413 private_message: silc_private_message,
415 command: silc_command,
416 command_reply: silc_command_reply,
417 connect: silc_connect,
418 disconnect: silc_disconnect,
419 get_auth_method: silc_get_auth_method,
420 verify_server_key: silc_verify_server_key,
421 ask_passphrase: silc_ask_passphrase,