1 | /* gtkpasswordentrybuffer.c: Entry buffer with secure allocation |
2 | * |
3 | Copyright 2009 Stefan Walter |
4 | * Copyright 2020 GNOME Foundation |
5 | * |
6 | * SPDX-License-Identifier: LGPL-2.1-or-later |
7 | * |
8 | * This library is free software; you can redistribute it and/or |
9 | * modify it under the terms of the GNU Lesser General Public |
10 | * License as published by the Free Software Foundation; either |
11 | * version 2.1 of the License, or (at your option) any later version. |
12 | * |
13 | * This library is distributed in the hope that it will be useful, |
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
16 | * Lesser General Public License for more details. |
17 | * |
18 | * You should have received a copy of the GNU Lesser General Public |
19 | * License along with this library; if not, see <http://www.gnu.org/licenses/>. |
20 | */ |
21 | |
22 | #include "config.h" |
23 | |
24 | #include "gtkpasswordentrybuffer.h" |
25 | |
26 | #include "gtksecurememoryprivate.h" |
27 | |
28 | #include <string.h> |
29 | |
30 | /* Initial size of buffer, in bytes */ |
31 | #define MIN_SIZE 16 |
32 | |
33 | /** |
34 | * GtkPasswordEntryBuffer: |
35 | * |
36 | * A `GtkEntryBuffer` that locks the underlying memory to prevent it |
37 | * from being swapped to disk. |
38 | * |
39 | * `GtkPasswordEntry` uses a `GtkPasswordEntryBuffer`. |
40 | * |
41 | * Since: 4.4 |
42 | */ |
43 | struct _GtkPasswordEntryBuffer |
44 | { |
45 | GtkEntryBuffer parent_instance; |
46 | |
47 | char *text; |
48 | gsize text_size; |
49 | gsize text_bytes; |
50 | guint text_chars; |
51 | }; |
52 | |
53 | G_DEFINE_TYPE (GtkPasswordEntryBuffer, gtk_password_entry_buffer, GTK_TYPE_ENTRY_BUFFER) |
54 | |
55 | static const char * |
56 | gtk_password_entry_buffer_real_get_text (GtkEntryBuffer *buffer, |
57 | gsize *n_bytes) |
58 | { |
59 | GtkPasswordEntryBuffer *self = GTK_PASSWORD_ENTRY_BUFFER (ptr: buffer); |
60 | |
61 | if (n_bytes != NULL) |
62 | *n_bytes = self->text_bytes; |
63 | |
64 | if (!self->text) |
65 | return "" ; |
66 | |
67 | return self->text; |
68 | } |
69 | |
70 | static guint |
71 | gtk_password_entry_buffer_real_get_length (GtkEntryBuffer *buffer) |
72 | { |
73 | GtkPasswordEntryBuffer *self = GTK_PASSWORD_ENTRY_BUFFER (ptr: buffer); |
74 | return self->text_chars; |
75 | } |
76 | |
77 | static guint |
78 | gtk_password_entry_buffer_real_insert_text (GtkEntryBuffer *buffer, |
79 | guint position, |
80 | const char *chars, |
81 | guint n_chars) |
82 | { |
83 | GtkPasswordEntryBuffer *self = GTK_PASSWORD_ENTRY_BUFFER (ptr: buffer); |
84 | |
85 | gsize n_bytes = g_utf8_offset_to_pointer (str: chars, offset: n_chars) - chars; |
86 | |
87 | /* Need more memory */ |
88 | if (n_bytes + self->text_bytes + 1 > self->text_size) |
89 | { |
90 | /* Calculate our new buffer size */ |
91 | while (n_bytes + self->text_bytes + 1 > self->text_size) |
92 | { |
93 | if (self->text_size == 0) |
94 | { |
95 | self->text_size = MIN_SIZE; |
96 | } |
97 | else |
98 | { |
99 | if (2 * self->text_size < GTK_ENTRY_BUFFER_MAX_SIZE) |
100 | { |
101 | self->text_size *= 2; |
102 | } |
103 | else |
104 | { |
105 | self->text_size = GTK_ENTRY_BUFFER_MAX_SIZE; |
106 | if (n_bytes > self->text_size - self->text_bytes - 1) |
107 | { |
108 | n_bytes = self->text_size - self->text_bytes - 1; |
109 | n_bytes = g_utf8_find_prev_char (str: chars, p: chars + n_bytes + 1) - chars; |
110 | n_chars = g_utf8_strlen (p: chars, max: n_bytes); |
111 | } |
112 | break; |
113 | } |
114 | } |
115 | } |
116 | |
117 | self->text = gtk_secure_realloc (p: self->text, length: self->text_size); |
118 | } |
119 | |
120 | /* Actual text insertion */ |
121 | gsize at = g_utf8_offset_to_pointer (str: self->text, offset: position) - self->text; |
122 | memmove (dest: self->text + at + n_bytes, src: self->text + at, n: self->text_bytes - at); |
123 | memcpy (dest: self->text + at, src: chars, n: n_bytes); |
124 | |
125 | /* Book keeping */ |
126 | self->text_bytes += n_bytes; |
127 | self->text_chars += n_chars; |
128 | self->text[self->text_bytes] = '\0'; |
129 | |
130 | gtk_entry_buffer_emit_inserted_text (buffer, position, chars, n_chars); |
131 | |
132 | return n_chars; |
133 | } |
134 | |
135 | static void |
136 | gtk_password_entry_buffer_real_deleted_text (GtkEntryBuffer *buffer, |
137 | guint position, |
138 | guint n_chars) |
139 | { |
140 | GtkPasswordEntryBuffer *self = GTK_PASSWORD_ENTRY_BUFFER (ptr: buffer); |
141 | |
142 | gsize start = g_utf8_offset_to_pointer (str: self->text, offset: position) - self->text; |
143 | gsize end = g_utf8_offset_to_pointer (str: self->text, offset: position + n_chars) - self->text; |
144 | |
145 | memmove (dest: self->text + start, src: self->text + end, n: self->text_bytes + 1 - end); |
146 | self->text_chars -= n_chars; |
147 | self->text_bytes -= (end - start); |
148 | |
149 | g_object_notify (G_OBJECT (buffer), property_name: "text" ); |
150 | g_object_notify (G_OBJECT (buffer), property_name: "length" ); |
151 | } |
152 | |
153 | static guint |
154 | gtk_password_entry_buffer_real_delete_text (GtkEntryBuffer *buffer, |
155 | guint position, |
156 | guint n_chars) |
157 | { |
158 | GtkPasswordEntryBuffer *self = GTK_PASSWORD_ENTRY_BUFFER (ptr: buffer); |
159 | |
160 | if (position > self->text_chars) |
161 | position = self->text_chars; |
162 | if (position + n_chars > self->text_chars) |
163 | n_chars = self->text_chars - position; |
164 | |
165 | if (n_chars > 0) |
166 | gtk_entry_buffer_emit_deleted_text (buffer, position, n_chars); |
167 | |
168 | return n_chars; |
169 | } |
170 | |
171 | static void |
172 | gtk_password_entry_buffer_finalize (GObject *gobject) |
173 | { |
174 | GtkPasswordEntryBuffer *self = GTK_PASSWORD_ENTRY_BUFFER (ptr: gobject); |
175 | |
176 | g_clear_pointer (&self->text, gtk_secure_free); |
177 | |
178 | self->text_bytes = 0; |
179 | self->text_size = 0; |
180 | self->text_chars = 0; |
181 | |
182 | G_OBJECT_CLASS (gtk_password_entry_buffer_parent_class)->finalize (gobject); |
183 | } |
184 | |
185 | static void |
186 | gtk_password_entry_buffer_class_init (GtkPasswordEntryBufferClass *klass) |
187 | { |
188 | GObjectClass *gobject_class = G_OBJECT_CLASS (klass); |
189 | GtkEntryBufferClass *buffer_class = GTK_ENTRY_BUFFER_CLASS (klass); |
190 | |
191 | gobject_class->finalize = gtk_password_entry_buffer_finalize; |
192 | |
193 | buffer_class->get_text = gtk_password_entry_buffer_real_get_text; |
194 | buffer_class->get_length = gtk_password_entry_buffer_real_get_length; |
195 | buffer_class->insert_text = gtk_password_entry_buffer_real_insert_text; |
196 | buffer_class->delete_text = gtk_password_entry_buffer_real_delete_text; |
197 | buffer_class->deleted_text = gtk_password_entry_buffer_real_deleted_text; |
198 | } |
199 | |
200 | static void |
201 | gtk_password_entry_buffer_init (GtkPasswordEntryBuffer *self) |
202 | { |
203 | } |
204 | |
205 | /** |
206 | * gtk_password_entry_buffer_new: |
207 | * |
208 | * Creates a new `GtkEntryBuffer` using secure memory allocations. |
209 | * |
210 | * Returns: (transfer full): the newly created instance |
211 | */ |
212 | GtkEntryBuffer * |
213 | gtk_password_entry_buffer_new (void) |
214 | { |
215 | return g_object_new (GTK_TYPE_PASSWORD_ENTRY_BUFFER, NULL); |
216 | } |
217 | |