Minor change to ops API
[silc.git] / apps / silc / client_ops.c
1 /*
2
3   client_ops.c
4
5   Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
6
7   Copyright (C) 2000 Pekka Riikonen
8
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.
13   
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.
18
19 */
20
21 #include "clientincludes.h"
22
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. */
26
27 void silc_say(SilcClient client, SilcClientConnection conn, 
28               char *msg, ...)
29 {
30   va_list vp;
31   char message[2048];
32   SilcClientInternal app = (SilcClientInternal)client->application;
33
34   memset(message, 0, sizeof(message));
35   strncat(message, "\n***  ", 5);
36
37   va_start(vp, msg);
38   vsprintf(message + 5, msg, vp);
39   va_end(vp);
40   
41   /* Print the message */
42   silc_print_to_window(app->screen->output_win[0], message);
43 }
44
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. */
47
48 void silc_channel_message(SilcClient client, SilcClientConnection conn,
49                           char *sender, char *channel_name, char *msg)
50 {
51   /* Message from client */
52   if (!strcmp(conn->current_channel->channel_name, channel_name))
53     silc_print(client, "<%s> %s", sender, msg);
54   else
55     silc_print(client, "<%s:%s> %s", sender, channel_name, msg);
56 }
57
58 /* Private message to the client. The `sender' is the nickname of the
59    sender received in the packet. */
60
61 void silc_private_message(SilcClient client, SilcClientConnection conn,
62                           char *sender, char *msg)
63 {
64   silc_print(client, "*%s* %s", sender, msg);
65 }
66
67
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. */
70
71 void silc_notify(SilcClient client, SilcClientConnection conn, 
72                  SilcNotifyType type, char *msg)
73 {
74   silc_print(client, "*** %s", msg);
75 }
76
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. */
85
86 void silc_command(SilcClient client, SilcClientConnection conn, 
87                   SilcClientCommandContext cmd_context, int success,
88                   SilcCommand command)
89 {
90   SilcClientInternal app = (SilcClientInternal)client->application;
91
92   if (!success)
93     return;
94
95   switch(command)
96     {
97     case SILC_COMMAND_QUIT:
98       app->screen->bottom_line->channel = NULL;
99       silc_screen_print_bottom_line(app->screen, 0);
100       break;
101
102     case SILC_COMMAND_LEAVE:
103 #if 0
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);
107       }
108 #endif
109       break;
110
111     }
112 }
113
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
118    the application. 
119
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). */
129
130 void silc_command_reply(SilcClient client, SilcClientConnection conn,
131                         SilcCommandPayload cmd_payload, int success,
132                         SilcCommandStatus status, SilcCommand command, ...)
133 {
134   SilcClientInternal app = (SilcClientInternal)client->application;
135   va_list vp;
136
137   if (!success)
138     return;
139
140   va_start(vp, command);
141
142   switch(command)
143     {
144
145     case SILC_COMMAND_JOIN:
146       app->screen->bottom_line->channel = va_arg(vp, char *);
147       silc_screen_print_bottom_line(app->screen, 0);
148       break;
149
150     case SILC_COMMAND_NICK:
151       app->screen->bottom_line->nickname = va_arg(vp, char *);
152       silc_screen_print_bottom_line(app->screen, 0);
153       break;
154
155     }
156 }
157
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. */
161
162 void silc_connect(SilcClient client, SilcClientConnection conn, int success)
163 {
164   SilcClientInternal app = (SilcClientInternal)client->application;
165
166   if (success) {
167     app->screen->bottom_line->connection = conn->remote_host;
168     silc_screen_print_bottom_line(app->screen, 0);
169     app->conn = conn;
170   }
171 }
172
173 /* Called to indicate that connection was disconnected to the server. */
174
175 void silc_disconnect(SilcClient client, SilcClientConnection conn)
176 {
177   SilcClientInternal app = (SilcClientInternal)client->application;
178
179   app->screen->bottom_line->connection = NULL;
180   silc_screen_print_bottom_line(app->screen, 0);
181 }
182
183 /* Asks passphrase from user on the input line. */
184
185 unsigned char *silc_ask_passphrase(SilcClient client, 
186                                    SilcClientConnection conn)
187 {
188   SilcClientInternal app = (SilcClientInternal)conn->client->application;
189   char pass1[256], pass2[256];
190   char *ret;
191   int try = 3;
192
193   while(try) {
194
195     /* Print prompt */
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);
199     
200     /* Get string */
201     memset(pass1, 0, sizeof(pass1));
202     wgetnstr(app->screen->input_win, pass1, sizeof(pass1));
203     
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);
208     
209     /* Get string */
210     memset(pass2, 0, sizeof(pass2));
211     wgetnstr(app->screen->input_win, pass2, sizeof(pass2));
212
213     if (!strncmp(pass1, pass2, strlen(pass2)))
214       break;
215
216     try--;
217   }
218
219   ret = silc_calloc(strlen(pass1), sizeof(char));
220   memcpy(ret, pass1, strlen(pass1));
221
222   memset(pass1, 0, sizeof(pass1));
223   memset(pass2, 0, sizeof(pass2));
224
225   wattroff(app->screen->input_win, A_INVIS);
226   silc_screen_input_reset(app->screen);
227
228   return ret;
229 }
230
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. */
234
235 int silc_verify_server_key(SilcClient client,
236                            SilcClientConnection conn, 
237                            unsigned char *pk, unsigned int pk_len,
238                            SilcSKEPKType pk_type)
239 {
240   SilcSocketConnection sock = conn->sock;
241   char filename[256];
242   char file[256];
243   char *hostname, *fingerprint;
244   struct passwd *pw;
245   struct stat st;
246
247   hostname = sock->hostname ? sock->hostname : sock->ip;
248
249   if (pk_type != SILC_SKE_PK_TYPE_SILC) {
250     silc_say(client, conn, "We don't support server %s key type", hostname);
251     return FALSE;
252   }
253
254   pw = getpwuid(getuid());
255   if (!pw)
256     return FALSE;
257
258   memset(filename, 0, sizeof(filename));
259   memset(file, 0, sizeof(file));
260   snprintf(file, sizeof(file) - 1, "serverkey_%s_%d.pub", hostname,
261            sock->port);
262   snprintf(filename, sizeof(filename) - 1, "%s/.silc/serverkeys/%s", 
263            pw->pw_dir, file);
264
265   /* Check wheter this key already exists */
266   if (stat(filename, &st) < 0) {
267
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);
273
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)? "))
277       {
278         /* Save the key for future checking */
279         silc_pkcs_save_public_key_data(filename, pk, pk_len, 
280                                        SILC_PKCS_FILE_PEM);
281         return TRUE;
282       }
283   } else {
284     /* The key already exists, verify it. */
285     SilcPublicKey public_key;
286     unsigned char *encpk;
287     unsigned int encpk_len;
288
289     /* Load the key file */
290     if (!silc_pkcs_load_public_key(filename, &public_key, 
291                                    SILC_PKCS_FILE_PEM))
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",
300                  hostname);
301         if (silc_client_ask_yes_no(client, 
302            "Would you like to accept the key anyway (y/n)? "))
303           {
304             /* Save the key for future checking */
305             unlink(filename);
306             silc_pkcs_save_public_key_data(filename, pk, pk_len,
307                                            SILC_PKCS_FILE_PEM);
308             return TRUE;
309           }
310         
311         return FALSE;
312       }
313   
314     /* Encode the key data */
315     encpk = silc_pkcs_public_key_encode(public_key, &encpk_len);
316     if (!encpk) {
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",
323                hostname);
324       if (silc_client_ask_yes_no(client, 
325          "Would you like to accept the key anyway (y/n)? "))
326         {
327           /* Save the key for future checking */
328           unlink(filename);
329           silc_pkcs_save_public_key_data(filename, pk, pk_len,
330                                          SILC_PKCS_FILE_PEM);
331           return TRUE;
332         }
333
334       return FALSE;
335     }
336
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",
344                hostname);
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");
348       
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)? "))
352         {
353           /* Save the key for future checking */
354           unlink(filename);
355           silc_pkcs_save_public_key_data(filename, pk, pk_len,
356                                          SILC_PKCS_FILE_PEM);
357           return TRUE;
358         }
359
360       silc_say(client, conn, "Will not accept server %s key", hostname);
361       return FALSE;
362     }
363
364     /* Local copy matched */
365     return TRUE;
366   }
367
368   silc_say(client, conn, "Will not accept server %s key", hostname);
369   return FALSE;
370 }
371
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. */
377
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)
383 {
384   SilcClientInternal app = (SilcClientInternal)client->application;
385
386   if (app->config->conns) {
387     SilcClientConfigSectionConnection *conn = NULL;
388
389     /* Check if we find a match from user configured connections */
390     conn = silc_client_config_find_connection(app->config,
391                                               hostname,
392                                               port);
393     if (conn) {
394       /* Match found. Use the configured authentication method */
395       *auth_meth = conn->auth_meth;
396
397       if (conn->auth_data) {
398         *auth_data = strdup(conn->auth_data);
399         *auth_data_len = strlen(conn->auth_data);
400       }
401
402       return TRUE;
403     }
404   }
405
406   return FALSE;
407 }
408
409 /* SILC client operations */
410 SilcClientOperations ops = {
411   say:                  silc_say,
412   channel_message:      silc_channel_message,
413   private_message:      silc_private_message,
414   notify:               silc_notify,
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,
422 };