static void silcpurple_buddy_getkey_cb(SilcPurpleBuddyGetkey g, SilcClientCommandReplyContext cmd) { SilcClientEntry client_entry; unsigned char *pk; SilcUInt32 pk_len; /* Get the client entry. */ client_entry = silc_client_get_client_by_id(g->client, g->conn, &g->client_id); if (!client_entry) { purple_notify_error(g->client->application, _("Get Public Key"), _("The remote user is not present in the network any more"), NULL); silc_free(g); return; } if (!client_entry->public_key) { silc_free(g); return; } /* Now verify the public key */ pk = silc_pkcs_public_key_encode(client_entry->public_key, &pk_len); silcpurple_verify_public_key(g->client, g->conn, client_entry->nickname, SILC_SOCKET_TYPE_CLIENT, pk, pk_len, SILC_SKE_PK_TYPE_SILC, NULL, NULL); silc_free(pk); silc_free(g); }
SilcBuffer silc_public_key_payload_encode(SilcPublicKey public_key) { SilcBuffer buffer; unsigned char *pk; SilcUInt32 pk_len; SilcPKCSType type; if (!public_key) return NULL; type = silc_pkcs_get_type(public_key); pk = silc_pkcs_public_key_encode(public_key, &pk_len); if (!pk) return NULL; buffer = silc_buffer_alloc_size(4 + pk_len); if (!buffer) { silc_free(pk); return NULL; } if (silc_buffer_format(buffer, SILC_STR_UI_SHORT(pk_len), SILC_STR_UI_SHORT(type), SILC_STR_DATA(pk, pk_len), SILC_STR_END) < 0) { silc_buffer_free(buffer); silc_free(pk); return NULL; } silc_free(pk); return buffer; }
static void silcpurple_add_buddy_getkey_cb(SilcPurpleBuddyRes r, SilcClientCommandReplyContext cmd) { SilcClientEntry client_entry; unsigned char *pk; SilcUInt32 pk_len; /* Get the client entry. */ client_entry = silc_client_get_client_by_id(r->client, r->conn, &r->client_id); if (!client_entry || !client_entry->public_key) { /* The buddy is offline/nonexistent. We will require user to associate a public key with the buddy or the buddy cannot be added. */ r->offline = TRUE; silcpurple_add_buddy_ask_pk(r); return; } /* Now verify the public key */ pk = silc_pkcs_public_key_encode(client_entry->public_key, &pk_len); silcpurple_verify_public_key(r->client, r->conn, client_entry->nickname, SILC_SOCKET_TYPE_CLIENT, pk, pk_len, SILC_SKE_PK_TYPE_SILC, silcpurple_add_buddy_save, r); silc_free(pk); }
static void silcpurple_add_buddy_ask_import(void *user_data, const char *name) { SilcPurpleBuddyRes r = (SilcPurpleBuddyRes)user_data; SilcPublicKey public_key; /* Load the public key */ if (!silc_pkcs_load_public_key(name, &public_key, SILC_PKCS_FILE_PEM) && !silc_pkcs_load_public_key(name, &public_key, SILC_PKCS_FILE_BIN)) { silcpurple_add_buddy_ask_pk_cb(r, 0); purple_notify_error(r->client->application, _("Add Buddy"), _("Could not load public key"), NULL); return; } /* Now verify the public key */ r->offline_pk = silc_pkcs_public_key_encode(public_key, &r->offline_pk_len); silcpurple_verify_public_key(r->client, r->conn, r->b->name, SILC_SOCKET_TYPE_CLIENT, r->offline_pk, r->offline_pk_len, SILC_SKE_PK_TYPE_SILC, silcpurple_add_buddy_save, r); }
static void silcpurple_chat_getinfo(PurpleConnection *gc, GHashTable *components) { SilcPurple sg = gc->proto_data; const char *chname; char tmp[256], *tmp2; GString *s; SilcChannelEntry channel; SilcHashTableList htl; SilcChannelUser chu; if (!components) return; chname = g_hash_table_lookup(components, "channel"); if (!chname) return; channel = silc_client_get_channel(sg->client, sg->conn, (char *)chname); if (!channel) { silc_client_get_channel_resolve(sg->client, sg->conn, (char *)chname, silcpurple_chat_getinfo_res, components); return; } s = g_string_new(""); tmp2 = g_markup_escape_text(channel->channel_name, -1); g_string_append_printf(s, _("<b>Channel Name:</b> %s"), tmp2); g_free(tmp2); if (channel->user_list && silc_hash_table_count(channel->user_list)) g_string_append_printf(s, _("<br><b>User Count:</b> %d"), (int)silc_hash_table_count(channel->user_list)); silc_hash_table_list(channel->user_list, &htl); while (silc_hash_table_get(&htl, NULL, (void *)&chu)) { if (chu->mode & SILC_CHANNEL_UMODE_CHANFO) { tmp2 = g_markup_escape_text(chu->client->nickname, -1); g_string_append_printf(s, _("<br><b>Channel Founder:</b> %s"), tmp2); g_free(tmp2); break; } } silc_hash_table_list_reset(&htl); if (channel->cipher) g_string_append_printf(s, _("<br><b>Channel Cipher:</b> %s"), channel->cipher); if (channel->hmac) /* Definition of HMAC: http://en.wikipedia.org/wiki/HMAC */ g_string_append_printf(s, _("<br><b>Channel HMAC:</b> %s"), channel->hmac); if (channel->topic) { tmp2 = g_markup_escape_text(channel->topic, -1); g_string_append_printf(s, _("<br><b>Channel Topic:</b><br>%s"), tmp2); g_free(tmp2); } if (channel->mode) { g_string_append_printf(s, _("<br><b>Channel Modes:</b> ")); silcpurple_get_chmode_string(channel->mode, tmp, sizeof(tmp)); g_string_append(s, tmp); } if (channel->founder_key) { char *fingerprint, *babbleprint; unsigned char *pk; SilcUInt32 pk_len; pk = silc_pkcs_public_key_encode(channel->founder_key, &pk_len); if (pk) { fingerprint = silc_hash_fingerprint(NULL, pk, pk_len); babbleprint = silc_hash_babbleprint(NULL, pk, pk_len); g_string_append_printf(s, _("<br><b>Founder Key Fingerprint:</b><br>%s"), fingerprint); g_string_append_printf(s, _("<br><b>Founder Key Babbleprint:</b><br>%s"), babbleprint); silc_free(fingerprint); silc_free(babbleprint); silc_free(pk); } } purple_notify_formatted(gc, NULL, _("Channel Information"), NULL, s->str, NULL, NULL); g_string_free(s, TRUE); }
void silcpurple_show_public_key(SilcPurple sg, const char *name, SilcPublicKey public_key, GCallback callback, void *context) { SilcPublicKeyIdentifier ident; SilcSILCPublicKey silc_pubkey; char *fingerprint, *babbleprint; unsigned char *pk; SilcUInt32 pk_len, key_len = 0; GString *s; /* We support showing only SILC public keys for now */ if (silc_pkcs_get_type(public_key) != SILC_PKCS_SILC) return; silc_pubkey = silc_pkcs_get_context(SILC_PKCS_SILC, public_key); ident = &silc_pubkey->identifier; key_len = silc_pkcs_public_key_get_len(public_key); pk = silc_pkcs_public_key_encode(public_key, &pk_len); if (!pk) return; fingerprint = silc_hash_fingerprint(NULL, pk, pk_len); babbleprint = silc_hash_babbleprint(NULL, pk, pk_len); if (!fingerprint || !babbleprint) return; s = g_string_new(""); if (ident->realname) /* Hint for translators: Please check the tabulator width here and in the next strings (short strings: 2 tabs, longer strings 1 tab, sum: 3 tabs or 24 characters) */ g_string_append_printf(s, _("Real Name: \t%s\n"), ident->realname); if (ident->username) g_string_append_printf(s, _("User Name: \t%s\n"), ident->username); if (ident->email) g_string_append_printf(s, _("Email: \t\t%s\n"), ident->email); if (ident->host) g_string_append_printf(s, _("Host Name: \t%s\n"), ident->host); if (ident->org) g_string_append_printf(s, _("Organization: \t%s\n"), ident->org); if (ident->country) g_string_append_printf(s, _("Country: \t%s\n"), ident->country); g_string_append_printf(s, _("Algorithm: \t%s\n"), silc_pubkey->pkcs->name); g_string_append_printf(s, _("Key Length: \t%d bits\n"), (int)key_len); if (ident->version) g_string_append_printf(s, _("Version: \t%s\n"), ident->version); g_string_append_printf(s, "\n"); g_string_append_printf(s, _("Public Key Fingerprint:\n%s\n\n"), fingerprint); g_string_append_printf(s, _("Public Key Babbleprint:\n%s"), babbleprint); purple_request_action(sg->gc, _("Public Key Information"), _("Public Key Information"), s->str, 0, purple_connection_get_account(sg->gc), NULL, NULL, context, 1, _("Close"), callback); g_string_free(s, TRUE); silc_free(fingerprint); silc_free(babbleprint); silc_free(pk); }
void silcpurple_verify_public_key(SilcClient client, SilcClientConnection conn, const char *name, SilcConnectionType conn_type, SilcPublicKey public_key, SilcVerifyPublicKey completion, void *context) { PurpleConnection *gc = client->application; int i; char file[256], filename[256], filename2[256], *ipf, *hostf = NULL; char *fingerprint, *babbleprint; struct passwd *pw; struct stat st; char *entity = ((conn_type == SILC_CONN_SERVER || conn_type == SILC_CONN_ROUTER) ? "server" : "client"); PublicKeyVerify verify; const char *ip, *hostname; SilcUInt16 port; unsigned char *pk; SilcUInt32 pk_len; if (silc_pkcs_get_type(public_key) != SILC_PKCS_SILC) { purple_notify_error(gc, _("Verify Public Key"), _("Unsupported public key type"), NULL); if (completion) completion(FALSE, context); return; } pw = getpwuid(getuid()); if (!pw) { if (completion) completion(FALSE, context); return; } memset(filename, 0, sizeof(filename)); memset(filename2, 0, sizeof(filename2)); memset(file, 0, sizeof(file)); silc_socket_stream_get_info(silc_packet_stream_get_stream(conn->stream), NULL, &hostname, &ip, &port); pk = silc_pkcs_public_key_encode(public_key, &pk_len); if (!pk) { if (completion) completion(FALSE, context); return; } if (conn_type == SILC_CONN_SERVER || conn_type == SILC_CONN_ROUTER) { if (!name) { g_snprintf(file, sizeof(file) - 1, "%skey_%s_%d.pub", entity, ip, port); g_snprintf(filename, sizeof(filename) - 1, "%s" G_DIR_SEPARATOR_S "%skeys" G_DIR_SEPARATOR_S "%s", silcpurple_silcdir(), entity, file); g_snprintf(file, sizeof(file) - 1, "%skey_%s_%d.pub", entity, hostname, port); g_snprintf(filename2, sizeof(filename2) - 1, "%s" G_DIR_SEPARATOR_S "%skeys" G_DIR_SEPARATOR_S "%s", silcpurple_silcdir(), entity, file); ipf = filename; hostf = filename2; } else { g_snprintf(file, sizeof(file) - 1, "%skey_%s_%d.pub", entity, name, port); g_snprintf(filename, sizeof(filename) - 1, "%s" G_DIR_SEPARATOR_S "%skeys" G_DIR_SEPARATOR_S "%s", silcpurple_silcdir(), entity, file); ipf = filename; } } 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] = '_'; g_snprintf(file, sizeof(file) - 1, "%skey_%s.pub", entity, fingerprint); g_snprintf(filename, sizeof(filename) - 1, "%s" G_DIR_SEPARATOR_S "%skeys" G_DIR_SEPARATOR_S "%s", silcpurple_silcdir(), entity, file); silc_free(fingerprint); ipf = filename; } verify = silc_calloc(1, sizeof(*verify)); if (!verify) return; verify->client = client; verify->conn = conn; verify->filename = g_strdup(ipf); verify->entity = g_strdup(entity); verify->entity_name = (conn_type != SILC_CONN_CLIENT ? (name ? g_strdup(name) : g_strdup(hostname)) : NULL); verify->public_key = silc_pkcs_public_key_copy(public_key); verify->completion = completion; verify->context = context; fingerprint = verify->fingerprint = silc_hash_fingerprint(NULL, pk, pk_len); babbleprint = verify->babbleprint = silc_hash_babbleprint(NULL, pk, pk_len); /* Check whether this key already exists */ if (g_stat(ipf, &st) < 0 && (!hostf || g_stat(hostf, &st) < 0)) { /* Key does not exist, ask user to verify the key and save it */ silcpurple_verify_ask(name ? name : entity, fingerprint, babbleprint, verify); return; } else { /* The key already exists, verify it. */ SilcPublicKey public_key; unsigned char *encpk; SilcUInt32 encpk_len; /* Load the key file, try for both IP filename and hostname filename */ if (!silc_pkcs_load_public_key(ipf, &public_key) && (!hostf || (!silc_pkcs_load_public_key(hostf, &public_key)))) { silcpurple_verify_ask(name ? name : entity, fingerprint, babbleprint, verify); return; } /* Encode the key data */ encpk = silc_pkcs_public_key_encode(public_key, &encpk_len); if (!encpk) { silcpurple_verify_ask(name ? name : entity, fingerprint, babbleprint, verify); return; } /* Compare the keys */ if (memcmp(encpk, pk, encpk_len)) { /* Ask user to verify the key and save it */ verify->changed = TRUE; silcpurple_verify_ask(name ? name : entity, fingerprint, babbleprint, verify); return; } /* Local copy matched */ if (completion) completion(TRUE, context); g_free(verify->filename); g_free(verify->entity); g_free(verify->entity_name); silc_free(verify->fingerprint); silc_free(verify->babbleprint); silc_pkcs_public_key_free(verify->public_key); silc_free(verify); } }
static void silcpurple_add_buddy_i(PurpleConnection *gc, PurpleBuddy *b, gboolean init) { SilcPurple sg = gc->proto_data; SilcClient client = sg->client; SilcClientConnection conn = sg->conn; SilcPurpleBuddyRes r; SilcBuffer attrs; const char *filename, *name = b->name; r = silc_calloc(1, sizeof(*r)); if (!r) return; r->client = client; r->conn = conn; r->b = b; r->init = init; /* See if we have this buddy's public key. If we do use that to search the details. */ filename = purple_blist_node_get_string((PurpleBlistNode *)b, "public-key"); if (filename) { SilcPublicKey public_key; SilcAttributeObjPk userpk; if (!silc_pkcs_load_public_key(filename, &public_key, SILC_PKCS_FILE_PEM) && !silc_pkcs_load_public_key(filename, &public_key, SILC_PKCS_FILE_BIN)) return; /* Get all attributes, and use the public key to search user */ name = NULL; attrs = silc_client_attributes_request(SILC_ATTRIBUTE_USER_INFO, SILC_ATTRIBUTE_SERVICE, SILC_ATTRIBUTE_STATUS_MOOD, SILC_ATTRIBUTE_STATUS_FREETEXT, SILC_ATTRIBUTE_STATUS_MESSAGE, SILC_ATTRIBUTE_PREFERRED_LANGUAGE, SILC_ATTRIBUTE_PREFERRED_CONTACT, SILC_ATTRIBUTE_TIMEZONE, SILC_ATTRIBUTE_GEOLOCATION, #ifdef SILC_ATTRIBUTE_USER_ICON SILC_ATTRIBUTE_USER_ICON, #endif SILC_ATTRIBUTE_DEVICE_INFO, 0); userpk.type = "silc-rsa"; userpk.data = silc_pkcs_public_key_encode(public_key, &userpk.data_len); attrs = silc_attribute_payload_encode(attrs, SILC_ATTRIBUTE_USER_PUBLIC_KEY, SILC_ATTRIBUTE_FLAG_VALID, &userpk, sizeof(userpk)); silc_free(userpk.data); silc_pkcs_public_key_free(public_key); r->pubkey_search = TRUE; } else { /* Get all attributes */ attrs = silc_client_attributes_request(0); } /* Resolve */ silc_client_get_clients_whois(client, conn, name, NULL, attrs, silcpurple_add_buddy_resolved, r); silc_buffer_free(attrs); }
static void silcpurple_add_buddy_resolved(SilcClient client, SilcClientConnection conn, SilcClientEntry *clients, SilcUInt32 clients_count, void *context) { SilcPurpleBuddyRes r = context; PurpleBuddy *b = r->b; SilcAttributePayload pub; SilcAttributeObjPk userpk; unsigned char *pk; SilcUInt32 pk_len; const char *filename; filename = purple_blist_node_get_string((PurpleBlistNode *)b, "public-key"); /* If the buddy is offline/nonexistent, we will require user to associate a public key with the buddy or the buddy cannot be added. */ if (!clients_count) { if (r->init) { silc_free(r); return; } r->offline = TRUE; /* If the user has already associated a public key, try loading it * before prompting the user to load it again */ if (filename != NULL) silcpurple_add_buddy_ask_import(r, filename); else silcpurple_add_buddy_ask_pk(r); return; } /* If more than one client was found with nickname, we need to verify from user which one is the correct. */ if (clients_count > 1 && !r->pubkey_search) { if (r->init) { silc_free(r); return; } silcpurple_add_buddy_select(r, clients, clients_count); return; } /* If we searched using public keys and more than one entry was found the same person is logged on multiple times. */ if (clients_count > 1 && r->pubkey_search && b->name) { if (r->init) { /* Find the entry that closest matches to the buddy nickname. */ int i; for (i = 0; i < clients_count; i++) { if (!g_ascii_strncasecmp(b->name, clients[i]->nickname, strlen(b->name))) { clients[0] = clients[i]; break; } } } else { /* Verify from user which one is correct */ silcpurple_add_buddy_select(r, clients, clients_count); return; } } /* The client was found. Now get its public key and verify that before adding the buddy. */ memset(&userpk, 0, sizeof(userpk)); b->proto_data = silc_memdup(clients[0]->id, sizeof(*clients[0]->id)); r->client_id = *clients[0]->id; /* Get the public key from attributes, if not present then resolve it with GETKEY unless we have it cached already. */ if (clients[0]->attrs && !clients[0]->public_key) { pub = silcpurple_get_attr(clients[0]->attrs, SILC_ATTRIBUTE_USER_PUBLIC_KEY); if (!pub || !silc_attribute_get_object(pub, (void *)&userpk, sizeof(userpk))) { /* Get public key with GETKEY */ silc_client_command_call(client, conn, NULL, "GETKEY", clients[0]->nickname, NULL); silc_client_command_pending(conn, SILC_COMMAND_GETKEY, conn->cmd_ident, (SilcCommandCb)silcpurple_add_buddy_getkey_cb, r); return; } if (!silc_pkcs_public_key_decode(userpk.data, userpk.data_len, &clients[0]->public_key)) return; silc_free(userpk.data); } else if (filename && !clients[0]->public_key) { if (!silc_pkcs_load_public_key(filename, &clients[0]->public_key, SILC_PKCS_FILE_PEM) && !silc_pkcs_load_public_key(filename, &clients[0]->public_key, SILC_PKCS_FILE_BIN)) { /* Get public key with GETKEY */ silc_client_command_call(client, conn, NULL, "GETKEY", clients[0]->nickname, NULL); silc_client_command_pending(conn, SILC_COMMAND_GETKEY, conn->cmd_ident, (SilcCommandCb)silcpurple_add_buddy_getkey_cb, r); return; } } else if (!clients[0]->public_key) { /* Get public key with GETKEY */ silc_client_command_call(client, conn, NULL, "GETKEY", clients[0]->nickname, NULL); silc_client_command_pending(conn, SILC_COMMAND_GETKEY, conn->cmd_ident, (SilcCommandCb)silcpurple_add_buddy_getkey_cb, r); return; } /* We have the public key, verify it. */ pk = silc_pkcs_public_key_encode(clients[0]->public_key, &pk_len); silcpurple_verify_public_key(client, conn, clients[0]->nickname, SILC_SOCKET_TYPE_CLIENT, pk, pk_len, SILC_SKE_PK_TYPE_SILC, silcpurple_add_buddy_save, r); silc_free(pk); }
SilcBuffer silc_client_attributes_process(SilcClient client, SilcClientConnection conn, SilcDList attrs) { SilcBuffer buffer = NULL; SilcAttrForeach f; SilcAttribute attribute; SilcAttributePayload attr; SilcAttributeObjPk pk; unsigned char sign[2048 + 1]; SilcUInt32 sign_len; SILC_LOG_DEBUG(("Process Requested Attributes")); /* If nothing is set by application assume that we don't want to use attributes, ignore the request. */ if (!conn->internal->attrs) { SILC_LOG_DEBUG(("User has not set any attributes")); return NULL; } /* Always put our public key. */ pk.type = "silc-rsa"; pk.data = silc_pkcs_public_key_encode(conn->public_key, &pk.data_len); buffer = silc_attribute_payload_encode(buffer, SILC_ATTRIBUTE_USER_PUBLIC_KEY, pk.data ? SILC_ATTRIBUTE_FLAG_VALID : SILC_ATTRIBUTE_FLAG_INVALID, &pk, sizeof(pk)); silc_free(pk.data); /* Go through all requested attributes */ f.buffer = buffer; silc_dlist_start(attrs); while ((attr = silc_dlist_get(attrs)) != SILC_LIST_END) { /* Put all attributes of this type */ attribute = silc_attribute_get_attribute(attr); /* Ignore signature since we will compute it later */ if (attribute == SILC_ATTRIBUTE_USER_DIGITAL_SIGNATURE) continue; silc_hash_table_find_foreach(conn->internal->attrs, SILC_32_TO_PTR(attribute), silc_client_attributes_process_foreach, &f); } buffer = f.buffer; /* Finally compute the digital signature of all the data we provided. */ if (silc_pkcs_sign(conn->private_key, silc_buffer_data(buffer), silc_buffer_len(buffer), sign, sizeof(sign), &sign_len, TRUE, conn->internal->sha1hash)) { pk.type = NULL; pk.data = sign; pk.data_len = sign_len; buffer = silc_attribute_payload_encode(buffer, SILC_ATTRIBUTE_USER_DIGITAL_SIGNATURE, SILC_ATTRIBUTE_FLAG_VALID, &pk, sizeof(pk)); } return buffer; }