1 /* libsecret - GLib wrapper for Secret Service
2 *
3 * Copyright 2011 Collabora Ltd.
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU Lesser General Public License as published
7 * by the Free Software Foundation; either version 2.1 of the licence or (at
8 * your option) any later version.
9 *
10 * See the included COPYING file for more information.
11 *
12 * Author: Stef Walter <stefw@gnome.org>
13 */
14
15 #include "config.h"
16
17 #include "secret-private.h"
18 #include "secret-value.h"
19
20 #include "egg/egg-secure-memory.h"
21
22 #include <string.h>
23
24 /**
25 * SECTION:secret-value
26 * @title: SecretValue
27 * @short_description: a value containing a secret
28 *
29 * A #SecretValue contains a password or other secret value.
30 *
31 * Use secret_value_get() to get the actual secret data, such as a password.
32 * The secret data is not necessarily null-terminated, unless the content type
33 * is "text/plain".
34 *
35 * Each #SecretValue has a content type. For passwords, this is "text/plain".
36 * Use secret_value_get_content_type() to look at the content type.
37 *
38 * #SecretValue is reference counted and immutable. The secret data is only
39 * freed when all references have been released via secret_value_unref().
40 *
41 * Stability: Stable
42 */
43
44 static gboolean is_password_value (SecretValue *value);
45
46 /**
47 * SecretValue:
48 *
49 * A secret value, like a password or other binary secret.
50 */
51
52 EGG_SECURE_DECLARE (secret_value);
53
54 struct _SecretValue {
55 gint refs;
56 gpointer secret;
57 gsize length;
58 GDestroyNotify destroy;
59 gchar *content_type;
60 };
61
62 GType
secret_value_get_type(void)63 secret_value_get_type (void)
64 {
65 static gsize initialized = 0;
66 static GType type = 0;
67
68 if (g_once_init_enter (&initialized)) {
69 type = g_boxed_type_register_static ("SecretValue",
70 (GBoxedCopyFunc)secret_value_ref,
71 (GBoxedFreeFunc)secret_value_unref);
72 g_once_init_leave (&initialized, 1);
73 }
74
75 return type;
76 }
77
78 /**
79 * secret_value_new:
80 * @secret: the secret data
81 * @length: the length of the data
82 * @content_type: the content type of the data
83 *
84 * Create a #SecretValue for the secret data passed in. The secret data is
85 * copied into non-pageable 'secure' memory.
86 *
87 * If the length is less than zero, then @secret is assumed to be
88 * null-terminated.
89 *
90 * Returns: (transfer full): the new #SecretValue
91 */
92 SecretValue *
secret_value_new(const gchar * secret,gssize length,const gchar * content_type)93 secret_value_new (const gchar *secret,
94 gssize length,
95 const gchar *content_type)
96 {
97 gchar *copy;
98
99 g_return_val_if_fail (length == 0 || secret != NULL, NULL);
100 g_return_val_if_fail (content_type, NULL);
101
102 if (length < 0)
103 length = strlen (secret);
104
105 copy = egg_secure_alloc (length + 1);
106 if (secret)
107 memcpy (copy, secret, length);
108 copy[length] = 0;
109 return secret_value_new_full (copy, length, content_type, egg_secure_free);
110 }
111
112 /**
113 * secret_value_new_full:
114 * @secret: the secret data
115 * @length: the length of the data
116 * @content_type: the content type of the data
117 * @destroy: function to call to free the secret data
118 *
119 * Create a #SecretValue for the secret data passed in. The secret data is
120 * not copied, and will later be freed with the @destroy function.
121 *
122 * If the length is less than zero, then @secret is assumed to be
123 * null-terminated.
124 *
125 * Returns: (transfer full): the new #SecretValue
126 */
127 SecretValue *
secret_value_new_full(gchar * secret,gssize length,const gchar * content_type,GDestroyNotify destroy)128 secret_value_new_full (gchar *secret,
129 gssize length,
130 const gchar *content_type,
131 GDestroyNotify destroy)
132 {
133 SecretValue *value;
134
135 g_return_val_if_fail (content_type, NULL);
136
137 if (length < 0)
138 length = strlen (secret);
139
140 value = g_slice_new0 (SecretValue);
141 value->refs = 1;
142 value->content_type = g_strdup (content_type);
143 value->destroy = destroy;
144 value->length = length;
145 value->secret = secret;
146
147 return value;
148 }
149
150 /**
151 * secret_value_get:
152 * @value: the value
153 * @length: the length of the secret
154 *
155 * Get the secret data in the #SecretValue. The value is not necessarily
156 * null-terminated unless it was created with secret_value_new() or a
157 * null-terminated string was passed to secret_value_new_full().
158 *
159 * Returns: (array length=length) (element-type guint8): the secret data
160 */
161 const gchar *
secret_value_get(SecretValue * value,gsize * length)162 secret_value_get (SecretValue *value,
163 gsize *length)
164 {
165 g_return_val_if_fail (value, NULL);
166 if (length)
167 *length = value->length;
168 return value->secret;
169 }
170
171 /**
172 * secret_value_get_text:
173 * @value: the value
174 *
175 * Get the secret data in the #SecretValue if it contains a textual
176 * value. The content type must be <literal>text/plain</literal>.
177 *
178 * Returns: (allow-none): the content type
179 */
180 const gchar *
secret_value_get_text(SecretValue * value)181 secret_value_get_text (SecretValue *value)
182 {
183 g_return_val_if_fail (value, NULL);
184
185 if (!is_password_value (value))
186 return NULL;
187
188 return value->secret;
189 }
190
191 /**
192 * secret_value_get_content_type:
193 * @value: the value
194 *
195 * Get the content type of the secret value, such as
196 * <literal>text/plain</literal>.
197 *
198 * Returns: the content type
199 */
200 const gchar *
secret_value_get_content_type(SecretValue * value)201 secret_value_get_content_type (SecretValue *value)
202 {
203 g_return_val_if_fail (value, NULL);
204 return value->content_type;
205 }
206
207 /**
208 * secret_value_ref:
209 * @value: value to reference
210 *
211 * Add another reference to the #SecretValue. For each reference
212 * secret_value_unref() should be called to unreference the value.
213 *
214 * Returns: (transfer full): the value
215 */
216 SecretValue *
secret_value_ref(SecretValue * value)217 secret_value_ref (SecretValue *value)
218 {
219 g_return_val_if_fail (value, NULL);
220 g_atomic_int_inc (&value->refs);
221 return value;
222 }
223
224 /**
225 * secret_value_unref:
226 * @value: (type Secret.Value): value to unreference
227 *
228 * Unreference a #SecretValue. When the last reference is gone, then
229 * the value will be freed.
230 */
231 void
secret_value_unref(gpointer value)232 secret_value_unref (gpointer value)
233 {
234 SecretValue *val = value;
235
236 g_return_if_fail (value != NULL);
237
238 if (g_atomic_int_dec_and_test (&val->refs)) {
239 g_free (val->content_type);
240 if (val->destroy)
241 (val->destroy) (val->secret);
242 g_slice_free (SecretValue, val);
243 }
244 }
245
246 static gboolean
is_password_value(SecretValue * value)247 is_password_value (SecretValue *value)
248 {
249 if (value->content_type && g_str_equal (value->content_type, "text/plain"))
250 return TRUE;
251
252 /* gnome-keyring-daemon used to return passwords like this, so support this, but validate */
253 if (!value->content_type || g_str_equal (value->content_type, "application/octet-stream"))
254 return g_utf8_validate (value->secret, value->length, NULL);
255
256 return FALSE;
257 }
258
259 /**
260 * secret_value_unref_to_password:
261 * @value: the value
262 * @length: the length of the secret
263 *
264 * Unreference a #SecretValue and steal the secret data in
265 * #SecretValue as nonpageable memory.
266 *
267 * Returns: (transfer full): a new password string stored in nonpageable memory
268 * which must be freed with secret_password_free() when done
269 *
270 * Since: 0.19.0
271 */
272 gchar *
secret_value_unref_to_password(SecretValue * value,gsize * length)273 secret_value_unref_to_password (SecretValue *value,
274 gsize *length)
275 {
276 SecretValue *val = value;
277 gchar *result;
278
279 g_return_val_if_fail (value != NULL, NULL);
280
281 if (g_atomic_int_dec_and_test (&val->refs)) {
282 if (val->destroy == egg_secure_free) {
283 result = val->secret;
284 if (length)
285 *length = val->length;
286
287 } else {
288 result = egg_secure_strndup (val->secret, val->length);
289 if (val->destroy)
290 (val->destroy) (val->secret);
291 if (length)
292 *length = val->length;
293 }
294 g_free (val->content_type);
295 g_slice_free (SecretValue, val);
296
297 } else {
298 result = egg_secure_strndup (val->secret, val->length);
299 if (length)
300 *length = val->length;
301 }
302
303 return result;
304 }
305
306 gchar *
_secret_value_unref_to_password(SecretValue * value)307 _secret_value_unref_to_password (SecretValue *value)
308 {
309 g_return_val_if_fail (value != NULL, NULL);
310
311 if (!is_password_value (value)) {
312 secret_value_unref (value);
313 return NULL;
314 }
315
316 return secret_value_unref_to_password (value, NULL);
317 }
318
319 gchar *
_secret_value_unref_to_string(SecretValue * value)320 _secret_value_unref_to_string (SecretValue *value)
321 {
322 SecretValue *val = value;
323 gchar *result;
324
325 g_return_val_if_fail (value != NULL, NULL);
326
327 if (!is_password_value (value)) {
328 secret_value_unref (value);
329 return NULL;
330 }
331
332 if (g_atomic_int_dec_and_test (&val->refs)) {
333 if (val->destroy == g_free) {
334 result = val->secret;
335
336 } else {
337 result = g_strndup (val->secret, val->length);
338 if (val->destroy)
339 (val->destroy) (val->secret);
340 }
341 g_free (val->content_type);
342 g_slice_free (SecretValue, val);
343
344 } else {
345 result = g_strndup (val->secret, val->length);
346 }
347
348 return result;
349 }
350