Beispiel #1
0
SilcServerEntry
silc_idlist_find_server_by_conn(SilcIDList id_list, char *hostname,
				int port, SilcBool registered,
				SilcIDCacheEntry *ret_entry)
{
  SilcList list;
  SilcIDCacheEntry id_cache = NULL;
  SilcServerEntry server = NULL;
  SilcPacketStream sock;
  const char *host = NULL, *ip = NULL;

  SILC_LOG_DEBUG(("Server by hostname %s and port %d", hostname, port));

  if (!silc_idcache_get_all(id_list->servers, &list))
    return NULL;

  silc_list_start(list);
  while ((id_cache = silc_list_get(list))) {
    server = id_cache->context;
    sock = server->connection;

    if (sock && silc_socket_stream_get_info(
			    silc_packet_stream_get_stream(sock),
			    NULL, &host, &ip, NULL)) {
      if (((host && !strcasecmp(host, hostname)) ||
	   (ip && !strcasecmp(ip, hostname))) &&
	  server->id->port == SILC_SWAB_16(port))
	break;
    }

    id_cache = NULL;
    server = NULL;
  }

  if (server && registered &&
      !(server->data.status & SILC_IDLIST_STATUS_REGISTERED))
    return NULL;

  if (ret_entry)
    *ret_entry = id_cache;

  SILC_LOG_DEBUG(("Found"));

  return server;
}
Beispiel #2
0
SilcAsyncOperation
silc_client_key_exchange(SilcClient client,
			 SilcClientConnectionParams *params,
			 SilcPublicKey public_key,
			 SilcPrivateKey private_key,
			 SilcStream stream,
			 SilcConnectionType conn_type,
			 SilcClientConnectCallback callback,
			 void *context)
{
  SilcClientConnection conn;
  const char *host;
  SilcUInt16 port;

  SILC_LOG_DEBUG(("Performing key exchange"));

  if (!client || !stream)
    return NULL;

  if (client->internal->run_callback) {
    SILC_LOG_ERROR(("Client library is not started yet. SilcClientRunning "
		    "callback has not been called yet."));
    return NULL;
  }

  if (!silc_socket_stream_get_info(stream, NULL, &host, NULL, &port)) {
    SILC_LOG_ERROR(("Socket stream does not have remote host name set"));
    callback(client, NULL, SILC_CLIENT_CONN_ERROR, 0, NULL, context);
    return NULL;
  }

  /* Add new connection */
  conn = silc_client_add_connection(client, conn_type, TRUE, params,
				    public_key, private_key,
				    (char *)host, port, callback, context);
  if (!conn) {
    callback(client, NULL, SILC_CLIENT_CONN_ERROR, 0, NULL, context);
    return NULL;
  }
  conn->internal->user_stream = stream;

  /* Signal connection to start key exchange */
  conn->internal->key_exchange = TRUE;
  return conn->internal->cop;
}
Beispiel #3
0
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);
	}
}
Beispiel #4
0
SilcClientListener
silc_client_listener_add(SilcClient client,
			 SilcSchedule schedule,
			 SilcClientConnectionParams *params,
			 SilcPublicKey public_key,
			 SilcPrivateKey private_key,
			 SilcClientConnectCallback callback,
			 void *context)
{
  SilcClientListener listener;
  SilcStream stream;

  if (!client || !schedule ||
      !params || (!params->local_ip && !params->bind_ip))
    return NULL;

  SILC_LOG_DEBUG(("Adding new listener"));

  listener = silc_calloc(1, sizeof(*listener));
  if (!listener)
    return NULL;
  listener->client = client;
  listener->schedule = schedule;
  listener->callback = callback;
  listener->context = context;
  listener->params = *params;
  listener->public_key = public_key;
  listener->private_key = private_key;

  /* Create network listener */
  if (params->udp) {
    /* UDP listener */
    stream = silc_net_udp_connect(params->bind_ip ? params->bind_ip :
				  params->local_ip, params->local_port,
				  NULL, 0, schedule);
    listener->udp_listener =
      silc_packet_stream_create(client->internal->packet_engine,
				schedule, stream);
    if (!listener->udp_listener) {
      client->internal->ops->say(
		     client, NULL, SILC_CLIENT_MESSAGE_ERROR,
		     "Cannot create UDP listener on %s on port %d: %s",
		     params->bind_ip ? params->bind_ip :
		     params->local_ip, params->local_port, strerror(errno));
      silc_client_listener_free(listener);
      if (stream)
	silc_stream_destroy(stream);
      return NULL;
    }
    silc_packet_stream_link(listener->udp_listener,
			    &silc_client_listener_stream_cb, listener,
			    1000000, SILC_PACKET_ANY, -1);

    if (!params->local_port) {
      /* Get listener port */
      SilcSocket sock;
      silc_socket_stream_get_info(stream, &sock, NULL, NULL, NULL);
      listener->params.local_port = silc_net_get_local_port(sock);
    }
  } else {
    /* TCP listener */
    listener->tcp_listener =
      silc_net_tcp_create_listener(params->bind_ip ?
				   (const char **)&params->bind_ip :
				   (const char **)&params->local_ip,
				   1, params->local_port, TRUE, FALSE,
				   schedule, silc_client_listener_tcp_accept,
				   listener);
    if (!listener->tcp_listener) {
      client->internal->ops->say(
		     client, NULL, SILC_CLIENT_MESSAGE_ERROR,
		     "Cannot create listener on %s on port %d: %s",
		     params->bind_ip ? params->bind_ip :
		     params->local_ip, params->local_port, strerror(errno));

      silc_client_listener_free(listener);
      return NULL;
    }

    if (!params->local_port) {
      /* Get listener port */
      SilcUInt16 *ports;
      ports = silc_net_listener_get_port(listener->tcp_listener, NULL);
      listener->params.local_port = ports[0];
      silc_free(ports);
    }
  }

  SILC_LOG_DEBUG(("Bound listener to %s:%d",
		  params->bind_ip ? params->bind_ip : params->local_ip,
		  listener->params.local_port));

  return listener;
}
Beispiel #5
0
static void
silc_client_listener_new_connection(SilcClientListener listener,
				    SilcPacketStream stream)
{
  SilcClient client = listener->client;
  SilcClientConnection conn;
  SilcSKEParamsStruct params;
  const char *hostname = NULL, *ip = NULL;
  SilcUInt16 port;

  /* Get remote information */
  silc_socket_stream_get_info(silc_packet_stream_get_stream(stream),
			      NULL, &hostname, &ip, &port);
  if (!ip || !port) {
    listener->callback(client, NULL, SILC_CLIENT_CONN_ERROR, 0, NULL,
		       listener->context);
    silc_packet_stream_destroy(stream);
    return;
  }
  if (!hostname)
    hostname = ip;

  /* Add new connection */
  conn = silc_client_add_connection(client, SILC_CONN_CLIENT, FALSE,
				    &listener->params,
				    listener->public_key,
				    listener->private_key,
				    (char *)hostname, port,
				    listener->callback, listener->context);
  if (!conn) {
    listener->callback(client, NULL, SILC_CLIENT_CONN_ERROR, 0, NULL,
		       listener->context);
    silc_packet_stream_destroy(stream);
    return;
  }
  conn->stream = stream;
  conn->internal->schedule = listener->schedule;
  silc_packet_set_context(conn->stream, conn);

  SILC_LOG_DEBUG(("Processing new incoming connection %p", conn));

  /* Allocate SKE */
  conn->internal->ske =
    silc_ske_alloc(client->rng, conn->internal->schedule,
		   listener->params.repository, listener->public_key,
		   listener->private_key, listener);
  if (!conn->internal->ske) {
    conn->callback(conn->client, conn, SILC_CLIENT_CONN_ERROR, 0, NULL,
		   conn->callback_context);
    return;
  }

  /* Set SKE parameters */
  params.version = client->internal->silc_client_version;
  params.flags = SILC_SKE_SP_FLAG_MUTUAL;
  if (listener->params.udp) {
    params.flags |= SILC_SKE_SP_FLAG_IV_INCLUDED;
    params.session_port = listener->params.local_port;
  }

  silc_ske_set_callbacks(conn->internal->ske, silc_client_listener_verify_key,
			 silc_client_listener_completion, conn);

  /* Start key exchange as responder */
  conn->internal->op = silc_ske_responder(conn->internal->ske,
					  conn->stream, &params);
  if (!conn->internal->op)
    conn->callback(conn->client, conn, SILC_CLIENT_CONN_ERROR, 0, NULL,
		   conn->callback_context);
}
Beispiel #6
0
static char
silc_server_command_reply_whowas_save(SilcServerCommandReplyContext cmd)
{
  SilcServer server = cmd->server;
  SilcUInt32 len, id_len;
  unsigned char *id_data;
  char *nickname, *username, *realname;
  SilcID id;
  SilcClientEntry client;
  SilcIDCacheEntry cache = NULL;
  char nick[128 + 1], servername[256 + 1], uname[128 + 1];
  int global = FALSE;
  const char *hostname, *ip;

  silc_socket_stream_get_info(silc_packet_stream_get_stream(cmd->sock),
			      NULL, &hostname, &ip, NULL);

  id_data = silc_argument_get_arg_type(cmd->args, 2, &id_len);
  nickname = silc_argument_get_arg_type(cmd->args, 3, &len);
  username = silc_argument_get_arg_type(cmd->args, 4, &len);
  if (!id_data || !nickname || !username)
    return FALSE;

  realname = silc_argument_get_arg_type(cmd->args, 5, &len);

  if (!silc_id_payload_parse_id(id_data, id_len, &id))
    return FALSE;

  /* Check if we have this client cached already. */

  client = silc_idlist_find_client_by_id(server->local_list,
					 SILC_ID_GET_ID(id),
					 FALSE, &cache);
  if (!client) {
    client = silc_idlist_find_client_by_id(server->global_list,
					   SILC_ID_GET_ID(id),
					   FALSE, &cache);
    global = TRUE;
  }

  if (!client) {
    /* If router did not find such Client ID in its lists then this must
       be bogus client or some router in the net is buggy. */
    if (server->server_type != SILC_SERVER)
      return FALSE;

    /* Take hostname out of nick string if it includes it. */
    silc_parse_userfqdn(nickname, nick, sizeof(nick), servername,
			sizeof(servername));

    /* We don't have that client anywhere, add it. The client is added
       to global list since server didn't have it in the lists so it must be
       global. */
    client = silc_idlist_add_client(server->global_list,
				    strdup(nick), username,
				    strdup(realname),
				    silc_id_dup(SILC_ID_GET_ID(id),
						SILC_ID_CLIENT),
				    silc_packet_get_context(cmd->sock), NULL);
    if (!client) {
      SILC_LOG_ERROR(("Could not add new client to the ID Cache"));
      return FALSE;
    }

    client->data.status |= SILC_IDLIST_STATUS_RESOLVED;
    client->data.status &= ~SILC_IDLIST_STATUS_RESOLVING;
    client->data.status &= ~SILC_IDLIST_STATUS_REGISTERED;
    client->servername = servername[0] ? strdup(servername) : NULL;
  } else {
    /* We have the client already, update the data */

    /* Check nickname */
    silc_parse_userfqdn(nickname, nick, sizeof(nick), servername,
			sizeof(servername));
    nickname = silc_identifier_check(nick, strlen(nick), SILC_STRING_UTF8,
				     128, NULL);
    if (!nickname) {
      SILC_LOG_ERROR(("Malformed nickname '%s' received in WHOWAS reply "
		      "from %s",
		      nick, hostname ? hostname : ""));
      return FALSE;
    }

    /* Check username */
    silc_parse_userfqdn(username, uname, sizeof(uname), NULL, 0);
    if (!silc_identifier_verify(uname, strlen(uname), SILC_STRING_UTF8, 128))
      return FALSE;

    silc_free(client->nickname);
    silc_free(client->username);
    silc_free(client->servername);

    client->nickname = strdup(nick);
    client->username = strdup(username);
    client->servername = servername[0] ? strdup(servername) : NULL;
    client->data.status |= SILC_IDLIST_STATUS_RESOLVED;
    client->data.status &= ~SILC_IDLIST_STATUS_RESOLVING;
    client->data.status &= ~SILC_IDLIST_STATUS_REGISTERED;

    /* Update cache entry */
    silc_idcache_update_by_context(global ? server->global_list->clients :
				   server->local_list->clients, client, NULL,
				   nickname, TRUE);
  }

  /* If client is global and is not on any channel then add that we'll
     expire the entry after a while. */
  if (global) {
    client = silc_idlist_find_client_by_id(server->global_list, client->id,
					   FALSE, &cache);
    if (client && !silc_hash_table_count(client->channels)) {
      client->data.created = silc_time();
      silc_dlist_del(server->expired_clients, client);
      silc_dlist_add(server->expired_clients, client);
    }
  }

  return TRUE;
}
Beispiel #7
0
static char
silc_server_command_reply_whois_save(SilcServerCommandReplyContext cmd)
{
  SilcServer server = cmd->server;
  unsigned char *id_data, *umodes;
  char *nickname, *username, *realname, *tmp;
  unsigned char *fingerprint;
  SilcID id;
  SilcClientEntry client;
  char global = FALSE;
  char nick[128 + 1], servername[256 + 1], uname[128 + 1];
  SilcUInt32 mode = 0, len, len2, id_len, flen;
  const char *hostname, *ip;

  silc_socket_stream_get_info(silc_packet_stream_get_stream(cmd->sock),
			      NULL, &hostname, &ip, NULL);

  id_data = silc_argument_get_arg_type(cmd->args, 2, &id_len);
  nickname = silc_argument_get_arg_type(cmd->args, 3, &len);
  username = silc_argument_get_arg_type(cmd->args, 4, &len);
  realname = silc_argument_get_arg_type(cmd->args, 5, &len);
  if (!id_data || !nickname || !username || !realname)
    return FALSE;

  tmp = silc_argument_get_arg_type(cmd->args, 7, &len);
  if (tmp)
    SILC_GET32_MSB(mode, tmp);

  if (!silc_id_payload_parse_id(id_data, id_len, &id))
    return FALSE;

  fingerprint = silc_argument_get_arg_type(cmd->args, 9, &flen);

  /* Check if we have this client cached already. */

  client = silc_idlist_find_client_by_id(server->local_list,
					 SILC_ID_GET_ID(id),
					 FALSE, NULL);
  if (!client) {
    client = silc_idlist_find_client_by_id(server->global_list,
					   SILC_ID_GET_ID(id),
					   FALSE, NULL);
    global = TRUE;
  }

  if (!client) {
    /* If router did not find such Client ID in its lists then this must
       be bogus client or some router in the net is buggy. */
    if (server->server_type != SILC_SERVER)
      return FALSE;

    /* Take hostname out of nick string if it includes it. */
    silc_parse_userfqdn(nickname, nick, sizeof(nick), servername,
			sizeof(servername));

    /* We don't have that client anywhere, add it. The client is added
       to global list since server didn't have it in the lists so it must be
       global. This will check for valid nickname and username strings. */
    client = silc_idlist_add_client(server->global_list,
				    strdup(nick), username,
				    strdup(realname),
				    silc_id_dup(SILC_ID_GET_ID(id),
						SILC_ID_CLIENT),
				    silc_packet_get_context(cmd->sock),
				    NULL);
    if (!client) {
      SILC_LOG_ERROR(("Could not add new client to the ID Cache"));
      return FALSE;
    }

    client->data.status |=
      (SILC_IDLIST_STATUS_REGISTERED | SILC_IDLIST_STATUS_RESOLVED);
    client->data.status &= ~SILC_IDLIST_STATUS_RESOLVING;
    client->mode = mode;
    client->servername = servername[0] ? strdup(servername) : NULL;

    SILC_LOG_DEBUG(("stat.clients %d->%d", server->stat.clients,
		    server->stat.clients + 1));
    server->stat.clients++;
  } else {
    /* We have the client already, update the data */

    SILC_LOG_DEBUG(("Updating client data"));

    /* Check nickname */
    silc_parse_userfqdn(nickname, nick, sizeof(nick), servername,
			sizeof(servername));
    nickname = silc_identifier_check(nick, strlen(nick), SILC_STRING_UTF8,
				     128, NULL);
    if (!nickname) {
      SILC_LOG_ERROR(("Malformed nickname '%s' received in WHOIS reply "
		      "from %s",
		      hostname ? hostname : "", nick));
      return FALSE;
    }

    /* Check username */
    silc_parse_userfqdn(username, uname, sizeof(uname), NULL, 0);
    if (!silc_identifier_verify(uname, strlen(uname), SILC_STRING_UTF8, 128)) {
      SILC_LOG_ERROR(("Malformed username '%s' received in WHOIS reply "
		      "from %s",
		      hostname ? hostname : "", tmp));
      return FALSE;
    }

    /* Update entry */
    silc_idcache_update_by_context(global ? server->global_list->clients :
				   server->local_list->clients, client, NULL,
				   nickname, TRUE);

    silc_free(client->nickname);
    silc_free(client->username);
    silc_free(client->userinfo);
    silc_free(client->servername);

    client->nickname = strdup(nick);
    client->username = strdup(username);
    client->userinfo = strdup(realname);
    client->servername = servername[0] ? strdup(servername) : NULL;
    client->mode = mode;
    client->data.status |= SILC_IDLIST_STATUS_RESOLVED;
    client->data.status &= ~SILC_IDLIST_STATUS_RESOLVING;
  }

  /* Save channel list if it was sent to us */
  if (server->server_type == SILC_SERVER) {
    tmp = silc_argument_get_arg_type(cmd->args, 6, &len);
    umodes = silc_argument_get_arg_type(cmd->args, 10, &len2);
    if (tmp && umodes) {
      SilcBufferStruct channels_buf, umodes_buf;
      silc_buffer_set(&channels_buf, tmp, len);
      silc_buffer_set(&umodes_buf, umodes, len2);
      silc_server_save_user_channels(server, cmd->sock, client, &channels_buf,
				     &umodes_buf);
    } else {
      silc_server_save_user_channels(server, cmd->sock, client, NULL, NULL);
    }
  }

  if (fingerprint && flen == sizeof(client->data.fingerprint))
    memcpy(client->data.fingerprint, fingerprint, flen);

  /* Take Requested Attributes if set. */
  tmp = silc_argument_get_arg_type(cmd->args, 11, &len);
  if (tmp) {
    silc_free(client->attrs);
    client->attrs = silc_memdup(tmp, len);
    client->attrs_len = len;

    /* Try to take public key from attributes if present and we don't have
       the key already.  Do this only on normal server.  Routers do GETKEY
       for all clients anyway. */
    if (server->server_type != SILC_ROUTER && !client->data.public_key) {
      SilcAttributePayload attr;
      SilcAttributeObjPk pk;
      unsigned char f[SILC_HASH_MAXLEN];
      SilcDList attrs = silc_attribute_payload_parse(tmp, len);

      SILC_LOG_DEBUG(("Take client public key from attributes"));

      if (attrs) {
	silc_dlist_start(attrs);
	while ((attr = silc_dlist_get(attrs)) != SILC_LIST_END) {
	  if (silc_attribute_get_attribute(attr) ==
	      SILC_ATTRIBUTE_USER_PUBLIC_KEY) {

	    if (!silc_attribute_get_object(attr, &pk, sizeof(pk)))
	      continue;

	    /* Take only SILC public keys */
	    if (strcmp(pk.type, "silc-rsa")) {
	      silc_free(pk.type);
	      silc_free(pk.data);
	      continue;
	    }

	    /* Verify that the server provided fingerprint matches the key */
	    silc_hash_make(server->sha1hash, pk.data, pk.data_len, f);
	    if (memcmp(f, client->data.fingerprint, sizeof(f))) {
	      silc_free(pk.type);
	      silc_free(pk.data);
	      continue;
	    }

	    /* Save the public key. */
	    if (!silc_pkcs_public_key_alloc(SILC_PKCS_SILC,
					    pk.data, pk.data_len,
					    &client->data.public_key)) {
	      silc_free(pk.type);
	      silc_free(pk.data);
	      continue;
	    }

	    SILC_LOG_DEBUG(("Saved client public key from attributes"));

	    /* Add client's public key to repository */
	    if (!silc_server_get_public_key_by_client(server, client, NULL))
	      silc_skr_add_public_key_simple(server->repository,
					     client->data.public_key,
					     SILC_SKR_USAGE_IDENTIFICATION,
					     client, NULL);

	    silc_free(pk.type);
	    silc_free(pk.data);
	    break;
	  }
	}

	silc_attribute_payload_list_free(attrs);
      }
    }
  }

  return TRUE;
}