df84032f32c85c207ad2be4fa25aa166c1761028
[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