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