Created SILC Client Libary by moving stuff from silc/ directory.
[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 /* Command handler. This function is called always in the command function.
68    If error occurs it will be called as well. `conn' is the associated
69    client connection. `cmd_context' is the command context that was
70    originally sent to the command. `success' is FALSE if error occured
71    during command. `command' is the command being processed. It must be
72    noted that this is not reply from server. This is merely called just
73    after application has called the command. Just to tell application
74    that the command really was processed. */
75
76 void silc_command(SilcClient client, SilcClientConnection conn, 
77                   SilcClientCommandContext cmd_context, int success,
78                   SilcCommand command)
79 {
80   SilcClientInternal app = (SilcClientInternal)client->application;
81
82   if (!success)
83     return;
84
85   switch(command)
86     {
87     case SILC_COMMAND_QUIT:
88       app->screen->bottom_line->channel = NULL;
89       silc_screen_print_bottom_line(app->screen, 0);
90       break;
91
92     case SILC_COMMAND_LEAVE:
93 #if 0
94       if (!strncmp(conn->current_channel->channel_name, name, strlen(name))) {
95         app->screen->bottom_line->channel = NULL;
96         silc_screen_print_bottom_line(app->screen, 0);
97       }
98 #endif
99       break;
100
101     }
102 }
103
104 /* Command reply handler. This function is called always in the command reply
105    function. If error occurs it will be called as well. Normal scenario
106    is that it will be called after the received command data has been parsed
107    and processed. The function is used to pass the received command data to
108    the application. 
109
110    `conn' is the associated client connection. `cmd_payload' is the command
111    payload data received from server and it can be ignored. It is provided
112    if the application would like to re-parse the received command data,
113    however, it must be noted that the data is parsed already by the library
114    thus the payload can be ignored. `success' is FALSE if error occured.
115    In this case arguments are not sent to the application. `command' is the
116    command reply being processed. The function has variable argument list
117    and each command defines the number and type of arguments it passes to the
118    application (on error they are not sent). */
119
120 void silc_command_reply(SilcClient client, SilcClientConnection conn,
121                         SilcCommandPayload cmd_payload, int success,
122                         SilcCommand command, ...)
123 {
124   SilcClientInternal app = (SilcClientInternal)client->application;
125   va_list vp;
126
127   if (!success)
128     return;
129
130   va_start(vp, command);
131
132   switch(command)
133     {
134
135     case SILC_COMMAND_JOIN:
136       app->screen->bottom_line->channel = va_arg(vp, char *);
137       silc_screen_print_bottom_line(app->screen, 0);
138       break;
139
140     case SILC_COMMAND_NICK:
141       app->screen->bottom_line->nickname = va_arg(vp, char *);
142       silc_screen_print_bottom_line(app->screen, 0);
143       break;
144
145     }
146 }
147
148 /* Called to indicate that connection was either successfully established
149    or connecting failed.  This is also the first time application receives
150    the SilcClientConnection objecet which it should save somewhere. */
151
152 void silc_connect(SilcClient client, SilcClientConnection conn, int success)
153 {
154   SilcClientInternal app = (SilcClientInternal)client->application;
155
156   if (success) {
157     app->screen->bottom_line->connection = conn->remote_host;
158     silc_screen_print_bottom_line(app->screen, 0);
159     app->conn = conn;
160   }
161 }
162
163 /* Called to indicate that connection was disconnected to the server. */
164
165 void silc_disconnect(SilcClient client, SilcClientConnection conn)
166 {
167   SilcClientInternal app = (SilcClientInternal)client->application;
168
169   app->screen->bottom_line->connection = NULL;
170   silc_screen_print_bottom_line(app->screen, 0);
171 }
172
173 /* Asks passphrase from user on the input line. */
174
175 unsigned char *silc_ask_passphrase(SilcClient client, 
176                                    SilcClientConnection conn)
177 {
178   SilcClientInternal app = (SilcClientInternal)conn->client->application;
179   char pass1[256], pass2[256];
180   char *ret;
181   int try = 3;
182
183   while(try) {
184
185     /* Print prompt */
186     wattroff(app->screen->input_win, A_INVIS);
187     silc_screen_input_print_prompt(app->screen, "Passphrase: ");
188     wattron(app->screen->input_win, A_INVIS);
189     
190     /* Get string */
191     memset(pass1, 0, sizeof(pass1));
192     wgetnstr(app->screen->input_win, pass1, sizeof(pass1));
193     
194     /* Print retype prompt */
195     wattroff(app->screen->input_win, A_INVIS);
196     silc_screen_input_print_prompt(app->screen, "Retype passphrase: ");
197     wattron(app->screen->input_win, A_INVIS);
198     
199     /* Get string */
200     memset(pass2, 0, sizeof(pass2));
201     wgetnstr(app->screen->input_win, pass2, sizeof(pass2));
202
203     if (!strncmp(pass1, pass2, strlen(pass2)))
204       break;
205
206     try--;
207   }
208
209   ret = silc_calloc(strlen(pass1), sizeof(char));
210   memcpy(ret, pass1, strlen(pass1));
211
212   memset(pass1, 0, sizeof(pass1));
213   memset(pass2, 0, sizeof(pass2));
214
215   wattroff(app->screen->input_win, A_INVIS);
216   silc_screen_input_reset(app->screen);
217
218   return ret;
219 }
220
221 /* Verifies received public key. If user decides to trust the key it is
222    saved as trusted server key for later use. If user does not trust the
223    key this returns FALSE. */
224
225 int silc_verify_server_key(SilcClient client,
226                            SilcClientConnection conn, 
227                            unsigned char *pk, unsigned int pk_len,
228                            SilcSKEPKType pk_type)
229 {
230   SilcSocketConnection sock = conn->sock;
231   char filename[256];
232   char file[256];
233   char *hostname, *fingerprint;
234   struct passwd *pw;
235   struct stat st;
236
237   hostname = sock->hostname ? sock->hostname : sock->ip;
238
239   if (pk_type != SILC_SKE_PK_TYPE_SILC) {
240     silc_say(client, conn, "We don't support server %s key type", hostname);
241     return FALSE;
242   }
243
244   pw = getpwuid(getuid());
245   if (!pw)
246     return FALSE;
247
248   memset(filename, 0, sizeof(filename));
249   memset(file, 0, sizeof(file));
250   snprintf(file, sizeof(file) - 1, "serverkey_%s_%d.pub", hostname,
251            sock->port);
252   snprintf(filename, sizeof(filename) - 1, "%s/.silc/serverkeys/%s", 
253            pw->pw_dir, file);
254
255   /* Check wheter this key already exists */
256   if (stat(filename, &st) < 0) {
257
258     fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
259     silc_say(client, conn, "Received server %s public key", hostname);
260     silc_say(client, conn, "Fingerprint for the server %s key is", hostname);
261     silc_say(client, conn, "%s", fingerprint);
262     silc_free(fingerprint);
263
264     /* Ask user to verify the key and save it */
265     if (silc_client_ask_yes_no(client, 
266        "Would you like to accept the key (y/n)? "))
267       {
268         /* Save the key for future checking */
269         silc_pkcs_save_public_key_data(filename, pk, pk_len, 
270                                        SILC_PKCS_FILE_PEM);
271         return TRUE;
272       }
273   } else {
274     /* The key already exists, verify it. */
275     SilcPublicKey public_key;
276     unsigned char *encpk;
277     unsigned int encpk_len;
278
279     /* Load the key file */
280     if (!silc_pkcs_load_public_key(filename, &public_key, 
281                                    SILC_PKCS_FILE_PEM))
282       if (!silc_pkcs_load_public_key(filename, &public_key, 
283                                      SILC_PKCS_FILE_BIN)) {
284         fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
285         silc_say(client, conn, "Received server %s public key", hostname);
286         silc_say(client, conn, "Fingerprint for the server %s key is", hostname);
287         silc_say(client, conn, "%s", fingerprint);
288         silc_free(fingerprint);
289         silc_say(client, conn, "Could not load your local copy of the server %s key",
290                  hostname);
291         if (silc_client_ask_yes_no(client, 
292            "Would you like to accept the key anyway (y/n)? "))
293           {
294             /* Save the key for future checking */
295             unlink(filename);
296             silc_pkcs_save_public_key_data(filename, pk, pk_len,
297                                            SILC_PKCS_FILE_PEM);
298             return TRUE;
299           }
300         
301         return FALSE;
302       }
303   
304     /* Encode the key data */
305     encpk = silc_pkcs_public_key_encode(public_key, &encpk_len);
306     if (!encpk) {
307       fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
308       silc_say(client, conn, "Received server %s public key", hostname);
309       silc_say(client, conn, "Fingerprint for the server %s key is", hostname);
310       silc_say(client, conn, "%s", fingerprint);
311       silc_free(fingerprint);
312       silc_say(client, conn, "Your local copy of the server %s key is malformed",
313                hostname);
314       if (silc_client_ask_yes_no(client, 
315          "Would you like to accept the key anyway (y/n)? "))
316         {
317           /* Save the key for future checking */
318           unlink(filename);
319           silc_pkcs_save_public_key_data(filename, pk, pk_len,
320                                          SILC_PKCS_FILE_PEM);
321           return TRUE;
322         }
323
324       return FALSE;
325     }
326
327     if (memcmp(encpk, pk, encpk_len)) {
328       fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
329       silc_say(client, conn, "Received server %s public key", hostname);
330       silc_say(client, conn, "Fingerprint for the server %s key is", hostname);
331       silc_say(client, conn, "%s", fingerprint);
332       silc_free(fingerprint);
333       silc_say(client, conn, "Server %s key does not match with your local copy",
334                hostname);
335       silc_say(client, conn, "It is possible that the key has expired or changed");
336       silc_say(client, conn, "It is also possible that some one is performing "
337                        "man-in-the-middle attack");
338       
339       /* Ask user to verify the key and save it */
340       if (silc_client_ask_yes_no(client, 
341          "Would you like to accept the key anyway (y/n)? "))
342         {
343           /* Save the key for future checking */
344           unlink(filename);
345           silc_pkcs_save_public_key_data(filename, pk, pk_len,
346                                          SILC_PKCS_FILE_PEM);
347           return TRUE;
348         }
349
350       silc_say(client, conn, "Will not accept server %s key", hostname);
351       return FALSE;
352     }
353
354     /* Local copy matched */
355     return TRUE;
356   }
357
358   silc_say(client, conn, "Will not accept server %s key", hostname);
359   return FALSE;
360 }
361
362 /* Find authentication method and authentication data by hostname and
363    port. The hostname may be IP address as well. The found authentication
364    method and authentication data is returned to `auth_meth', `auth_data'
365    and `auth_data_len'. The function returns TRUE if authentication method
366    is found and FALSE if not. `conn' may be NULL. */
367
368 int silc_get_auth_method(SilcClient client, SilcClientConnection conn,
369                          char *hostname, unsigned short port,
370                          SilcProtocolAuthMeth *auth_meth,
371                          unsigned char **auth_data,
372                          unsigned int *auth_data_len)
373 {
374   SilcClientInternal app = (SilcClientInternal)client->application;
375
376   if (app->config->conns) {
377     SilcClientConfigSectionConnection *conn = NULL;
378
379     /* Check if we find a match from user configured connections */
380     conn = silc_client_config_find_connection(app->config,
381                                               hostname,
382                                               port);
383     if (conn) {
384       /* Match found. Use the configured authentication method */
385       *auth_meth = conn->auth_meth;
386
387       if (conn->auth_data) {
388         *auth_data = strdup(conn->auth_data);
389         *auth_data_len = strlen(conn->auth_data);
390       }
391
392       return TRUE;
393     }
394   }
395
396   return FALSE;
397 }
398
399 /* SILC client operations */
400 SilcClientOperations ops = {
401   say:                  silc_say,
402   channel_message:      silc_channel_message,
403   private_message:      silc_private_message,
404   command:              silc_command,
405   command_reply:        silc_command_reply,
406   connect:              silc_connect,
407   disconnect:           silc_disconnect,
408   get_auth_method:      silc_get_auth_method,
409   verify_server_key:    silc_verify_server_key,
410   ask_passphrase:       silc_ask_passphrase,
411 };