Пример #1
0
SilcBool silc_mime_add_multipart(SilcMime mime, SilcMime part)
{
    if (!mime || !mime->multiparts || !part)
        return FALSE;

    silc_dlist_add(mime->multiparts, part);
    return TRUE;
}
Пример #2
0
SilcDList silc_attribute_payload_parse(const unsigned char *payload,
				       SilcUInt32 payload_len)
{
  SilcBufferStruct buffer;
  SilcDList list;
  SilcAttributePayload newp;
  SilcUInt32 len;
  int ret;

  SILC_LOG_DEBUG(("Parsing Attribute Payload list"));

  silc_buffer_set(&buffer, (unsigned char *)payload, payload_len);
  list = silc_dlist_init();

  while (silc_buffer_len(&buffer)) {
    newp = silc_calloc(1, sizeof(*newp));
    if (!newp)
      goto err;
    ret = silc_buffer_unformat(&buffer,
			       SILC_STR_UI_CHAR(&newp->attribute),
			       SILC_STR_UI_CHAR(&newp->flags),
			       SILC_STR_UI16_NSTRING_ALLOC(&newp->data,
							   &newp->data_len),
			       SILC_STR_END);
    if (ret == -1)
      goto err;

    if (newp->data_len > silc_buffer_len(&buffer) - 4) {
      SILC_LOG_ERROR(("Incorrect attribute payload in list"));
      goto err;
    }

    len = 4 + newp->data_len;
    if (silc_buffer_len(&buffer) < len)
      break;
    silc_buffer_pull(&buffer, len);

    silc_dlist_add(list, newp);
  }

  return list;

 err:
  silc_attribute_payload_list_free(list);
  return NULL;
}
Пример #3
0
SilcDList silc_client_list_channel_private_keys(SilcClient client,
						SilcClientConnection conn,
						SilcChannelEntry channel)
{
  SilcChannelPrivateKey entry;
  SilcDList list;

  if (!client || !conn || !channel)
    return FALSE;

  if (!channel->internal.private_keys)
    return NULL;

  list = silc_dlist_init();
  if (!list)
    return NULL;

  silc_dlist_start(channel->internal.private_keys);
  while ((entry = silc_dlist_get(channel->internal.private_keys)))
    silc_dlist_add(list, entry);

  return list;
}
Пример #4
0
SilcClientFileError
silc_client_file_send(SilcClient client,
		      SilcClientConnection conn,
		      SilcClientEntry client_entry,
		      SilcClientConnectionParams *params,
		      SilcPublicKey public_key,
		      SilcPrivateKey private_key,
		      SilcClientFileMonitor monitor,
		      void *monitor_context,
		      const char *filepath,
		      SilcUInt32 *session_id)
{
  SilcClientFtpSession session;
  SilcBuffer keyagr;
  char *filename, *path;
  int fd;

  SILC_LOG_DEBUG(("File send request (file: %s)", filepath));

  if (!client || !client_entry || !filepath || !params ||
      !public_key || !private_key)
    return SILC_CLIENT_FILE_ERROR;

  /* Check for existing session for `filepath'. */
  silc_dlist_start(client->internal->ftp_sessions);
  while ((session = silc_dlist_get(client->internal->ftp_sessions))) {
    if (session->filepath && !strcmp(session->filepath, filepath) &&
	session->client_entry == client_entry)
      return SILC_CLIENT_FILE_ALREADY_STARTED;
  }

  /* See whether the file exists and can be opened */
  fd = silc_file_open(filepath, O_RDONLY);
  if (fd < 0)
    return SILC_CLIENT_FILE_NO_SUCH_FILE;
  silc_file_close(fd);

  /* Add new session */
  session = silc_calloc(1, sizeof(*session));
  if (!session)
    return SILC_CLIENT_FILE_ERROR;
  session->session_id = ++client->internal->next_session_id;
  session->client = client;
  session->server_conn = conn;
  session->initiator = TRUE;
  session->client_entry = silc_client_ref_client(client, conn, client_entry);
  session->monitor = monitor;
  session->monitor_context = monitor_context;
  session->filepath = strdup(filepath);
  session->params = *params;
  session->public_key = public_key;
  session->private_key = private_key;

  if (silc_asprintf(&path, "file://%s", filepath) < 0) {
    silc_free(session);
    return SILC_CLIENT_FILE_NO_MEMORY;
  }

  /* Allocate memory filesystem and put the file to it */
  if (strrchr(path, '/'))
    filename = strrchr(path, '/') + 1;
  else
    filename = (char *)path;
  session->fs = silc_sftp_fs_memory_alloc(SILC_SFTP_FS_PERM_READ |
					  SILC_SFTP_FS_PERM_EXEC);
  silc_sftp_fs_memory_add_file(session->fs, NULL, SILC_SFTP_FS_PERM_READ,
			       filename, path);

  session->filesize = silc_file_size(filepath);

  /* If local IP is provided, create listener for incoming key exchange */
  if (params->local_ip || params->bind_ip) {
    session->listener =
      silc_client_listener_add(client,
			       conn->internal->schedule,
			       params, public_key, private_key,
			       silc_client_ftp_connect_completion,
			       session);
    if (!session->listener) {
      client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_ERROR,
				 "Cannot create listener for file transfer: "
				 "%s", strerror(errno));
      silc_free(session);
      return SILC_CLIENT_FILE_NO_MEMORY;
    }

    session->hostname = (params->bind_ip ? strdup(params->bind_ip) :
			 strdup(params->local_ip));
    session->port = silc_client_listener_get_local_port(session->listener);
  }

  SILC_LOG_DEBUG(("Sending key agreement for file transfer"));

  /* Send the key agreement inside FTP packet */
  keyagr = silc_key_agreement_payload_encode(session->hostname, 0,
					     session->port);
  if (!keyagr) {
    if (session->listener)
      silc_client_listener_free(session->listener);
    silc_free(session);
    return SILC_CLIENT_FILE_NO_MEMORY;
  }
  silc_packet_send_va_ext(conn->stream, SILC_PACKET_FTP, 0, 0, NULL,
			  SILC_ID_CLIENT, &client_entry->id, NULL, NULL,
			  SILC_STR_UI_CHAR(1),
			  SILC_STR_DATA(silc_buffer_data(keyagr),
					silc_buffer_len(keyagr)),
			  SILC_STR_END);

  silc_buffer_free(keyagr);
  silc_free(path);

  silc_dlist_add(client->internal->ftp_sessions, session);
  if (session_id)
    *session_id = session->session_id;

  /* Add session request timeout */
  if (params && params->timeout_secs)
    silc_schedule_task_add_timeout(client->schedule,
				   silc_client_ftp_timeout, session,
				   params->timeout_secs, 0);

  return SILC_CLIENT_FILE_OK;
}
Пример #5
0
SilcDList silcpurple_image_message(const char *msg, SilcMessageFlags *mflags)
{
	SilcMime mime = NULL, p;
	SilcDList list, parts = NULL;
	const char *start, *end, *last;
	GData *attribs;
	char *type;
	gboolean images = FALSE;

	last = msg;
	while (last && *last && purple_markup_find_tag("img", last, &start,
						     &end, &attribs)) {
		PurpleStoredImage *image = NULL;
		const char *id;

		/* Check if there is text before image */
		if (start - last) {
			char *text, *tmp;
			p = silc_mime_alloc();

			/* Add content type */
			silc_mime_add_field(p, "Content-Type",
					    "text/plain; charset=utf-8");

			tmp = g_strndup(last, start - last);
			text = purple_unescape_html(tmp);
			g_free(tmp);

			/* Add text */
			silc_mime_add_data(p, (const unsigned char *)text, strlen(text));
			g_free(text);

			if (!parts)
				parts = silc_dlist_init();
			silc_dlist_add(parts, p);
		}

		id = g_datalist_get_data(&attribs, "id");
		if (id && (image = purple_imgstore_find_by_id(atoi(id)))) {
			unsigned long imglen = purple_imgstore_get_size(image);
			gconstpointer img = purple_imgstore_get_data(image);

			p = silc_mime_alloc();

			/* Add content type */
			type = silcpurple_file2mime(purple_imgstore_get_filename(image));
			if (!type) {
				g_datalist_clear(&attribs);
				last = end + 1;
				continue;
			}
			silc_mime_add_field(p, "Content-Type", type);
			g_free(type);

			/* Add content transfer encoding */
			silc_mime_add_field(p, "Content-Transfer-Encoding", "binary");

			/* Add image data */
			silc_mime_add_data(p, img, imglen);

			if (!parts)
				parts = silc_dlist_init();
			silc_dlist_add(parts, p);
			images = TRUE;
		}

		g_datalist_clear(&attribs);

		/* Continue after tag */
		last = end + 1;
	}

	/* Check for text after the image(s) */
	if (images && last && *last) {
		char *tmp = purple_unescape_html(last);
		p = silc_mime_alloc();

		/* Add content type */
		silc_mime_add_field(p, "Content-Type",
				    "text/plain; charset=utf-8");

		/* Add text */
		silc_mime_add_data(p, (const unsigned char *)tmp, strlen(tmp));
		g_free(tmp);

		if (!parts)
			parts = silc_dlist_init();
		silc_dlist_add(parts, p);
	}

	/* If there weren't any images, don't return anything. */
	if (!images) {
		if (parts)
			silc_dlist_uninit(parts);
		return NULL;
	}

	if (silc_dlist_count(parts) > 1) {
		/* Multipart MIME message */
		char b[32];
		mime = silc_mime_alloc();
		silc_mime_add_field(mime, "MIME-Version", "1.0");
		g_snprintf(b, sizeof(b), "b%4X%4X",
			   (unsigned int)time(NULL),
			   silc_dlist_count(parts));
		silc_mime_set_multipart(mime, "mixed", b);
		silc_dlist_start(parts);
		while ((p = silc_dlist_get(parts)) != SILC_LIST_END)
			silc_mime_add_multipart(mime, p);
	} else {
		/* Simple MIME message */
		silc_dlist_start(parts);
		mime = silc_dlist_get(parts);
		silc_mime_add_field(mime, "MIME-Version", "1.0");
	}

	*mflags &= ~SILC_MESSAGE_FLAG_UTF8;
	*mflags |= SILC_MESSAGE_FLAG_DATA;

	/* Encode message. Fragment if it is too large */
	list = silc_mime_encode_partial(mime, 0xfc00);

	silc_dlist_uninit(parts);

	/* Added multiparts gets freed here */
	silc_mime_free(mime);

	return list;
}
Пример #6
0
SilcDList silc_mime_encode_partial(SilcMime mime, int max_size)
{
    unsigned char *buf, *tmp;
    SilcUInt32 buf_len, len, tmp_len, off;
    SilcDList list;
    SilcBuffer buffer;
    SilcMime partial;
    char type[128], id[64];
    int num;

    SILC_LOG_DEBUG(("Fragmenting MIME message"));

    /* Encode as normal */
    buf = silc_mime_encode(mime, &buf_len);
    if (!buf)
        return NULL;

    list = silc_dlist_init();

    /* Fragment if it is too large */
    if (buf_len > max_size) {
        memset(id, 0, sizeof(id));
        memset(type, 0, sizeof(type));
        gethostname(type, sizeof(type) - 1);
        srand((time(NULL) + buf_len) ^ rand());
        silc_snprintf(id, sizeof(id) - 1, "%X%X%X%s",
                      (unsigned int)rand(), (unsigned int)time(NULL),
                      (unsigned int)buf_len, type);

        SILC_LOG_DEBUG(("Fragment ID %s", id));

        partial = silc_mime_alloc();
        if (!partial)
            return NULL;

        silc_mime_add_field(partial, "MIME-Version", "1.0");
        memset(type, 0, sizeof(type));
        silc_snprintf(type, sizeof(type) - 1,
                      "message/partial; id=\"%s\"; number=1", id);
        silc_mime_add_field(partial, "Content-Type", type);
        silc_mime_add_data(partial, buf, max_size);

        tmp = silc_mime_encode(partial, &tmp_len);
        if (!tmp)
            return NULL;
        silc_mime_free(partial);

        /* Add to list */
        buffer = silc_buffer_alloc_size(tmp_len);
        if (!buffer)
            return NULL;
        silc_buffer_put(buffer, tmp, tmp_len);
        silc_dlist_add(list, buffer);
        silc_free(tmp);

        len = buf_len - max_size;
        off = max_size;
        num = 2;
        while (len > 0) {
            partial = silc_mime_alloc();
            if (!partial)
                return NULL;

            memset(type, 0, sizeof(type));
            silc_mime_add_field(partial, "MIME-Version", "1.0");

            if (len > max_size) {
                silc_snprintf(type, sizeof(type) - 1,
                              "message/partial; id=\"%s\"; number=%d",
                              id, num++);
                silc_mime_add_data(partial, buf + off, max_size);
                off += max_size;
                len -= max_size;
            } else {
                silc_snprintf(type, sizeof(type) - 1,
                              "message/partial; id=\"%s\"; number=%d; total=%d",
                              id, num, num);
                silc_mime_add_data(partial, buf + off, len);
                len = 0;
            }

            silc_mime_add_field(partial, "Content-Type", type);

            tmp = silc_mime_encode(partial, &tmp_len);
            if (!tmp)
                return NULL;
            silc_mime_free(partial);

            /* Add to list */
            buffer = silc_buffer_alloc_size(tmp_len);
            if (!buffer)
                return NULL;
            silc_buffer_put(buffer, tmp, tmp_len);
            silc_dlist_add(list, buffer);
            silc_free(tmp);
        }
    } else {
        /* No need to fragment */
        buffer = silc_buffer_alloc_size(buf_len);
        if (!buffer)
            return NULL;
        silc_buffer_put(buffer, buf, buf_len);
        silc_dlist_add(list, buffer);
    }

    silc_free(buf);

    return list;
}
Пример #7
0
SilcMime silc_mime_decode(SilcMime mime, const unsigned char *data,
                          SilcUInt32 data_len)
{
    SilcMime m = NULL;
    int i, k;
    char *tmp, *field, *value, *line;

    SILC_LOG_DEBUG(("Parsing MIME message"));

    if (!data)
        return NULL;

    if (!mime) {
        mime = silc_mime_alloc();
        if (!mime)
            return NULL;
        m = mime;
    }

    /* Parse the fields */
    line = tmp = (char *)data;
    for (i = 0; i < data_len; i++) {
        /* Get field line */
        if (data_len - i >= 2 && tmp[i] == '\r' && tmp[i + 1] == '\n') {
            /* Get field */
            field = strchr(line, ':');
            if (!field)
                goto err;
            field = silc_memdup(line, field - line);
            if (!field)
                goto err;

            /* Get value. Remove whitespaces too. */
            value = strchr(line, ':');
            if ((tmp + i) - value < 2)
                goto err;
            value++;
            for (k = 0; k < (tmp + i) - value; k++) {
                if (value[k] == '\r')
                    goto err;
                if (value[k] != ' ' && value[k] != '\t')
                    break;
            }
            value += k;
            if ((tmp + i) - value < 1)
                goto err;
            value = silc_memdup(value, (tmp + i) - value);
            if (!value)
                goto err;

            SILC_LOG_DEBUG(("Header '%s' '%s'", field, value));

            /* Add field and value */
            silc_mime_add_field(mime, field, value);
            silc_free(field);
            silc_free(value);

            /* Mark start of next line */
            line = (tmp + i) + 2;
            i += 2;

            /* Break if this is last header */
            if (data_len - i >= 2 &&
                    tmp[i] == '\r' && tmp[i + 1] == '\n') {
                i += 2;
                break;
            }
        }
    }

    /* Parse multiparts if present */
    field = (char *)silc_mime_get_field(mime, "Content-Type");
    if (field && strstr(field, "multipart")) {
        char b[1024];
        SilcMime p;
        unsigned int len;

        mime->multiparts = silc_dlist_init();
        if (!mime->multiparts)
            goto err;

        /* Get multipart type */
        value = strchr(field, '/');
        if (!value)
            goto err;
        value++;
        if (strchr(field, '"'))
            value++;
        if (!strchr(field, ';'))
            goto err;
        memset(b, 0, sizeof(b));
        len = (unsigned int)(strchr(field, ';') - value);
        if (len > sizeof(b) - 1)
            goto err;
        strncpy(b, value, len);
        if (strchr(b, '"'))
            *strchr(b, '"') = '\0';
        mime->multitype = silc_memdup(b, strlen(b));

        /* Get boundary */
        value = strrchr(field, '=');
        if (value && strlen(value) > 1) {
            value++;

            SILC_LOG_DEBUG(("Boundary '%s'", value));

            memset(b, 0, sizeof(b));
            line = strdup(value);
            if (strrchr(line, '"')) {
                *strrchr(line, '"') = '\0';
                silc_snprintf(b, sizeof(b) - 1, "--%s", line + 1);
                mime->boundary = strdup(line + 1);
            } else {
                silc_snprintf(b, sizeof(b) - 1, "--%s", line);
                mime->boundary = strdup(line);
            }
            silc_free(line);

            for (i = i; i < data_len; i++) {
                /* Get boundary data */
                if (data_len - i >= strlen(b) &&
                        tmp[i] == '-' && tmp[i + 1] == '-') {
                    if (memcmp(tmp + i, b, strlen(b)))
                        continue;

                    i += strlen(b);

                    if (data_len - i >= 4 &&
                            tmp[i    ] == '\r' && tmp[i + 1] == '\n' &&
                            tmp[i + 2] == '\r' && tmp[i + 3] == '\n')
                        i += 4;
                    else if (data_len - i >= 2 &&
                             tmp[i] == '\r' && tmp[i + 1] == '\n')
                        i += 2;
                    else if (data_len - i >= 2 &&
                             tmp[i] == '-' && tmp[i + 1] == '-')
                        break;

                    line = tmp + i;

                    /* Find end of boundary */
                    for (k = i; k < data_len; k++)
                        if (data_len - k >= strlen(b) &&
                                tmp[k] == '-' && tmp[k + 1] == '-')
                            if (!memcmp(tmp + k, b, strlen(b)))
                                break;
                    if (k >= data_len)
                        goto err;

                    /* Remove preceding CRLF */
                    k -= 2;

                    /* Parse the part */
                    p = silc_mime_decode(NULL, line, k - i);
                    if (!p)
                        goto err;

                    silc_dlist_add(mime->multiparts, p);
                    i += (k - i);
                }
            }
        }
    } else {
        /* Get data area.  If we are at the end and we have fields present
           there is no data area present, but, if fields are not present we
           only have data area. */
        if (i >= data_len && !silc_hash_table_count(mime->fields))
            i = 0;
        SILC_LOG_DEBUG(("Data len %d", data_len - i));
        if (data_len - i)
            silc_mime_add_data(mime, tmp + i, data_len - i);
    }

    return mime;

err:
    if (m)
        silc_mime_free(m);
    return NULL;
}
Пример #8
0
SilcBool silc_client_channel_save_public_keys(SilcChannelEntry channel,
					      unsigned char *chpk_list,
					      SilcUInt32 chpk_list_len,
					      SilcBool remove_all)
{
  SilcArgumentDecodedList a, b;
  SilcDList chpks;
  SilcBool found;

  if (remove_all) {
    /* Remove all channel public keys */
    if (!channel->channel_pubkeys)
      return FALSE;

    silc_dlist_start(channel->channel_pubkeys);
    while ((b = silc_dlist_get(channel->channel_pubkeys)))
      silc_dlist_del(channel->channel_pubkeys, b);

    silc_dlist_uninit(channel->channel_pubkeys);
    channel->channel_pubkeys = NULL;

    return TRUE;
  }

  /* Parse channel public key list and add or remove public keys */
  chpks = silc_argument_list_parse_decoded(chpk_list, chpk_list_len,
					   SILC_ARGUMENT_PUBLIC_KEY);
  if (!chpks)
    return FALSE;

  if (!channel->channel_pubkeys) {
    channel->channel_pubkeys = silc_dlist_init();
    if (!channel->channel_pubkeys) {
      silc_argument_list_free(chpks, SILC_ARGUMENT_PUBLIC_KEY);
      return FALSE;
    }
  }

  silc_dlist_start(chpks);
  while ((a = silc_dlist_get(chpks))) {
    found = FALSE;
    silc_dlist_start(channel->channel_pubkeys);
    while ((b = silc_dlist_get(channel->channel_pubkeys))) {
      if (silc_pkcs_public_key_compare(a->argument, b->argument)) {
	found = TRUE;
	break;
      }
    }

    if ((a->arg_type == 0x00 || a->arg_type == 0x03) && !found) {
      silc_dlist_add(channel->channel_pubkeys, a);
      silc_dlist_del(chpks, a);
    } else if (a->arg_type == 0x01 && found) {
      silc_dlist_del(channel->channel_pubkeys, b);
    }
  }

  silc_argument_list_free(chpks, SILC_ARGUMENT_PUBLIC_KEY);

  return TRUE;
}
Пример #9
0
SilcBool silc_client_add_channel_private_key(SilcClient client,
					     SilcClientConnection conn,
					     SilcChannelEntry channel,
					     const char *name,
					     char *cipher,
					     char *hmac,
					     unsigned char *key,
					     SilcUInt32 key_len,
					     SilcChannelPrivateKey *ret_key)
{
  SilcChannelPrivateKey entry;
  unsigned char hash[SILC_HASH_MAXLEN];
  SilcSKEKeyMaterial keymat;

  if (!client || !conn || !channel)
    return FALSE;

  if (!cipher)
    cipher = SILC_DEFAULT_CIPHER;
  if (!hmac)
    hmac = SILC_DEFAULT_HMAC;

  if (!silc_cipher_is_supported(cipher))
    return FALSE;
  if (!silc_hmac_is_supported(hmac))
    return FALSE;

  if (!channel->internal.private_keys) {
    channel->internal.private_keys = silc_dlist_init();
    if (!channel->internal.private_keys)
      return FALSE;
  }

  /* Produce the key material */
  keymat = silc_ske_process_key_material_data(key, key_len, 16, 256, 16,
					      conn->internal->sha1hash);
  if (!keymat)
    return FALSE;

  /* Save the key */
  entry = silc_calloc(1, sizeof(*entry));
  if (!entry) {
    silc_ske_free_key_material(keymat);
    return FALSE;
  }
  entry->name = name ? strdup(name) : NULL;

  /* Allocate the cipher and set the key */
  if (!silc_cipher_alloc(cipher, &entry->send_key)) {
    silc_free(entry);
    silc_free(entry->name);
    silc_ske_free_key_material(keymat);
    return FALSE;
  }
  if (!silc_cipher_alloc(cipher, &entry->receive_key)) {
    silc_free(entry);
    silc_free(entry->name);
    silc_cipher_free(entry->send_key);
    silc_ske_free_key_material(keymat);
    return FALSE;
  }
  silc_cipher_set_key(entry->send_key, keymat->send_enc_key,
		      keymat->enc_key_len, TRUE);
  silc_cipher_set_key(entry->receive_key, keymat->send_enc_key,
		      keymat->enc_key_len, FALSE);

  /* Generate HMAC key from the channel key data and set it */
  if (!silc_hmac_alloc(hmac, NULL, &entry->hmac)) {
    silc_free(entry);
    silc_free(entry->name);
    silc_cipher_free(entry->send_key);
    silc_cipher_free(entry->receive_key);
    silc_ske_free_key_material(keymat);
    return FALSE;
  }
  silc_hash_make(silc_hmac_get_hash(entry->hmac), keymat->send_enc_key,
		 keymat->enc_key_len / 8, hash);
  silc_hmac_set_key(entry->hmac, hash,
		    silc_hash_len(silc_hmac_get_hash(entry->hmac)));
  memset(hash, 0, sizeof(hash));

  /* Add to the private keys list */
  silc_dlist_add(channel->internal.private_keys, entry);

  if (!channel->internal.curr_key) {
    channel->internal.curr_key = entry;
    channel->cipher = silc_cipher_get_name(entry->send_key);
    channel->hmac = silc_cipher_get_name(entry->send_key);
  }

  /* Free the key material */
  silc_ske_free_key_material(keymat);

  if (ret_key)
    *ret_key = entry;

  return TRUE;
}
Пример #10
0
SilcBool silc_client_save_channel_key(SilcClient client,
				      SilcClientConnection conn,
				      SilcBuffer key_payload,
				      SilcChannelEntry channel)
{
  unsigned char *id_string, *key, *cipher, *hmac, hash[SILC_HASH_MAXLEN];
  SilcUInt32 tmp_len;
  SilcChannelID id;
  SilcChannelKeyPayload payload;

  SILC_LOG_DEBUG(("New channel key"));

  payload = silc_channel_key_payload_parse(silc_buffer_data(key_payload),
					   silc_buffer_len(key_payload));
  if (!payload)
    return FALSE;

  id_string = silc_channel_key_get_id(payload, &tmp_len);
  if (!id_string) {
    silc_channel_key_payload_free(payload);
    return FALSE;
  }

  if (!silc_id_str2id(id_string, tmp_len, SILC_ID_CHANNEL, &id, sizeof(id))) {
    silc_channel_key_payload_free(payload);
    return FALSE;
  }

  /* Find channel. */
  if (!channel) {
    channel = silc_client_get_channel_by_id(client, conn, &id);
    if (!channel) {
      SILC_LOG_DEBUG(("Key for unknown channel"));
      silc_channel_key_payload_free(payload);
      return FALSE;
    }
  } else {
    silc_client_ref_channel(client, conn, channel);
  }

  /* Save the old key for a short period of time so that we can decrypt
     channel message even after the rekey if some client would be sending
     messages with the old key after the rekey. */
  if (!channel->internal.old_channel_keys)
    channel->internal.old_channel_keys = silc_dlist_init();
  if (!channel->internal.old_hmacs)
    channel->internal.old_hmacs = silc_dlist_init();
  if (channel->internal.old_channel_keys && channel->internal.old_hmacs) {
    silc_dlist_add(channel->internal.old_channel_keys,
		   channel->internal.receive_key);
    silc_dlist_add(channel->internal.old_hmacs, channel->internal.hmac);
    silc_schedule_task_add_timeout(client->schedule,
				   silc_client_save_channel_key_rekey,
				   channel, 15, 0);
  }

  /* Get channel cipher */
  cipher = silc_channel_key_get_cipher(payload, NULL);
  if (!silc_cipher_alloc(cipher, &channel->internal.send_key)) {
    client->internal->ops->say(
			   conn->client, conn,
			   SILC_CLIENT_MESSAGE_AUDIT,
			   "Cannot talk to channel: unsupported cipher %s",
			   cipher);
    silc_client_unref_channel(client, conn, channel);
    silc_channel_key_payload_free(payload);
    return FALSE;
  }
  if (!silc_cipher_alloc(cipher, &channel->internal.receive_key)) {
    client->internal->ops->say(
			   conn->client, conn,
			   SILC_CLIENT_MESSAGE_AUDIT,
			   "Cannot talk to channel: unsupported cipher %s",
			   cipher);
    silc_client_unref_channel(client, conn, channel);
    silc_channel_key_payload_free(payload);
    return FALSE;
  }

  /* Set the cipher key.  Both sending and receiving keys are same */
  key = silc_channel_key_get_key(payload, &tmp_len);
  silc_cipher_set_key(channel->internal.send_key, key, tmp_len * 8, TRUE);
  silc_cipher_set_key(channel->internal.receive_key, key, tmp_len * 8, FALSE);

  /* Get channel HMAC */
  hmac = (channel->internal.hmac ?
	  (char *)silc_hmac_get_name(channel->internal.hmac) :
	  SILC_DEFAULT_HMAC);
  if (!silc_hmac_alloc(hmac, NULL, &channel->internal.hmac)) {
    client->internal->ops->say(
			   conn->client, conn,
			   SILC_CLIENT_MESSAGE_AUDIT,
			   "Cannot talk to channel: unsupported HMAC %s",
			   hmac);
    silc_client_unref_channel(client, conn, channel);
    silc_channel_key_payload_free(payload);
    return FALSE;
  }

  channel->cipher = silc_cipher_get_name(channel->internal.send_key);
  channel->hmac = silc_hmac_get_name(channel->internal.hmac);

  /* Set HMAC key */
  silc_hash_make(silc_hmac_get_hash(channel->internal.hmac), key,
		 tmp_len, hash);
  silc_hmac_set_key(channel->internal.hmac, hash,
		    silc_hash_len(silc_hmac_get_hash(channel->internal.hmac)));
  memset(hash, 0, sizeof(hash));
  silc_channel_key_payload_free(payload);

  silc_client_unref_channel(client, conn, channel);

  return TRUE;
}
Пример #11
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;
}