Bump version number
[platform/upstream/libsecret.git] / libsecret / secret-value.c
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: Unstable
42  */
43
44 /**
45  * SecretValue:
46  *
47  * A secret value, like a password or other binary secret.
48  */
49
50 EGG_SECURE_DECLARE (secret_value);
51
52 struct _SecretValue {
53         gint refs;
54         gpointer secret;
55         gsize length;
56         GDestroyNotify destroy;
57         gchar *content_type;
58 };
59
60 GType
61 secret_value_get_type (void)
62 {
63         static gsize initialized = 0;
64         static GType type = 0;
65
66         if (g_once_init_enter (&initialized)) {
67                 type = g_boxed_type_register_static ("SecretValue",
68                                                      (GBoxedCopyFunc)secret_value_ref,
69                                                      (GBoxedFreeFunc)secret_value_unref);
70                 g_once_init_leave (&initialized, 1);
71         }
72
73         return type;
74 }
75
76 /**
77  * secret_value_new:
78  * @secret: the secret data
79  * @length: the length of the data
80  * @content_type: the content type of the data
81  *
82  * Create a #SecretValue for the secret data passed in. The secret data is
83  * copied into non-pageable 'secure' memory.
84  *
85  * If the length is less than zero, then @secret is assumed to be
86  * null-terminated.
87  *
88  * Returns: (transfer full): the new #SecretValue
89  */
90 SecretValue *
91 secret_value_new (const gchar *secret,
92                   gssize length,
93                   const gchar *content_type)
94 {
95         gchar *copy;
96
97         g_return_val_if_fail (secret == NULL || length != 0, NULL);
98         g_return_val_if_fail (content_type, NULL);
99
100         if (length < 0)
101                 length = strlen (secret);
102
103         copy = egg_secure_alloc (length + 1);
104         memcpy (copy, secret, length);
105         copy[length] = 0;
106         return secret_value_new_full (copy, length, content_type, egg_secure_free);
107 }
108
109 /**
110  * secret_value_new_full:
111  * @secret: the secret data
112  * @length: the length of the data
113  * @content_type: the content type of the data
114  * @destroy: function to call to free the secret data
115  *
116  * Create a #SecretValue for the secret data passed in. The secret data is
117  * not copied, and will later be freed with the @destroy function.
118  *
119  * If the length is less than zero, then @secret is assumed to be
120  * null-terminated.
121  *
122  * Returns: (transfer full): the new #SecretValue
123  */
124 SecretValue *
125 secret_value_new_full (gchar *secret,
126                        gssize length,
127                        const gchar *content_type,
128                        GDestroyNotify destroy)
129 {
130         SecretValue *value;
131
132         g_return_val_if_fail (secret == NULL || length != 0, NULL);
133         g_return_val_if_fail (content_type, NULL);
134
135         if (length < 0)
136                 length = strlen (secret);
137
138         value = g_slice_new0 (SecretValue);
139         value->refs = 1;
140         value->content_type = g_strdup (content_type);
141         value->destroy = destroy;
142         value->length = length;
143         value->secret = secret;
144
145         return value;
146 }
147
148 /**
149  * secret_value_get:
150  * @value: the value
151  * @length: (out): the length of the secret
152  *
153  * Get the secret data in the #SecretValue. The value is not necessarily
154  * null-terminated unless it was created with secret_value_new() or a
155  * null-terminated string was passed to secret_value_new_full().
156  *
157  * Returns: (array length=length): the secret data
158  */
159 const gchar *
160 secret_value_get (SecretValue *value,
161                   gsize *length)
162 {
163         g_return_val_if_fail (value, NULL);
164         if (length)
165                 *length = value->length;
166         return value->secret;
167 }
168
169 /**
170  * secret_value_get_content_type:
171  * @value: the value
172  *
173  * Get the content type of the secret value, such as
174  * <literal>text/plain</literal>.
175  *
176  * Returns: the content type
177  */
178 const gchar *
179 secret_value_get_content_type (SecretValue *value)
180 {
181         g_return_val_if_fail (value, NULL);
182         return value->content_type;
183 }
184
185 /**
186  * secret_value_ref:
187  * @value: value to reference
188  *
189  * Add another reference to the #SecretValue. For each reference
190  * secret_value_unref() should be called to unreference the value.
191  *
192  * Returns: (transfer full): the value
193  */
194 SecretValue *
195 secret_value_ref (SecretValue *value)
196 {
197         g_return_val_if_fail (value, NULL);
198         g_atomic_int_inc (&value->refs);
199         return value;
200 }
201
202 /**
203  * secret_value_unref:
204  * @value: (type Secret.Value) (allow-none): value to unreference
205  *
206  * Unreference a #SecretValue. When the last reference is gone, then
207  * the value will be freed.
208  */
209 void
210 secret_value_unref (gpointer value)
211 {
212         SecretValue *val = value;
213
214         g_return_if_fail (value != NULL);
215
216         if (g_atomic_int_dec_and_test (&val->refs)) {
217                 g_free (val->content_type);
218                 if (val->destroy)
219                         (val->destroy) (val->secret);
220                 g_slice_free (SecretValue, val);
221         }
222 }
223
224 static gboolean
225 is_password_value (SecretValue *value)
226 {
227         if (value->content_type && g_str_equal (value->content_type, "text/plain"))
228                 return TRUE;
229
230         /* gnome-keyring-daemon used to return passwords like this, so support this, but validate */
231         if (!value->content_type || g_str_equal (value->content_type, "application/octet-stream"))
232                 return g_utf8_validate (value->secret, value->length, NULL);
233
234         return FALSE;
235 }
236
237 gchar *
238 _secret_value_unref_to_password (SecretValue *value)
239 {
240         SecretValue *val = value;
241         gchar *result;
242
243         g_return_val_if_fail (value != NULL, NULL);
244
245         if (!is_password_value (value)) {
246                 secret_value_unref (value);
247                 return NULL;
248         }
249
250         if (g_atomic_int_dec_and_test (&val->refs)) {
251                 if (val->destroy == egg_secure_free) {
252                         result = val->secret;
253
254                 } else {
255                         result = egg_secure_strndup (val->secret, val->length);
256                         if (val->destroy)
257                                 (val->destroy) (val->secret);
258                 }
259                 g_free (val->content_type);
260                 g_slice_free (SecretValue, val);
261
262         } else {
263                 result = egg_secure_strndup (val->secret, val->length);
264         }
265
266         return result;
267 }
268
269 gchar *
270 _secret_value_unref_to_string (SecretValue *value)
271 {
272         SecretValue *val = value;
273         gchar *result;
274
275         g_return_val_if_fail (value != NULL, NULL);
276
277         if (!is_password_value (value)) {
278                 secret_value_unref (value);
279                 return NULL;
280         }
281
282         if (g_atomic_int_dec_and_test (&val->refs)) {
283                 if (val->destroy == g_free) {
284                         result = val->secret;
285
286                 } else {
287                         result = g_strndup (val->secret, val->length);
288                         if (val->destroy)
289                                 (val->destroy) (val->secret);
290                 }
291                 g_free (val->content_type);
292                 g_slice_free (SecretValue, val);
293
294         } else {
295                 result = g_strndup (val->secret, val->length);
296         }
297
298         return result;
299 }