X-Git-Url: http://git.silcnet.org/gitweb/?a=blobdiff_plain;f=apps%2Fsilc%2Fclient_ops.c;h=19dbb519c25741e10976790412e86d85af51061d;hb=a818c5b5411bbc4436d1c5f011236985c96bb787;hp=c6134960451cb19ad2f91b927229cde1577985e9;hpb=9b953153b9253683f0d152a405fe1be37d7d065b;p=silc.git diff --git a/apps/silc/client_ops.c b/apps/silc/client_ops.c index c6134960..19dbb519 100644 --- a/apps/silc/client_ops.c +++ b/apps/silc/client_ops.c @@ -20,12 +20,187 @@ #include "clientincludes.h" +static bool +silc_verify_public_key_internal(SilcClient client, SilcClientConnection conn, + SilcSocketType conn_type, unsigned char *pk, + SilcUInt32 pk_len, SilcSKEPKType pk_type) +{ + int i; + char file[256], filename[256], *fingerprint; + struct passwd *pw; + struct stat st; + char *entity = ((conn_type == SILC_SOCKET_TYPE_SERVER || + conn_type == SILC_SOCKET_TYPE_ROUTER) ? + "server" : "client"); + + if (pk_type != SILC_SKE_PK_TYPE_SILC) { + silc_say(client, conn, "We don't support %s public key type %d", + entity, pk_type); + return FALSE; + } + + pw = getpwuid(getuid()); + if (!pw) + return FALSE; + + memset(filename, 0, sizeof(filename)); + memset(file, 0, sizeof(file)); + + if (conn_type == SILC_SOCKET_TYPE_SERVER || + conn_type == SILC_SOCKET_TYPE_ROUTER) { + snprintf(file, sizeof(file) - 1, "%skey_%s_%d.pub", entity, + conn->sock->hostname, conn->sock->port); + snprintf(filename, sizeof(filename) - 1, "%s/.silc/%skeys/%s", + pw->pw_dir, entity, file); + } else { + /* Replace all whitespaces with `_'. */ + fingerprint = silc_hash_fingerprint(NULL, pk, pk_len); + for (i = 0; i < strlen(fingerprint); i++) + if (fingerprint[i] == ' ') + fingerprint[i] = '_'; + + snprintf(file, sizeof(file) - 1, "%skey_%s.pub", entity, fingerprint); + snprintf(filename, sizeof(filename) - 1, "%s/.silc/%skeys/%s", + pw->pw_dir, entity, file); + silc_free(fingerprint); + } + + /* Take fingerprint of the public key */ + fingerprint = silc_hash_fingerprint(NULL, pk, pk_len); + + /* Check whether this key already exists */ + if (stat(filename, &st) < 0) { + + silc_say(client, conn, "Received %s public key", entity); + silc_say(client, conn, "Fingerprint for the %s key is", entity); + silc_say(client, conn, "%s", fingerprint); + + /* Ask user to verify the key and save it */ + if (silc_client_ask_yes_no(client, + "Would you like to accept the key (y/n)? ")) + { + /* Save the key for future checking */ + silc_pkcs_save_public_key_data(filename, pk, pk_len, + SILC_PKCS_FILE_PEM); + silc_free(fingerprint); + return TRUE; + } + } else { + /* The key already exists, verify it. */ + SilcPublicKey public_key; + unsigned char *encpk; + SilcUInt32 encpk_len; + + /* Load the key file */ + if (!silc_pkcs_load_public_key(filename, &public_key, + SILC_PKCS_FILE_PEM)) + if (!silc_pkcs_load_public_key(filename, &public_key, + SILC_PKCS_FILE_BIN)) { + silc_say(client, conn, "Received %s public key", entity); + silc_say(client, conn, "Fingerprint for the %s key is", entity); + silc_say(client, conn, "%s", fingerprint); + silc_say(client, conn, "Could not load your local copy of the %s key", + entity); + if (silc_client_ask_yes_no(client, + "Would you like to accept the key anyway (y/n)? ")) + { + /* Save the key for future checking */ + unlink(filename); + silc_pkcs_save_public_key_data(filename, pk, pk_len, + SILC_PKCS_FILE_PEM); + silc_free(fingerprint); + return TRUE; + } + + silc_free(fingerprint); + return FALSE; + } + + /* Encode the key data */ + encpk = silc_pkcs_public_key_encode(public_key, &encpk_len); + if (!encpk) { + silc_say(client, conn, "Received %s public key", entity); + silc_say(client, conn, "Fingerprint for the %s key is", entity); + silc_say(client, conn, "%s", fingerprint); + silc_say(client, conn, "Your local copy of the %s key is malformed", + entity); + if (silc_client_ask_yes_no(client, + "Would you like to accept the key anyway (y/n)? ")) + { + /* Save the key for future checking */ + unlink(filename); + silc_pkcs_save_public_key_data(filename, pk, pk_len, + SILC_PKCS_FILE_PEM); + silc_free(fingerprint); + return TRUE; + } + + silc_free(fingerprint); + return FALSE; + } + + if (memcmp(encpk, pk, encpk_len)) { + silc_say(client, conn, "Received %s public key", entity); + silc_say(client, conn, "Fingerprint for the %s key is", entity); + silc_say(client, conn, "%s", fingerprint); + silc_say(client, conn, "%s key does not match with your local copy", + entity); + silc_say(client, conn, + "It is possible that the key has expired or changed"); + silc_say(client, conn, "It is also possible that some one is performing " + "man-in-the-middle attack"); + + /* Ask user to verify the key and save it */ + if (silc_client_ask_yes_no(client, + "Would you like to accept the key anyway (y/n)? ")) + { + /* Save the key for future checking */ + unlink(filename); + silc_pkcs_save_public_key_data(filename, pk, pk_len, + SILC_PKCS_FILE_PEM); + silc_free(fingerprint); + return TRUE; + } + + silc_say(client, conn, "Will not accept the %s key", entity); + silc_free(fingerprint); + return FALSE; + } + + /* Local copy matched */ + silc_free(fingerprint); + return TRUE; + } + + silc_say(client, conn, "Will not accept the %s key", entity); + silc_free(fingerprint); + return FALSE; +} + +void silc_say(SilcClient client, SilcClientConnection conn, + char *msg, ...) +{ + va_list vp; + char message[2048]; + SilcClientInternal app = (SilcClientInternal)client->application; + + memset(message, 0, sizeof(message)); + strncat(message, "\n*** ", 5); + + va_start(vp, msg); + vsprintf(message + 5, msg, vp); + va_end(vp); + + /* Print the message */ + silc_print_to_window(app->screen->output_win[0], message); +} + /* Prints a message with three star (*) sign before the actual message on the current output window. This is used to print command outputs and error messages. */ -void silc_say(SilcClient client, SilcClientConnection conn, - char *msg, ...) +void silc_op_say(SilcClient client, SilcClientConnection conn, + SilcClientMessageType type, char *msg, ...) { va_list vp; char message[2048]; @@ -46,24 +221,42 @@ void silc_say(SilcClient client, SilcClientConnection conn, received in the packet. The `channel_name' is the name of the channel. */ void silc_channel_message(SilcClient client, SilcClientConnection conn, - SilcClientEntry sender, SilcChannelEntry channel - , char *msg) + SilcClientEntry sender, SilcChannelEntry channel, + SilcMessageFlags flags, char *msg) { /* Message from client */ if (conn && !strcmp(conn->current_channel->channel_name, channel->channel_name)) - silc_print(client, "<%s> %s", sender ? sender->nickname : "[]", - msg); + if (flags & SILC_MESSAGE_FLAG_ACTION) + silc_print(client, "* %s %s", sender ? sender->nickname : "[]", + msg); + else if (flags & SILC_MESSAGE_FLAG_NOTICE) + silc_print(client, "- %s %s", sender ? sender->nickname : "[]", + msg); + else + silc_print(client, "<%s> %s", sender ? sender->nickname : "[]", + msg); else - silc_print(client, "<%s:%s> %s", sender ? sender->nickname : "[]", - channel->channel_name, msg); + if (flags & SILC_MESSAGE_FLAG_ACTION) + silc_print(client, "* %s:%s %s", sender ? sender->nickname : + "[]", + channel->channel_name, msg); + else if (flags & SILC_MESSAGE_FLAG_NOTICE) + silc_print(client, "- %s:%s %s", sender ? sender->nickname : + "[]", + channel->channel_name, msg); + else + silc_print(client, "<%s:%s> %s", sender ? sender->nickname : + "[]", + channel->channel_name, msg); } /* Private message to the client. The `sender' is the nickname of the sender received in the packet. */ void silc_private_message(SilcClient client, SilcClientConnection conn, - SilcClientEntry sender, char *msg) + SilcClientEntry sender, SilcMessageFlags flags, + char *msg) { silc_print(client, "*%s* %s", sender->nickname, msg); } @@ -86,7 +279,7 @@ void silc_notify(SilcClient client, SilcClientConnection conn, SilcClientEntry client_entry, client_entry2; SilcChannelEntry channel_entry; char *tmp = NULL; - unsigned int tmp_int; + SilcUInt32 tmp_int; va_start(vp, type); @@ -183,19 +376,34 @@ void silc_notify(SilcClient client, SilcClientConnection conn, case SILC_NOTIFY_TYPE_CMODE_CHANGE: client_entry = va_arg(vp, SilcClientEntry); - tmp_int = va_arg(vp, unsigned int); + tmp_int = va_arg(vp, SilcUInt32); (void)va_arg(vp, char *); (void)va_arg(vp, char *); channel_entry = va_arg(vp, SilcChannelEntry); - tmp = silc_client_chmode(tmp_int, channel_entry); + tmp = silc_client_chmode(tmp_int, + channel_entry->channel_key->cipher->name, + channel_entry->hmac->hmac->name); - if (tmp) - snprintf(message, sizeof(message), "%s changed channel mode to +%s", - client_entry->nickname, tmp); - else - snprintf(message, sizeof(message), "%s removed all channel modes", - client_entry->nickname); + if (tmp) { + if (client_entry) { + snprintf(message, sizeof(message), "%s changed channel mode to +%s", + client_entry->nickname, tmp); + } else { + snprintf(message, sizeof(message), + "channel mode was changed to +%s (forced by router)", + tmp); + } + } else { + if (client_entry) { + snprintf(message, sizeof(message), "%s removed all channel modes", + client_entry->nickname); + } else { + snprintf(message, sizeof(message), + "Removed all channel modes (forced by router)"); + } + } + if (app->screen->bottom_line->channel_mode) silc_free(app->screen->bottom_line->channel_mode); app->screen->bottom_line->channel_mode = tmp; @@ -204,7 +412,7 @@ void silc_notify(SilcClient client, SilcClientConnection conn, case SILC_NOTIFY_TYPE_CUMODE_CHANGE: client_entry = va_arg(vp, SilcClientEntry); - tmp_int = va_arg(vp, unsigned int); + tmp_int = va_arg(vp, SilcUInt32); tmp = silc_client_chumode(tmp_int); client_entry2 = va_arg(vp, SilcClientEntry); channel_entry = va_arg(vp, SilcChannelEntry); @@ -247,6 +455,7 @@ void silc_notify(SilcClient client, SilcClientConnection conn, return; case SILC_NOTIFY_TYPE_CHANNEL_CHANGE: + return; break; case SILC_NOTIFY_TYPE_KICKED: @@ -289,6 +498,31 @@ void silc_notify(SilcClient client, SilcClientConnection conn, } break; + case SILC_NOTIFY_TYPE_SERVER_SIGNOFF: + { + SilcClientEntry *clients; + SilcUInt32 clients_count; + int i; + + (void)va_arg(vp, void *); + clients = va_arg(vp, SilcClientEntry *); + clients_count = va_arg(vp, SilcUInt32); + + for (i = 0; i < clients_count; i++) { + if (clients[i]->server) + snprintf(message, sizeof(message), "Server signoff: %s@%s %s%s%s", + clients[i]->nickname, clients[i]->server, + tmp ? "(" : "", tmp ? tmp : "", tmp ? ")" : ""); + else + snprintf(message, sizeof(message), "Server signoff: %s %s%s%s", + clients[i]->nickname, + tmp ? "(" : "", tmp ? tmp : "", tmp ? ")" : ""); + silc_print(client, "*** %s", message); + memset(message, 0, sizeof(message)); + } + return; + } + default: break; } @@ -323,12 +557,9 @@ void silc_command(SilcClient client, SilcClientConnection conn, break; case SILC_COMMAND_LEAVE: -#if 0 - if (!strncmp(conn->current_channel->channel_name, name, strlen(name))) { - app->screen->bottom_line->channel = NULL; - silc_screen_print_bottom_line(app->screen, 0); - } -#endif + /* We won't talk anymore on this channel */ + silc_say(client, conn, "You have left channel %s", + conn->current_channel->channel_name); break; } @@ -340,7 +571,7 @@ void silc_command(SilcClient client, SilcClientConnection conn, void silc_client_show_users(SilcClient client, SilcClientConnection conn, SilcClientEntry *clients, - unsigned int clients_count, + SilcUInt32 clients_count, void *context) { SilcChannelEntry channel = (SilcChannelEntry)context; @@ -379,7 +610,7 @@ void silc_client_show_users(SilcClient client, k++; } - client->ops->say(client, conn, "Users on %s: %s", channel->channel_name, + silc_say(client, conn, "Users on %s: %s", channel->channel_name, name_list); silc_free(name_list); } @@ -416,7 +647,7 @@ void silc_command_reply(SilcClient client, SilcClientConnection conn, { char buf[1024], *nickname, *username, *realname; int len; - unsigned int idle, mode; + SilcUInt32 idle, mode; SilcBuffer channels; if (status == SILC_STATUS_ERR_NO_SUCH_NICK || @@ -425,10 +656,10 @@ void silc_command_reply(SilcClient client, SilcClientConnection conn, tmp = silc_argument_get_arg_type(silc_command_get_args(cmd_payload), 3, NULL); if (tmp) - client->ops->say(client, conn, "%s: %s", tmp, + silc_say(client, conn, "%s: %s", tmp, silc_client_command_status_message(status)); else - client->ops->say(client, conn, "%s", + silc_say(client, conn, "%s", silc_client_command_status_message(status)); break; } @@ -441,8 +672,8 @@ void silc_command_reply(SilcClient client, SilcClientConnection conn, username = va_arg(vp, char *); realname = va_arg(vp, char *); channels = va_arg(vp, SilcBuffer); - mode = va_arg(vp, unsigned int); - idle = va_arg(vp, unsigned int); + mode = va_arg(vp, SilcUInt32); + idle = va_arg(vp, SilcUInt32); memset(buf, 0, sizeof(buf)); @@ -462,7 +693,7 @@ void silc_command_reply(SilcClient client, SilcClientConnection conn, strncat(buf, ")", 1); } - client->ops->say(client, conn, "%s", buf); + silc_say(client, conn, "%s", buf); if (channels) { SilcDList list = silc_channel_payload_parse_list(channels); @@ -475,7 +706,7 @@ void silc_command_reply(SilcClient client, SilcClientConnection conn, silc_dlist_start(list); while ((entry = silc_dlist_get(list)) != SILC_LIST_END) { char *m = silc_client_chumode_char(silc_channel_get_mode(entry)); - unsigned int name_len; + SilcUInt32 name_len; char *name = silc_channel_get_name(entry, &name_len); if (m) @@ -485,20 +716,26 @@ void silc_command_reply(SilcClient client, SilcClientConnection conn, silc_free(m); } - client->ops->say(client, conn, "%s", buf); + silc_say(client, conn, "%s", buf); silc_channel_payload_list_free(list); } } - if (mode) - client->ops->say(client, conn, "%s is %s", nickname, - (mode & SILC_UMODE_SERVER_OPERATOR) ? - "Server Operator" : - (mode & SILC_UMODE_ROUTER_OPERATOR) ? - "SILC Operator" : "[Unknown mode]"); + if (mode) { + if ((mode & SILC_UMODE_SERVER_OPERATOR) || + (mode & SILC_UMODE_ROUTER_OPERATOR)) + silc_say(client, conn, "%s is %s", nickname, + (mode & SILC_UMODE_SERVER_OPERATOR) ? + "Server Operator" : + (mode & SILC_UMODE_ROUTER_OPERATOR) ? + "SILC Operator" : "[Unknown mode]"); + + if (mode & SILC_UMODE_GONE) + silc_say(client, conn, "%s is gone", nickname); + } if (idle && nickname) - client->ops->say(client, conn, "%s has been idle %d %s", + silc_say(client, conn, "%s has been idle %d %s", nickname, idle > 60 ? (idle / 60) : idle, idle > 60 ? "minutes" : "seconds"); @@ -516,10 +753,10 @@ void silc_command_reply(SilcClient client, SilcClientConnection conn, tmp = silc_argument_get_arg_type(silc_command_get_args(cmd_payload), 3, NULL); if (tmp) - client->ops->say(client, conn, "%s: %s", tmp, + silc_say(client, conn, "%s: %s", tmp, silc_client_command_status_message(status)); else - client->ops->say(client, conn, "%s", + silc_say(client, conn, "%s", silc_client_command_status_message(status)); break; } @@ -550,7 +787,7 @@ void silc_command_reply(SilcClient client, SilcClientConnection conn, strncat(buf, ")", 1); } - client->ops->say(client, conn, "%s", buf); + silc_say(client, conn, "%s", buf); } break; @@ -576,10 +813,10 @@ void silc_command_reply(SilcClient client, SilcClientConnection conn, case SILC_COMMAND_JOIN: { - unsigned int mode; + SilcUInt32 mode; char *topic; SilcBuffer client_id_list; - unsigned int list_count; + SilcUInt32 list_count; SilcChannelEntry channel; if (!success) @@ -587,22 +824,24 @@ void silc_command_reply(SilcClient client, SilcClientConnection conn, app->screen->bottom_line->channel = va_arg(vp, char *); channel = va_arg(vp, SilcChannelEntry); - mode = va_arg(vp, unsigned int); - (void)va_arg(vp, unsigned int); + mode = va_arg(vp, SilcUInt32); + (void)va_arg(vp, SilcUInt32); (void)va_arg(vp, unsigned char *); (void)va_arg(vp, unsigned char *); (void)va_arg(vp, unsigned char *); topic = va_arg(vp, char *); (void)va_arg(vp, unsigned char *); - list_count = va_arg(vp, unsigned int); + list_count = va_arg(vp, SilcUInt32); client_id_list = va_arg(vp, SilcBuffer); if (topic) - client->ops->say(client, conn, "Topic for %s: %s", + silc_say(client, conn, "Topic for %s: %s", app->screen->bottom_line->channel, topic); app->screen->bottom_line->channel_mode = - silc_client_chmode(mode, channel); + silc_client_chmode(mode, + channel->channel_key->cipher->name, + channel->hmac->hmac->name); silc_screen_print_bottom_line(app->screen, 0); /* Resolve the client information */ @@ -629,7 +868,7 @@ void silc_command_reply(SilcClient client, SilcClientConnection conn, case SILC_COMMAND_LIST: { char *topic, *name; - unsigned int usercount; + int usercount; unsigned char buf[256], tmp[16]; int i, len; @@ -639,7 +878,7 @@ void silc_command_reply(SilcClient client, SilcClientConnection conn, (void)va_arg(vp, SilcChannelEntry); name = va_arg(vp, char *); topic = va_arg(vp, char *); - usercount = va_arg(vp, unsigned int); + usercount = va_arg(vp, int); if (status == SILC_STATUS_LIST_START || status == SILC_STATUS_OK) @@ -677,12 +916,12 @@ void silc_command_reply(SilcClient client, SilcClientConnection conn, case SILC_COMMAND_UMODE: { - unsigned int mode; + SilcUInt32 mode; if (!success) return; - mode = va_arg(vp, unsigned int); + mode = va_arg(vp, SilcUInt32); if (!mode && app->screen->bottom_line->umode) { silc_free(app->screen->bottom_line->umode); @@ -726,19 +965,88 @@ void silc_command_reply(SilcClient client, SilcClientConnection conn, break; case SILC_COMMAND_USERS: - if (!success) - return; + { + SilcChannelEntry channel; + int line_len; + char *line; + + if (!success) + return; - silc_list_start(conn->current_channel->clients); - while ((chu = silc_list_get(conn->current_channel->clients)) - != SILC_LIST_END) { - if (chu->client == conn->local_entry) { - if (app->screen->bottom_line->mode) - silc_free(app->screen->bottom_line->mode); - app->screen->bottom_line->mode = silc_client_chumode_char(chu->mode); - silc_screen_print_bottom_line(app->screen, 0); - break; + channel = va_arg(vp, SilcChannelEntry); + + /* There are two ways to do this, either parse the list (that + the command_reply sends (just take it with va_arg()) or just + traverse the channel's client list. I'll do the latter. See + JOIN command reply for example for the list. */ + + silc_say(client, conn, "Users on %s", channel->channel_name); + + line = silc_calloc(1024, sizeof(*line)); + line_len = 1024; + silc_list_start(channel->clients); + while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) { + SilcClientEntry e = chu->client; + int i, len1; + char *m, tmp[80]; + + memset(line, 0, line_len); + + if (chu->client == conn->local_entry) { + /* Update status line */ + if (app->screen->bottom_line->mode) + silc_free(app->screen->bottom_line->mode); + app->screen->bottom_line->mode = + silc_client_chumode_char(chu->mode); + silc_screen_print_bottom_line(app->screen, 0); + } + + if (strlen(e->nickname) + strlen(e->server) + 100 > line_len) { + silc_free(line); + line_len += strlen(e->nickname) + strlen(e->server) + 100; + line = silc_calloc(line_len, sizeof(*line)); + } + + memset(tmp, 0, sizeof(tmp)); + m = silc_client_chumode_char(chu->mode); + + strncat(line, " ", 1); + strncat(line, e->nickname, strlen(e->nickname)); + strncat(line, e->server ? "@" : "", 1); + + len1 = 0; + if (e->server) + len1 = strlen(e->server); + strncat(line, e->server ? e->server : "", len1 > 30 ? 30 : len1); + + len1 = strlen(line); + if (len1 >= 30) { + memset(&line[29], 0, len1 - 29); + } else { + for (i = 0; i < 30 - len1 - 1; i++) + strcat(line, " "); + } + + if (e->mode & SILC_UMODE_GONE) + strcat(line, " G"); + else + strcat(line, " H"); + strcat(tmp, m ? m : ""); + strncat(line, tmp, strlen(tmp)); + + if (strlen(tmp) < 5) + for (i = 0; i < 5 - strlen(tmp); i++) + strcat(line, " "); + + strcat(line, e->username ? e->username : ""); + + silc_say(client, conn, "%s", line); + + if (m) + silc_free(m); } + + silc_free(line); } break; @@ -761,6 +1069,47 @@ void silc_command_reply(SilcClient client, SilcClientConnection conn, } break; + case SILC_COMMAND_GETKEY: + { + SilcIdType id_type; + void *entry; + SilcPublicKey public_key; + unsigned char *pk; + SilcUInt32 pk_len; + + id_type = va_arg(vp, SilcUInt32); + entry = va_arg(vp, void *); + public_key = va_arg(vp, SilcPublicKey); + + pk = silc_pkcs_public_key_encode(public_key, &pk_len); + + if (id_type == SILC_ID_CLIENT) { + silc_verify_public_key_internal(client, conn, + SILC_SOCKET_TYPE_CLIENT, + pk, pk_len, SILC_SKE_PK_TYPE_SILC); + } + + silc_free(pk); + } + + case SILC_COMMAND_TOPIC: + { + SilcChannelEntry channel; + char *topic; + + if (!success) + return; + + channel = va_arg(vp, SilcChannelEntry); + topic = va_arg(vp, char *); + + if (topic) + silc_say(client, conn, + "Topic on channel %s: %s", channel->channel_name, + topic); + } + break; + default: break; } @@ -794,12 +1143,11 @@ void silc_disconnect(SilcClient client, SilcClientConnection conn) /* Asks passphrase from user on the input line. */ -unsigned char *silc_ask_passphrase(SilcClient client, - SilcClientConnection conn) +void silc_ask_passphrase(SilcClient client, SilcClientConnection conn, + SilcAskPassphrase completion, void *context) { SilcClientInternal app = (SilcClientInternal)conn->client->application; char pass1[256], pass2[256]; - char *ret; int try = 3; while(try) { @@ -828,157 +1176,33 @@ unsigned char *silc_ask_passphrase(SilcClient client, try--; } - ret = silc_calloc(strlen(pass1), sizeof(char)); - memcpy(ret, pass1, strlen(pass1)); - - memset(pass1, 0, sizeof(pass1)); - memset(pass2, 0, sizeof(pass2)); - wattroff(app->screen->input_win, A_INVIS); silc_screen_input_reset(app->screen); - return ret; + /* Deliver the passphrase to the library */ + completion(pass1, strlen(pass1), context); + + memset(pass1, 0, sizeof(pass1)); + memset(pass2, 0, sizeof(pass2)); } -/* Verifies received public key. If user decides to trust the key it is - saved as trusted server key for later use. If user does not trust the - key this returns FALSE. */ +/* Verifies received public key. The `conn_type' indicates which entity + (server, client etc.) has sent the public key. If user decides to trust + the key may be saved as trusted public key for later use. The + `completion' must be called after the public key has been verified. */ -int silc_verify_server_key(SilcClient client, - SilcClientConnection conn, - unsigned char *pk, unsigned int pk_len, - SilcSKEPKType pk_type) +void silc_verify_public_key(SilcClient client, SilcClientConnection conn, + SilcSocketType conn_type, unsigned char *pk, + SilcUInt32 pk_len, SilcSKEPKType pk_type, + SilcVerifyPublicKey completion, void *context) { - SilcSocketConnection sock = conn->sock; - char filename[256]; - char file[256]; - char *hostname, *fingerprint; - struct passwd *pw; - struct stat st; - - hostname = sock->hostname ? sock->hostname : sock->ip; - - if (pk_type != SILC_SKE_PK_TYPE_SILC) { - silc_say(client, conn, "We don't support server %s key type", hostname); - return FALSE; - } - - pw = getpwuid(getuid()); - if (!pw) - return FALSE; - - memset(filename, 0, sizeof(filename)); - memset(file, 0, sizeof(file)); - snprintf(file, sizeof(file) - 1, "serverkey_%s_%d.pub", hostname, - sock->port); - snprintf(filename, sizeof(filename) - 1, "%s/.silc/serverkeys/%s", - pw->pw_dir, file); - - /* Check wheter this key already exists */ - if (stat(filename, &st) < 0) { - - fingerprint = silc_hash_fingerprint(NULL, pk, pk_len); - silc_say(client, conn, "Received server %s public key", hostname); - silc_say(client, conn, "Fingerprint for the server %s key is", hostname); - silc_say(client, conn, "%s", fingerprint); - silc_free(fingerprint); - - /* Ask user to verify the key and save it */ - if (silc_client_ask_yes_no(client, - "Would you like to accept the key (y/n)? ")) - { - /* Save the key for future checking */ - silc_pkcs_save_public_key_data(filename, pk, pk_len, - SILC_PKCS_FILE_PEM); - return TRUE; - } - } else { - /* The key already exists, verify it. */ - SilcPublicKey public_key; - unsigned char *encpk; - unsigned int encpk_len; - - /* Load the key file */ - if (!silc_pkcs_load_public_key(filename, &public_key, - SILC_PKCS_FILE_PEM)) - if (!silc_pkcs_load_public_key(filename, &public_key, - SILC_PKCS_FILE_BIN)) { - fingerprint = silc_hash_fingerprint(NULL, pk, pk_len); - silc_say(client, conn, "Received server %s public key", hostname); - silc_say(client, conn, "Fingerprint for the server %s key is", hostname); - silc_say(client, conn, "%s", fingerprint); - silc_free(fingerprint); - silc_say(client, conn, "Could not load your local copy of the server %s key", - hostname); - if (silc_client_ask_yes_no(client, - "Would you like to accept the key anyway (y/n)? ")) - { - /* Save the key for future checking */ - unlink(filename); - silc_pkcs_save_public_key_data(filename, pk, pk_len, - SILC_PKCS_FILE_PEM); - return TRUE; - } - - return FALSE; - } - - /* Encode the key data */ - encpk = silc_pkcs_public_key_encode(public_key, &encpk_len); - if (!encpk) { - fingerprint = silc_hash_fingerprint(NULL, pk, pk_len); - silc_say(client, conn, "Received server %s public key", hostname); - silc_say(client, conn, "Fingerprint for the server %s key is", hostname); - silc_say(client, conn, "%s", fingerprint); - silc_free(fingerprint); - silc_say(client, conn, "Your local copy of the server %s key is malformed", - hostname); - if (silc_client_ask_yes_no(client, - "Would you like to accept the key anyway (y/n)? ")) - { - /* Save the key for future checking */ - unlink(filename); - silc_pkcs_save_public_key_data(filename, pk, pk_len, - SILC_PKCS_FILE_PEM); - return TRUE; - } - - return FALSE; - } - - if (memcmp(encpk, pk, encpk_len)) { - fingerprint = silc_hash_fingerprint(NULL, pk, pk_len); - silc_say(client, conn, "Received server %s public key", hostname); - silc_say(client, conn, "Fingerprint for the server %s key is", hostname); - silc_say(client, conn, "%s", fingerprint); - silc_free(fingerprint); - silc_say(client, conn, "Server %s key does not match with your local copy", - hostname); - silc_say(client, conn, "It is possible that the key has expired or changed"); - silc_say(client, conn, "It is also possible that some one is performing " - "man-in-the-middle attack"); - - /* Ask user to verify the key and save it */ - if (silc_client_ask_yes_no(client, - "Would you like to accept the key anyway (y/n)? ")) - { - /* Save the key for future checking */ - unlink(filename); - silc_pkcs_save_public_key_data(filename, pk, pk_len, - SILC_PKCS_FILE_PEM); - return TRUE; - } - - silc_say(client, conn, "Will not accept server %s key", hostname); - return FALSE; - } - - /* Local copy matched */ - return TRUE; + if (silc_verify_public_key_internal(client, conn, conn_type, pk, + pk_len, pk_type)) { + completion(TRUE, context); + return; } - silc_say(client, conn, "Will not accept server %s key", hostname); - return FALSE; + completion(FALSE, context); } /* Find authentication method and authentication data by hostname and @@ -988,14 +1212,14 @@ int silc_verify_server_key(SilcClient client, is found and FALSE if not. `conn' may be NULL. */ int silc_get_auth_method(SilcClient client, SilcClientConnection conn, - char *hostname, unsigned short port, + char *hostname, SilcUInt16 port, SilcProtocolAuthMeth *auth_meth, unsigned char **auth_data, - unsigned int *auth_data_len) + SilcUInt32 *auth_data_len) { SilcClientInternal app = (SilcClientInternal)client->application; - if (app->config->conns) { + if (app->config && app->config->conns) { SilcClientConfigSectionConnection *conn = NULL; /* Check if we find a match from user configured connections */ @@ -1015,7 +1239,11 @@ int silc_get_auth_method(SilcClient client, SilcClientConnection conn, } } - return FALSE; + *auth_meth = SILC_AUTH_NONE; + *auth_data = NULL; + *auth_data_len = 0; + + return TRUE; } /* Notifies application that failure packet was received. This is called @@ -1028,7 +1256,40 @@ int silc_get_auth_method(SilcClient client, SilcClientConnection conn, void silc_failure(SilcClient client, SilcClientConnection conn, SilcProtocol protocol, void *failure) { + if (protocol->protocol->type == SILC_PROTOCOL_CLIENT_KEY_EXCHANGE) { + SilcSKEStatus status = (SilcSKEStatus)failure; + + if (status == SILC_SKE_STATUS_BAD_VERSION) + silc_say(client, conn, + "You are running incompatible client version (it may be " + "too old or too new)"); + if (status == SILC_SKE_STATUS_UNSUPPORTED_PUBLIC_KEY) + silc_say(client, conn, "Server does not support your public key type"); + if (status == SILC_SKE_STATUS_UNKNOWN_GROUP) + silc_say(client, conn, + "Server does not support one of your proposed KE group"); + if (status == SILC_SKE_STATUS_UNKNOWN_CIPHER) + silc_say(client, conn, + "Server does not support one of your proposed cipher"); + if (status == SILC_SKE_STATUS_UNKNOWN_PKCS) + silc_say(client, conn, + "Server does not support one of your proposed PKCS"); + if (status == SILC_SKE_STATUS_UNKNOWN_HASH_FUNCTION) + silc_say(client, conn, + "Server does not support one of your proposed hash function"); + if (status == SILC_SKE_STATUS_UNKNOWN_HMAC) + silc_say(client, conn, + "Server does not support one of your proposed HMAC"); + if (status == SILC_SKE_STATUS_INCORRECT_SIGNATURE) + silc_say(client, conn, "Incorrect signature"); + } + + if (protocol->protocol->type == SILC_PROTOCOL_CLIENT_CONNECTION_AUTH) { + SilcUInt32 err = (SilcUInt32)failure; + if (err == SILC_AUTH_FAILED) + silc_say(client, conn, "Authentication failed"); + } } /* Asks whether the user would like to perform the key agreement protocol. @@ -1044,23 +1305,38 @@ int silc_key_agreement(SilcClient client, SilcClientConnection conn, SilcKeyAgreementCallback *completion, void **context) { + char host[256]; + + /* We will just display the info on the screen and return FALSE and user + will have to start the key agreement with a command. */ + + if (hostname) { + memset(host, 0, sizeof(host)); + snprintf(host, sizeof(host) - 1, "(%s on port %d)", hostname, port); + } + + silc_say(client, conn, "%s wants to perform key agreement %s", + client_entry->nickname, hostname ? host : ""); + + *completion = NULL; + *context = NULL; return FALSE; } /* SILC client operations */ SilcClientOperations ops = { - say: silc_say, - channel_message: silc_channel_message, - private_message: silc_private_message, - notify: silc_notify, - command: silc_command, - command_reply: silc_command_reply, - connect: silc_connect, - disconnect: silc_disconnect, - get_auth_method: silc_get_auth_method, - verify_server_key: silc_verify_server_key, - ask_passphrase: silc_ask_passphrase, - failure: silc_failure, - key_agreement: silc_key_agreement, + silc_op_say, + silc_channel_message, + silc_private_message, + silc_notify, + silc_command, + silc_command_reply, + silc_connect, + silc_disconnect, + silc_get_auth_method, + silc_verify_public_key, + silc_ask_passphrase, + silc_failure, + silc_key_agreement, };