Remove GLib dependency.
[pwmd.git] / src / convert.c
blob74a3772e194827a8415abb01fafb9a7417f53cb0
1 /*
2 Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2012
3 Ben Kibbey <bjk@luxsci.net>
5 This file is part of pwmd.
7 Pwmd is free software: you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation, either version 2 of the License, or
10 (at your option) any later version.
12 Pwmd is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with Pwmd. If not, see <http://www.gnu.org/licenses/>.
20 #ifdef HAVE_CONFIG_H
21 #include <config.h>
22 #endif
24 #include <fcntl.h>
25 #include <sys/stat.h>
26 #include <zlib.h>
28 #include "pwmd-error.h"
29 #include <gcrypt.h>
30 #include "agent.h"
31 #include "convert.h"
32 #include "xml.h"
33 #include "mem.h"
34 #include "util-misc.h"
36 #ifndef _
37 #include "gettext.h"
38 #define _(msgid) gettext(msgid)
39 #endif
41 #define KEYSIZE 32
43 typedef struct {
44 uint8_t magic[5];
45 uint16_t version;
46 uint64_t iter;
47 uint64_t flags;
48 uint8_t iv[16];
49 uint8_t salt[8];
50 } v2_file_header_t;
52 struct gz_s {
53 z_stream z;
54 void * out;
55 int done;
58 static unsigned char crypto_magic[5] = "\177PWMD";
60 static void gz_cleanup(void *arg)
62 struct gz_s **gz = (struct gz_s **)arg;
64 if (!gz)
65 return;
67 if (!(*gz)->done && (*gz)->out)
68 gcry_free((*gz)->out);
70 if ((*gz)->z.zalloc)
71 inflateEnd(&(*gz)->z);
73 xfree(*gz);
74 *gz = NULL;
77 static void *z_alloc(void *data, unsigned items, unsigned size)
79 return gcry_calloc(items, size);
82 static void z_free(void *data, void *p)
84 gcry_free(p);
87 #define ZLIB_BUFSIZE 65536
89 static gpg_error_t decompress(void * in, unsigned long insize, void * *out,
90 unsigned long *outsize)
92 struct gz_s *gz;
93 gz_header h;
94 char buf[17];
95 gpg_error_t rc;
96 int zrc;
98 gz = xcalloc(1, sizeof(struct gz_s));
99 if (!gz)
100 return GPG_ERR_ENOMEM;
102 gz->z.zalloc = z_alloc;
103 gz->z.zfree = z_free;
104 gz->z.next_in = in;
105 gz->z.avail_in = (uInt)insize;
106 gz->z.avail_out = ZLIB_BUFSIZE;
107 gz->z.next_out = gz->out = gcry_malloc(ZLIB_BUFSIZE);
108 if (!gz->out) {
109 gz_cleanup(&gz);
110 return GPG_ERR_ENOMEM;
113 zrc = inflateInit2(&gz->z, 47);
114 if (zrc != Z_OK) {
115 gz_cleanup(&gz);
116 return zrc == Z_MEM_ERROR ? GPG_ERR_ENOMEM : GPG_ERR_COMPR_ALGO;
119 memset(&h, 0, sizeof(gz_header));
120 h.comment = (unsigned char *)buf;
121 h.comm_max = sizeof(buf);
122 zrc = inflateGetHeader(&gz->z, &h);
123 if (zrc != Z_OK) {
124 gz_cleanup(&gz);
125 return zrc == Z_MEM_ERROR ? GPG_ERR_ENOMEM : GPG_ERR_COMPR_ALGO;
128 zrc = inflate(&gz->z, Z_BLOCK);
129 if (zrc != Z_OK) {
130 gz_cleanup(&gz);
131 return zrc == Z_MEM_ERROR ? GPG_ERR_ENOMEM : GPG_ERR_COMPR_ALGO;
134 if (h.comment)
135 insize = strtoul((char *)h.comment, NULL, 10);
137 do {
138 void * p;
140 zrc = inflate(&gz->z, Z_FINISH);
141 switch (zrc) {
142 case Z_OK:
143 break;
144 case Z_BUF_ERROR:
145 if (!gz->z.avail_out) {
146 p = gcry_realloc(gz->out, gz->z.total_out + ZLIB_BUFSIZE);
147 if (!p) {
148 rc = GPG_ERR_ENOMEM;
149 goto fail;
152 gz->out = p;
153 gz->z.next_out = (unsigned char *)gz->out + gz->z.total_out;
154 gz->z.avail_out = ZLIB_BUFSIZE;
156 else {
157 rc = GPG_ERR_COMPR_ALGO;
158 goto fail;
161 break;
162 case Z_STREAM_END:
163 break;
164 default:
165 rc = GPG_ERR_COMPR_ALGO;
166 goto fail;
167 break;
169 } while (zrc != Z_STREAM_END);
171 *out = gz->out;
172 *outsize = gz->z.total_out;
173 gz->done = 1;
174 gz_cleanup(&gz);
175 return 0;
177 fail:
178 gz_cleanup(&gz);
179 return rc;
182 static gpg_error_t decrypt(v2_file_header_t *fh, unsigned char *key,
183 unsigned char **ciphertext, size_t datalen, void * *result,
184 size_t *result_len)
186 gpg_error_t rc;
187 size_t blocksize, keysize, len;
188 int algo = cipher_to_gcrypt(fh->flags);
189 gcry_cipher_hd_t gh = NULL;
190 uint64_t iter = 0ULL;
191 unsigned char *data = *ciphertext;
193 *result = NULL;
194 *result_len = 0;
196 /* No encryption iterations. This is a plain (gzipped) file. */
197 if (!fh->iter)
198 goto decompress;
200 if (algo == -1)
201 return GPG_ERR_CIPHER_ALGO;
203 rc = gcry_cipher_algo_info(algo, GCRYCTL_TEST_ALGO, NULL, NULL);
204 if (rc)
205 return rc;
207 rc = gcry_cipher_algo_info(algo, GCRYCTL_GET_KEYLEN, NULL, &keysize);
208 if (rc)
209 return rc;
211 rc = gcry_cipher_algo_info(algo, GCRYCTL_GET_BLKLEN, NULL, &blocksize);
212 if (rc)
213 return rc;
215 rc = gcry_cipher_open(&gh, algo, GCRY_CIPHER_MODE_CBC, 0);
216 if (rc)
217 return rc;
219 if ((rc = gcry_cipher_setiv(gh, fh->iv, blocksize)))
220 goto fail;
222 if ((rc = gcry_cipher_setkey(gh, key, keysize)))
223 goto fail;
225 rc = gcry_cipher_decrypt(gh, data, datalen, NULL, 0);
226 if (rc)
227 goto fail;
229 key[0] ^= 1;
230 if ((rc = gcry_cipher_setkey(gh, key, keysize)))
231 goto fail;
233 while (++iter < fh->iter) {
234 if ((rc = gcry_cipher_setiv(gh, fh->iv, blocksize)))
235 goto fail;
237 rc = gcry_cipher_decrypt(gh, data, datalen, NULL, 0);
238 if (rc)
239 goto fail;
242 decompress:
243 len = 0;
244 if (fh->version >= 0x218 && fh->iter > 0ULL) {
245 if (memcmp(data, crypto_magic, sizeof(crypto_magic))) {
246 rc = GPG_ERR_BAD_PASSPHRASE;
247 goto fail;
250 len = sizeof(crypto_magic);
253 rc = decompress(data+len, datalen-len, result, (unsigned long *)result_len);
254 if (rc) {
255 if (fh->version < 0x218 && rc == GPG_ERR_COMPR_ALGO)
256 rc = GPG_ERR_BAD_PASSPHRASE;
257 goto fail;
260 if (strncasecmp((char *)*result, "<?xml ", 6) != 0) {
261 gcry_free(*result);
262 *result = NULL;
263 *result_len = 0;
264 rc = GPG_ERR_BAD_PASSPHRASE;
265 goto fail;
268 fail:
269 if (gh)
270 gcry_cipher_close(gh);
272 return rc;
275 gpg_error_t read_v2_datafile(const char *filename, const char *keyfile,
276 void * *result, size_t *result_len, uint16_t *ver, int *algo)
278 gpg_error_t rc;
279 int fd;
280 v2_file_header_t fh;
281 size_t len, passphraselen, datalen;
282 struct agent_s *agent = NULL;
283 char *passphrase = NULL;
284 unsigned char *data = NULL;
285 struct stat st;
286 unsigned char *key = NULL;
287 FILE *fp;
289 if (stat(filename, &st) == -1)
290 return gpg_error_from_syserror();
292 fd = open(filename, O_RDONLY);
293 if (fd == -1)
294 return gpg_error_from_syserror();
296 len = read(fd, &fh, sizeof(fh)-8);
297 if (len != sizeof(fh)-8) {
298 rc = GPG_ERR_INV_LENGTH;
299 goto fail;
302 if (fh.version >= 0x221) {
303 len = read(fd, &fh.salt, 8);
304 if (len != 8) {
305 rc = GPG_ERR_INV_LENGTH;
306 goto fail;
310 *ver = fh.version;
311 *algo = cipher_to_gcrypt(fh.flags);
312 rc = agent_init(&agent);
313 if (rc)
314 goto fail;
316 if (!keyfile && fh.iter > 0) {
317 char *desc = plus_escape(_(
318 "A passphrase is required to decrypt the file for converting. "
319 "Please enter the passphrase below."));
321 rc = set_pinentry_options(agent);
322 if (rc)
323 goto fail;
325 assuan_begin_confidential(agent->ctx);
326 rc = send_to_agent(agent, &passphrase, &passphraselen,
327 "GET_PASSPHRASE --data pwmd:convert + %s %s",
328 _("Passphrase:"), desc);
329 assuan_end_confidential(agent->ctx);
330 xfree(desc);
331 if (rc)
332 goto fail;
334 passphraselen--; // null byte
335 send_to_agent(agent, NULL, NULL, "CLEAR_PASSPHRASE pwmd:convert");
336 cleanup_agent(agent);
337 agent = NULL;
339 else if (keyfile) {
340 struct stat pst;
341 int pfd = open(keyfile, O_RDONLY);
343 if (pfd == -1) {
344 rc = gpg_error_from_syserror();
345 goto fail;
348 if (stat(keyfile, &pst) == -1) {
349 rc = gpg_error_from_syserror();
350 close(pfd);
351 goto fail;
354 passphrase = xmalloc(pst.st_size);
355 if (!passphrase) {
356 rc = GPG_ERR_ENOMEM;
357 close(pfd);
358 goto fail;
361 passphraselen = read(pfd, passphrase, pst.st_size);
362 close(pfd);
363 if (passphraselen != pst.st_size) {
364 rc = GPG_ERR_INV_LENGTH;
365 goto fail;
369 key = gcry_malloc(KEYSIZE);
370 if (!key) {
371 rc = GPG_ERR_ENOMEM;
372 goto fail;
375 fp = fdopen(fd, "r");
376 len = st.st_size - ftell(fp);
378 if (fh.version >= 0x221 && fh.version < 0x0300) {
379 rc = gcry_kdf_derive(passphrase, passphraselen, GCRY_KDF_ITERSALTED_S2K,
380 GCRY_MD_SHA1, fh.salt, 8, 1000, KEYSIZE, key);
381 if (rc)
382 goto fail;
384 else
385 gcry_md_hash_buffer(GCRY_MD_SHA256, key, passphrase, passphraselen);
387 data = gcry_malloc(len);
388 if (!data) {
389 rc = GPG_ERR_ENOMEM;
390 goto fail;
393 datalen = read(fd, data, len);
394 if (datalen != len) {
395 rc = GPG_ERR_INV_LENGTH;
396 goto fail;
399 fprintf(stderr, _("Decrypting ...\n"));
400 rc = decrypt(&fh, key, &data, datalen, result, result_len);
402 fail:
403 gcry_free(key);
404 gcry_free(data);
405 xfree(passphrase);
407 if (agent)
408 cleanup_agent(agent);
410 if (fd != -1)
411 close(fd);
412 return rc;