Blame ui/gcr-secure-entry-buffer.c

Packit b00eeb
/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
Packit b00eeb
/* gcr-secure-buffer.c - secure memory gtkentry buffer
Packit b00eeb
Packit b00eeb
   Copyright (C) 2009 Stefan Walter
Packit b00eeb
Packit b00eeb
   The Gnome Keyring Library is free software; you can redistribute it and/or
Packit b00eeb
   modify it under the terms of the GNU Library General Public License as
Packit b00eeb
   published by the Free Software Foundation; either version 2 of the
Packit b00eeb
   License, or (at your option) any later version.
Packit b00eeb
Packit b00eeb
   The Gnome Keyring Library is distributed in the hope that it will be useful,
Packit b00eeb
   but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit b00eeb
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit b00eeb
   Library General Public License for more details.
Packit b00eeb
Packit b00eeb
   You should have received a copy of the GNU Library General Public
Packit b00eeb
   License along with the Gnome Library; see the file COPYING.LIB.  If not,
Packit b00eeb
   see <http://www.gnu.org/licenses/>.
Packit b00eeb
Packit b00eeb
   Author: Stef Walter <stef@memberwebs.com>
Packit b00eeb
*/
Packit b00eeb
Packit b00eeb
#include "config.h"
Packit b00eeb
Packit b00eeb
#include "gcr-secure-entry-buffer.h"
Packit b00eeb
Packit b00eeb
#include "egg/egg-secure-memory.h"
Packit b00eeb
Packit b00eeb
#include <string.h>
Packit b00eeb
Packit b00eeb
/**
Packit b00eeb
 * SECTION:gcr-secure-entry-buffer
Packit b00eeb
 * @title: GcrSecureEntryBuffer
Packit b00eeb
 * @short_description: a GtkEntryBuffer that uses non-pageable memory
Packit b00eeb
 *
Packit b00eeb
 * It's good practice to try to keep passwords or sensitive secrets out of
Packit b00eeb
 * pageable memory whenever possible, so that they don't get written to disk.
Packit b00eeb
 *
Packit b00eeb
 * This is a #GtkEntryBuffer to be used with #GtkEntry which uses non-pageable
Packit b00eeb
 * memory to store a password placed in the entry. In order to make any sense
Packit b00eeb
 * at all, the entry must have it's visibility turned off, and just be displaying
Packit b00eeb
 * place holder characters for the text. That is, a password style entry.
Packit b00eeb
 *
Packit b00eeb
 * Use gtk_entry_new_with_buffer() or gtk_entry_set_buffer() to set this buffer
Packit b00eeb
 * on an entry.
Packit b00eeb
 */
Packit b00eeb
Packit b00eeb
/**
Packit b00eeb
 * GcrSecureEntryBuffer:
Packit b00eeb
 *
Packit b00eeb
 * A #GtkEntryBuffer which uses non-pageable memory for passwords or secrets.
Packit b00eeb
 */
Packit b00eeb
Packit b00eeb
/**
Packit b00eeb
 * GcrSecureEntryBufferClass:
Packit b00eeb
 * @parent_class: parent class
Packit b00eeb
 *
Packit b00eeb
 * The class for #GcrSecureEntryBuffer.
Packit b00eeb
 */
Packit b00eeb
Packit b00eeb
EGG_SECURE_DECLARE (secure_entry_buffer);
Packit b00eeb
Packit b00eeb
/* Initial size of buffer, in bytes */
Packit b00eeb
#define MIN_SIZE 16
Packit b00eeb
Packit b00eeb
struct _GcrSecureEntryBufferPrivate
Packit b00eeb
{
Packit b00eeb
	gchar *text;
Packit b00eeb
	gsize text_size;
Packit b00eeb
	gsize text_bytes;
Packit b00eeb
	guint text_chars;
Packit b00eeb
};
Packit b00eeb
Packit b00eeb
G_DEFINE_TYPE (GcrSecureEntryBuffer, gcr_secure_entry_buffer, GTK_TYPE_ENTRY_BUFFER);
Packit b00eeb
Packit b00eeb
static const gchar *
Packit b00eeb
gcr_secure_entry_buffer_real_get_text (GtkEntryBuffer *buffer,
Packit b00eeb
                                       gsize *n_bytes)
