Update to upstream 1.0.1
[profile/ivi/gsignond.git] / src / common / gsignond-utils.c
1 /* vi: set et sw=4 ts=4 cino=t0,(0: */
2 /* -*- Mode: C; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 /*
4  * This file is part of gsignond
5  *
6  * Copyright (C) 2013 Intel Corporation.
7  *
8  * Contact: Jussi Laako <jussi.laako@linux.intel.com>
9  *
10  * This library is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU Lesser General Public
12  * License as published by the Free Software Foundation; either
13  * version 2.1 of the License, or (at your option) any later version.
14  *
15  * This library is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18  * Lesser General Public License for more details.
19  *
20  * You should have received a copy of the GNU Lesser General Public
21  * License along with this library; if not, write to the Free Software
22  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
23  * 02110-1301 USA
24  */
25
26 #include <stdio.h>
27 #include <string.h>
28 #include <time.h>
29 #include <fcntl.h>
30 #include <unistd.h>
31 #include <sys/stat.h>
32
33 #include "gsignond/gsignond-utils.h"
34 #include "gsignond/gsignond-log.h"
35
36 /**
37  * SECTION:gsignond-utils
38  * @title: Utility functions
39  * @short_description: miscellaneous utility functions
40  * @include: gsignond/gsignond-utils.h
41  *
42  * Miscellaneous utility functions are described below.
43  */
44
45 typedef struct __nonce_ctx_t
46 {
47     gboolean initialized;
48     guint32 serial;
49     guchar key[32];
50     guchar entropy[16];
51 } _nonce_ctx_t;
52
53 static size_t pagesize = 0;
54 static _nonce_ctx_t _nonce_ctx = { 0, };
55 G_LOCK_DEFINE_STATIC (_nonce_lock);
56
57 /**
58  * gsignond_wipe_file:
59  * @filename: filename to wipe
60  *
61  * This function securely wipes the contents of the file, by overwriting it with
62  * 0's, then 1's, then random data. The file is then removed.
63  *
64  * Returns: TRUE if wiping and removal was successful.
65  */
66 gboolean
67 gsignond_wipe_file (const gchar *filename)
68 {
69     gboolean retval = FALSE;
70     int rngfd;
71     int wipefd;
72     size_t sizeleft;
73     size_t writesize;
74     ssize_t sizewritten;
75     struct stat filestat;
76     guint8 *wipebuf;
77
78     if (!pagesize) {
79         long confval = sysconf (_SC_PAGE_SIZE);
80         if (confval <= 0)
81             return FALSE;
82         pagesize = (size_t) confval;
83     }
84
85     rngfd = open ("/dev/urandom", O_RDONLY);
86     if (rngfd < 0)
87         return FALSE;
88
89     wipefd = open (filename, O_WRONLY | O_SYNC);
90     if (wipefd < 0)
91         goto _rng_exit;
92     wipebuf = g_malloc (pagesize);
93     if (fstat (wipefd, &filestat))
94         goto _wipe_exit;
95
96     /* write all 1's */
97     sizeleft = filestat.st_size;
98     memset (wipebuf, 0xff, pagesize);
99     while (sizeleft) {
100         writesize = (sizeleft < pagesize) ? sizeleft : pagesize;
101         sizewritten = write (wipefd, wipebuf, writesize);
102         if (sizewritten != (ssize_t) writesize)
103             goto _wipe_exit;
104         sizeleft -= sizewritten;
105     }
106
107     if (lseek (wipefd, 0, SEEK_SET) == (off_t) -1)
108         goto _wipe_exit;
109
110     /* write all 0's */
111     sizeleft = filestat.st_size;
112     memset (wipebuf, 0x00, pagesize);
113     while (sizeleft) {
114         writesize = (sizeleft < pagesize) ? sizeleft : pagesize;
115         sizewritten = write (wipefd, wipebuf, writesize);
116         if (sizewritten != (ssize_t) writesize)
117             goto _wipe_exit;
118         sizeleft -= sizewritten;
119     }
120
121     if (lseek (wipefd, 0, SEEK_SET) == (off_t) -1)
122         goto _wipe_exit;
123
124     /* write random */
125     sizeleft = filestat.st_size;
126     while (sizeleft) {
127         writesize = (sizeleft < pagesize) ? sizeleft : pagesize;
128         if (read (rngfd, wipebuf, writesize) != (ssize_t) writesize)
129             goto _wipe_exit;
130         sizewritten = write (wipefd, wipebuf, writesize);
131         if (sizewritten != (ssize_t) writesize)
132             goto _wipe_exit;
133         sizeleft -= sizewritten;
134     }
135
136     /* don't leave traces of last pattern to the memory */
137     memset (wipebuf, 0x00, pagesize);
138
139     /* remove the file and set return value on success */
140     if (unlink (filename) == 0) {
141         retval = TRUE;
142         DBG ("successfully wiped file %s", filename);
143     }
144
145 _wipe_exit:
146     g_free (wipebuf);
147     close (wipefd);
148 _rng_exit:
149     close (rngfd);
150     return retval;
151 }
152
153 /**
154  * gsignond_wipe_directory:
155  * @dirname: directory to wipe
156  *
157  * This function securely wipes the contents of the directory by calling
158  * gsignond_wipe_file() on each file. It also removes links and empty directories but 
159  * does not recursively wipe them.
160  *
161  * Returns: TRUE if wiping and removal was successful.
162  */
163 gboolean
164 gsignond_wipe_directory (const gchar *dirname)
165 {
166     gboolean retval = FALSE;
167     gboolean wiperes;
168     const gchar *filename;
169     gchar *filepath;
170     GDir *dirctx;
171     struct stat stat_entry;
172
173     DBG ("wipe directory %s", dirname);
174     dirctx = g_dir_open (dirname, 0, NULL);
175     if (!dirctx)
176         return FALSE;
177     while ((filename = g_dir_read_name (dirctx))) {
178         filepath = g_build_filename (dirname, filename, NULL);
179         if (lstat(filepath, &stat_entry))
180             goto _dir_exit;
181         if (S_ISDIR (stat_entry.st_mode) ||
182             S_ISLNK (stat_entry.st_mode)) {
183             DBG ("remove directory or link %s", filepath);
184             wiperes = (remove (filepath) == 0);
185         } else {
186             DBG ("wipe file %s", filepath);
187             wiperes = gsignond_wipe_file (filepath);
188         }
189         g_free (filepath);
190         if (!wiperes)
191             goto _dir_exit;
192     }
193     retval = TRUE;
194
195 _dir_exit:
196     g_dir_close (dirctx);
197     return retval;
198 }
199
200 static gboolean
201 _init_nonce_gen ()
202 {
203     if (G_LIKELY(_nonce_ctx.initialized))
204         return TRUE;
205
206     int fd;
207
208     fd = open ("/dev/urandom", O_RDONLY);
209     if (fd < 0)
210         goto init_exit;
211     if (read (fd, _nonce_ctx.key, sizeof (_nonce_ctx.key)) !=
212         sizeof (_nonce_ctx.key))
213         goto init_close;
214     if (read (fd, _nonce_ctx.entropy, sizeof(_nonce_ctx.entropy)) !=
215         sizeof (_nonce_ctx.entropy))
216         goto init_close;
217
218     _nonce_ctx.serial = 0;
219
220     _nonce_ctx.initialized = TRUE;
221
222 init_close:
223     close (fd);
224
225 init_exit:
226     return _nonce_ctx.initialized;
227 }
228
229 /**
230  * gsignond_generate_nonce:
231  *
232  * This function generates a random secure nonce using SHA1 HMAC.
233  *
234  * Returns: (transfer full): the nonce in lowercase hexadecimal format, 40 bytes long.
235  */
236 gchar *
237 gsignond_generate_nonce ()
238 {
239     GHmac *hmac;
240     gchar *nonce = NULL;
241     struct timespec ts;
242
243     G_LOCK (_nonce_lock);
244
245     if (G_UNLIKELY (!_init_nonce_gen()))
246         goto nonce_exit;
247
248     hmac = g_hmac_new (G_CHECKSUM_SHA1,
249                        _nonce_ctx.key, sizeof (_nonce_ctx.key));
250     g_hmac_update (hmac, _nonce_ctx.entropy, sizeof (_nonce_ctx.entropy));
251     _nonce_ctx.serial++;
252     g_hmac_update (hmac,
253                    (const guchar *) &_nonce_ctx.serial,
254                    sizeof (_nonce_ctx.serial));
255     if (clock_gettime (CLOCK_MONOTONIC, &ts) == 0)
256         g_hmac_update (hmac, (const guchar *) &ts, sizeof (ts));
257     memset (&ts, 0x00, sizeof(ts));
258     nonce = g_strdup (g_hmac_get_string (hmac));
259     g_hmac_unref (hmac);
260
261 nonce_exit:
262     G_UNLOCK (_nonce_lock);
263
264     return nonce;
265 }
266
267 static gint
268 _compare_strings (
269                 const gchar* a,
270                 const gchar* b,
271                 gpointer data)
272 {
273         (void)data;
274         return g_strcmp0 (a,b);
275 }
276
277 /**
278  * gsignond_sequence_to_variant:
279  * @seq: Sequence of strings to convert
280  *
281  * Convert a string sequence to a variant.
282  *
283  * Returns: (transfer full): #GVariant of type "as".
284  */
285 GVariant *
286 gsignond_sequence_to_variant (GSequence *seq)
287 {
288     GSequenceIter * iter = NULL;
289     GVariant *var = NULL;
290     GVariantBuilder builder;
291
292     if (!seq) return NULL;
293
294     g_variant_builder_init (&builder, G_VARIANT_TYPE_STRING_ARRAY);
295     iter = g_sequence_get_begin_iter (seq);
296     while (!g_sequence_iter_is_end (iter)) {
297         const gchar * d = g_sequence_get (iter);
298         g_variant_builder_add (&builder, "s", d);
299         iter = g_sequence_iter_next (iter);
300     }
301     var = g_variant_builder_end (&builder);
302     return var;
303 }
304
305 /**
306  * gsignond_variant_to_sequence:
307  * @var: Variant of "as" to convert
308  *
309  * Convert variant containing string array to sequence.
310  *
311  * Returns: (transfer full): #GSequence of strings
312  */
313 GSequence *
314 gsignond_variant_to_sequence (GVariant *var)
315 {
316     GVariantIter iter;
317     GSequence *seq = NULL;
318     gchar *item = NULL;
319
320     if (!var) return NULL;
321
322     seq = g_sequence_new ((GDestroyNotify)g_free);
323     g_variant_iter_init (&iter, var);
324     while (g_variant_iter_next (&iter, "s", &item)) {
325         g_sequence_insert_sorted (seq,
326                                   item,
327                                   (GCompareDataFunc) _compare_strings,
328                                   NULL);
329     }
330     return seq;
331 }
332
333 /**
334  * gsignond_sequence_to_array:
335  * @seq: Sequence of strings to convert
336  *
337  * Convert sequence of strings to null-terminated string array.
338  *
339  * Returns: (transfer full): Null-terminated array of strings
340  */
341 gchar **
342 gsignond_sequence_to_array (GSequence *seq)
343 {
344     gchar **items, **temp;
345     GSequenceIter *iter;
346
347     if (!seq) return NULL;
348
349     items = g_malloc0 ((g_sequence_get_length (seq) + 1) * sizeof (gchar *));
350     temp = items;
351     for (iter = g_sequence_get_begin_iter (seq);
352          iter != g_sequence_get_end_iter (seq);
353          iter = g_sequence_iter_next (iter)) {
354         *temp = g_sequence_get (iter);
355         temp++;
356     }
357     return items;
358 }
359
360 /**
361  * gsignond_array_to_sequence:
362  * @items: (transfer full): Null-terminated array of strings to convert
363  *
364  * Convert null-terminated array of strings to a sequence.
365  *
366  * Returns: (transfer full): #GSequence of strings
367  */
368 GSequence *
369 gsignond_array_to_sequence (gchar **items)
370 {
371     gchar **item_iter = items;
372     GSequence *seq = NULL;
373
374     if (!items) return NULL;
375
376     seq = g_sequence_new ((GDestroyNotify) g_free);
377     while (*item_iter) {
378         g_sequence_insert_sorted (seq,
379                                   *item_iter,
380                                   (GCompareDataFunc) _compare_strings,
381                                   NULL);
382         item_iter++;
383     }
384     g_free (items);
385     return seq;
386 }
387
388 /**
389  * gsignond_copy_array_to_sequence:
390  * @items: Null-terminated array of strings to copy
391  *
392  * Copy null-terminated array of strings to a sequence.
393  *
394  * Returns: (transfer full): #GSequence of strings
395  */
396 GSequence *
397 gsignond_copy_array_to_sequence (const gchar **items)
398 {
399     GSequence *seq = NULL;
400
401     if (!items) return NULL;
402
403     seq = g_sequence_new ((GDestroyNotify) g_free);
404     while (*items) {
405         g_sequence_insert_sorted (seq,
406                                   g_strdup (*items),
407                                   (GCompareDataFunc) _compare_strings,
408                                   NULL);
409         items++;
410     }
411     return seq;
412 }
413
414 /**
415  * gsignond_is_host_in_domain:
416  * @domain: a domain name
417  * @host: a host name
418  *
419  * Checks if @host belongs to @domain.
420  *
421  * Returns: the result
422  */
423 gboolean 
424 gsignond_is_host_in_domain(const gchar *host, const gchar *domain)
425 {
426     gchar** domain_parts = g_strsplit(domain, ".", 0);
427     gchar** host_parts = g_strsplit(host, ".", 0);
428     gchar** truncated_host_parts = host_parts;
429     
430     guint domain_parts_n = g_strv_length(domain_parts);
431     guint host_parts_n = g_strv_length(host_parts);
432     
433     gint extra_host_parts_n = host_parts_n - domain_parts_n;
434     
435     while (extra_host_parts_n > 0) {
436         truncated_host_parts++;
437         extra_host_parts_n--;
438     }
439     gchar* truncated_host = g_strjoinv(".", truncated_host_parts);
440     gint result = g_strcmp0(domain, truncated_host);
441     
442     g_free(truncated_host);
443     g_strfreev(host_parts);
444     g_strfreev(domain_parts);
445     
446     return result == 0 ? TRUE : FALSE;
447 }