Packit b00eeb
{
Packit b00eeb
	GcrSecureEntryBuffer *self = GCR_SECURE_ENTRY_BUFFER (buffer);
Packit b00eeb
	if (n_bytes)
Packit b00eeb
		*n_bytes = self->pv->text_bytes;
Packit b00eeb
	if (!self->pv->text)
Packit b00eeb
		return "";
Packit b00eeb
	return self->pv->text;
Packit b00eeb
}
Packit b00eeb
Packit b00eeb
static guint
Packit b00eeb
gcr_secure_entry_buffer_real_get_length (GtkEntryBuffer *buffer)
Packit b00eeb
{
Packit b00eeb
	GcrSecureEntryBuffer *self = GCR_SECURE_ENTRY_BUFFER (buffer);
Packit b00eeb
	return self->pv->text_chars;
Packit b00eeb
}
Packit b00eeb
Packit b00eeb
static guint
Packit b00eeb
gcr_secure_entry_buffer_real_insert_text (GtkEntryBuffer *buffer,
Packit b00eeb
                                          guint position,
Packit b00eeb
                                          const gchar *chars,
Packit b00eeb
                                          guint n_chars)
Packit b00eeb
{
Packit b00eeb
	GcrSecureEntryBuffer *self = GCR_SECURE_ENTRY_BUFFER (buffer);
Packit b00eeb
	GcrSecureEntryBufferPrivate *pv = self->pv;
Packit b00eeb
	gsize n_bytes;
Packit b00eeb
	gsize at;
Packit b00eeb
Packit b00eeb
	n_bytes = g_utf8_offset_to_pointer (chars, n_chars) - chars;
Packit b00eeb
Packit b00eeb
	/* Need more memory */
Packit b00eeb
	if (n_bytes + pv->text_bytes + 1 > pv->text_size) {
Packit b00eeb
Packit b00eeb
		/* Calculate our new buffer size */
Packit b00eeb
		while (n_bytes + pv->text_bytes + 1 > pv->text_size) {
Packit b00eeb
			if (pv->text_size == 0) {
Packit b00eeb
				pv->text_size = MIN_SIZE;
Packit b00eeb
			} else {
Packit b00eeb
				if (2 * pv->text_size < GTK_ENTRY_BUFFER_MAX_SIZE) {
Packit b00eeb
					pv->text_size *= 2;
Packit b00eeb
				} else {
Packit b00eeb
					pv->text_size = GTK_ENTRY_BUFFER_MAX_SIZE;
Packit b00eeb
					if (n_bytes > pv->text_size - pv->text_bytes - 1) {
Packit b00eeb
						n_bytes = pv->text_size - pv->text_bytes - 1;
Packit b00eeb
						n_bytes = g_utf8_find_prev_char (chars, chars + n_bytes + 1) - chars;
Packit b00eeb
						n_chars = g_utf8_strlen (chars, n_bytes);
Packit b00eeb
					}
Packit b00eeb
					break;
Packit b00eeb
				}
Packit b00eeb
			}
Packit b00eeb
		}
Packit b00eeb
Packit b00eeb
		pv->text = egg_secure_realloc (pv->text, pv->text_size);
Packit b00eeb
	}
Packit b00eeb
Packit b00eeb
	/* Actual text insertion */
Packit b00eeb
	at = g_utf8_offset_to_pointer (pv->text, position) - pv->text;
Packit b00eeb
	memmove (pv->text + at + n_bytes, pv->text + at, pv->text_bytes - at);
Packit b00eeb
	memcpy (pv->text + at, chars, n_bytes);
Packit b00eeb
Packit b00eeb
	/* Book keeping */
Packit b00eeb
	pv->text_bytes += n_bytes;
Packit b00eeb
	pv->text_chars += n_chars;
Packit b00eeb
	pv->text[pv->text_bytes] = '\0';
Packit b00eeb
Packit b00eeb
	gtk_entry_buffer_emit_inserted_text (buffer, position, chars, n_chars);
Packit b00eeb
	return n_chars;
Packit b00eeb
}
Packit b00eeb
Packit b00eeb
static guint
Packit b00eeb
gcr_secure_entry_buffer_real_delete_text (GtkEntryBuffer *buffer,
Packit b00eeb
                                          guint position,
Packit b00eeb
                                          guint n_chars)
Packit b00eeb
{
Packit b00eeb
	GcrSecureEntryBuffer *self = GCR_SECURE_ENTRY_BUFFER (buffer);
Packit b00eeb
	GcrSecureEntryBufferPrivate *pv = self->pv;
Packit b00eeb
	gsize start, end;
Packit b00eeb
Packit b00eeb
	if (position > pv->text_chars)
Packit b00eeb
		position = pv->text_chars;
Packit b00eeb
	if (position + n_chars > pv->text_chars)
Packit b00eeb
		n_chars = pv->text_chars - position;
Packit b00eeb
Packit b00eeb
	if (n_chars > 0) {
Packit b00eeb
		start = g_utf8_offset_to_pointer (pv->text, position) - pv->text;
Packit b00eeb
		end = g_utf8_offset_to_pointer (pv->text, position + n_chars) - pv->text;
Packit b00eeb
Packit b00eeb
		memmove (pv->text + start, pv->text + end, pv->text_bytes + 1 - end);
Packit b00eeb
		pv->text_chars -= n_chars;
Packit b00eeb
		pv->text_bytes -= (end - start);
Packit b00eeb
Packit b00eeb
		gtk_entry_buffer_emit_deleted_text (buffer, position, n_chars);
Packit b00eeb
	}
Packit b00eeb
Packit b00eeb
	return n_chars;
Packit b00eeb
}
Packit b00eeb
Packit b00eeb
static void
Packit b00eeb
gcr_secure_entry_buffer_init (GcrSecureEntryBuffer *self)
Packit b00eeb
{
Packit b00eeb
	GcrSecureEntryBufferPrivate *pv;
Packit b00eeb
	pv = self->pv = G_TYPE_INSTANCE_GET_PRIVATE (self, GCR_TYPE_SECURE_ENTRY_BUFFER, GcrSecureEntryBufferPrivate);
Packit b00eeb
Packit b00eeb
	pv->text = NULL;
Packit b00eeb
	pv->text_chars = 0;
Packit b00eeb
	pv->text_bytes = 0;
Packit b00eeb
	pv->text_size = 0;
Packit b00eeb
}
Packit b00eeb
Packit b00eeb
static void
Packit b00eeb
gcr_secure_entry_buffer_finalize (GObject *obj)
Packit b00eeb
{
Packit b00eeb
	GcrSecureEntryBuffer *self = GCR_SECURE_ENTRY_BUFFER (obj);
Packit b00eeb
	GcrSecureEntryBufferPrivate *pv = self->pv;
Packit b00eeb
Packit b00eeb
	if (pv->text) {
Packit b00eeb
		egg_secure_strfree (pv->text);
Packit b00eeb
		pv->text = NULL;
Packit b00eeb
		pv->text_bytes = pv->text_size = 0;
Packit b00eeb
		pv->text_chars = 0;
Packit b00eeb
	}
Packit b00eeb
Packit b00eeb
	G_OBJECT_CLASS (gcr_secure_entry_buffer_parent_class)->finalize (obj);
Packit b00eeb
}
Packit b00eeb
Packit b00eeb
static void
Packit b00eeb
gcr_secure_entry_buffer_class_init (GcrSecureEntryBufferClass *klass)
Packit b00eeb
{
Packit b00eeb
	GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
Packit b00eeb
	GtkEntryBufferClass *buffer_class = GTK_ENTRY_BUFFER_CLASS (klass);
Packit b00eeb
Packit b00eeb
	gobject_class->finalize = gcr_secure_entry_buffer_finalize;
Packit b00eeb
Packit b00eeb
	buffer_class->get_text = gcr_secure_entry_buffer_real_get_text;
Packit b00eeb
	buffer_class->get_length = gcr_secure_entry_buffer_real_get_length;
Packit b00eeb
	buffer_class->insert_text = gcr_secure_entry_buffer_real_insert_text;
Packit b00eeb
	buffer_class->delete_text = gcr_secure_entry_buffer_real_delete_text;
Packit b00eeb
Packit b00eeb
	g_type_class_add_private (gobject_class, sizeof (GcrSecureEntryBufferPrivate));
Packit b00eeb
}
Packit b00eeb
Packit b00eeb
/**
Packit b00eeb
 * gcr_secure_entry_buffer_new:
Packit b00eeb
 *
Packit b00eeb
 * Create a new #GcrSecureEntryBuffer, a #GtkEntryBuffer which uses
Packit b00eeb
 * non-pageable memory for the text.
Packit b00eeb
 *
Packit b00eeb
 * Returns: (transfer full): the new entry buffer
Packit b00eeb
 */
Packit b00eeb
GtkEntryBuffer *
Packit b00eeb
gcr_secure_entry_buffer_new (void)
Packit b00eeb
{
Packit b00eeb
	return g_object_new (GCR_TYPE_SECURE_ENTRY_BUFFER, NULL);
Packit b00eeb
}