gwenhywfar  4.7.0beta
passwdstore.c
Go to the documentation of this file.
1 /***************************************************************************
2  begin : Sat Dec 16 2012
3  copyright : (C) 2012 by Martin Preuss
4  email : martin@libchipcard.de
5 
6  ***************************************************************************
7  * *
8  * This library is free software; you can redistribute it and/or *
9  * modify it under the terms of the GNU Lesser General Public *
10  * License as published by the Free Software Foundation; either *
11  * version 2.1 of the License, or (at your option) any later version. *
12  * *
13  * This library is distributed in the hope that it will be useful, *
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of *
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
16  * Lesser General Public License for more details. *
17  * *
18  * You should have received a copy of the GNU Lesser General Public *
19  * License along with this library; if not, write to the Free Software *
20  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, *
21  * MA 02111-1307 USA *
22  * *
23  ***************************************************************************/
24 
25 
26 #ifdef HAVE_CONFIG_H
27 # include <config.h>
28 #endif
29 
30 #include "passwdstore_p.h"
31 #include "i18n_l.h"
32 
33 #include <gwenhywfar/gui.h>
34 #include <gwenhywfar/db.h>
35 #include <gwenhywfar/directory.h>
36 #include <gwenhywfar/fslock.h>
37 #include <gwenhywfar/mdigest.h>
38 #include <gwenhywfar/text.h>
39 #include <gwenhywfar/debug.h>
40 #include <gwenhywfar/smalltresor.h>
41 
42 #include <errno.h>
43 
44 
45 
47  GWEN_PASSWD_STORE *sto;
48 
50  if (fname)
51  sto->fileName=strdup(fname);
52  sto->dbPasswords=NULL;
53 
54  return sto;
55 }
56 
57 
58 
60  if (sto) {
61  memset(sto->pw, 0, sizeof(sto->pw));
62  if (sto->dbPasswords) {
64  GWEN_DB_Group_free(sto->dbPasswords);
65  sto->dbPasswords=NULL;
66  }
67  free(sto->fileName);
68  GWEN_FREE_OBJECT(sto);
69  }
70 }
71 
72 
73 
75  assert(sto);
76  memset(sto->pw, 0, GWEN_PASSWDSTORE_PWLEN);
77  if (sto->dbPasswords) {
79  GWEN_DB_Group_free(sto->dbPasswords);
80  sto->dbPasswords=NULL;
81  }
82 }
83 
84 
85 
86 
87 static int readFile(const char *fname, GWEN_BUFFER *dbuf) {
88  FILE *f;
89 
90  f=fopen(fname, "rb");
91  if (f) {
92  while(!feof(f)) {
93  uint32_t l;
94  ssize_t s;
95  char *p;
96 
97  GWEN_Buffer_AllocRoom(dbuf, 1024);
100  s=fread(p, 1, l, f);
101  if (s==0)
102  break;
103  if (s==(ssize_t)-1) {
105  "fread(%s): %s",
106  fname, strerror(errno));
107  fclose(f);
108  return GWEN_ERROR_IO;
109  }
110 
111  GWEN_Buffer_IncrementPos(dbuf, s);
113  }
114 
115  fclose(f);
116  return 0;
117  }
118  else {
119  if (errno==ENOENT) {
120  DBG_INFO(GWEN_LOGDOMAIN, "File [%s] does not exist", fname);
121  return GWEN_ERROR_NOT_FOUND;
122  }
123  else {
125  "fopen(%s): %s",
126  fname, strerror(errno));
127  return GWEN_ERROR_IO;
128  }
129  }
130 }
131 
132 
133 
134 static int writeToFile(FILE *f, const char *p, int len) {
135  while(len>0) {
136  ssize_t l;
137  ssize_t s;
138 
139  l=1024;
140  if (l>len)
141  l=len;
142  s=fwrite(p, 1, l, f);
143  if (s==(ssize_t)-1 || s==0) {
145  "fwrite: %s",
146  strerror(errno));
147  return GWEN_ERROR_IO;
148  }
149  p+=s;
150  len-=s;
151  }
152 
153  return 0;
154 }
155 
156 
157 
158 static int writeFile(const char *fname, const char *p, int len) {
159  FILE *f;
160 
161  f=fopen(fname, "wb");
162  if (f) {
163  int rv;
164 
165  rv=writeToFile(f, p, len);
166  if (rv<0) {
167  DBG_ERROR(GWEN_LOGDOMAIN, "here (%d)", rv);
168  fclose(f);
169  return rv;
170  }
171  if (fclose(f)) {
172  DBG_ERROR(GWEN_LOGDOMAIN, "here (%d)", rv);
173  return rv;
174  }
175  }
176  else {
177  DBG_ERROR(GWEN_LOGDOMAIN, "fopen(%s): %s",
178  fname, strerror(errno));
179  return GWEN_ERROR_IO;
180  }
181 
182  return 0;
183 }
184 
185 
186 
187 static int GWEN_PasswordStore_Digest(const uint8_t *t, uint32_t size, GWEN_BUFFER *buf) {
188  GWEN_MDIGEST *md;
189  int rv;
190 
191  /* hash token and pin */
193  rv=GWEN_MDigest_Begin(md);
194  if (rv==0)
195  rv=GWEN_MDigest_Update(md, (const uint8_t*)t, size);
196  if (rv==0)
197  rv=GWEN_MDigest_End(md);
198  if (rv<0) {
199  DBG_ERROR(GWEN_LOGDOMAIN, "Hash error (%d)", rv);
200  GWEN_MDigest_free(md);
201  return rv;
202  }
203 
205  (const char*)GWEN_MDigest_GetDigestPtr(md),
207  GWEN_MDigest_free(md);
208  return 0;
209 }
210 
211 
212 
213 static int GWEN_PasswordStore_CheckDigest(const uint8_t *t, uint32_t size, const uint8_t *h) {
214  GWEN_MDIGEST *md;
215  int rv;
216 
217  /* hash token and pin */
219  rv=GWEN_MDigest_Begin(md);
220  if (rv==0)
221  rv=GWEN_MDigest_Update(md, (const uint8_t*)t, size);
222  if (rv==0)
223  rv=GWEN_MDigest_End(md);
224  if (rv<0) {
225  DBG_ERROR(GWEN_LOGDOMAIN, "Hash error (%d)", rv);
226  GWEN_MDigest_free(md);
227  return rv;
228  }
229 
230  if (memcmp(h, GWEN_MDigest_GetDigestPtr(md), 20)) {
231  DBG_ERROR(GWEN_LOGDOMAIN, "Bad hash");
232  GWEN_MDigest_free(md);
233  return GWEN_ERROR_BAD_DATA;
234  }
235 
236  GWEN_MDigest_free(md);
237  return 0;
238 }
239 
240 
241 
243  if (sto->dbPasswords) {
245  GWEN_DB_Group_free(sto->dbPasswords);
246  sto->dbPasswords=NULL;
247  }
248 }
249 
250 
251 
253  int rv;
254  GWEN_BUFFER *sbuf;
255 
256  sbuf=GWEN_Buffer_new(0, 256, 0, 1);
257  rv=readFile(sto->fileName, sbuf);
258  if (rv<0) {
259  DBG_INFO(GWEN_LOGDOMAIN, "here (%d)", rv);
260  GWEN_Buffer_free(sbuf);
261  return rv;
262  }
263 
264  if (GWEN_Buffer_GetUsedBytes(sbuf)<1) {
265  DBG_INFO(GWEN_LOGDOMAIN, "Empty file");
266  GWEN_Buffer_free(sbuf);
267  return GWEN_ERROR_NO_DATA;
268  }
269 
270  for (;;) {
271  GWEN_BUFFER *tbuf;
272  uint32_t pos1;
273  uint32_t pos2;
274  uint32_t len;
275 
276  tbuf=GWEN_Buffer_new(0, 256, 0, 1);
277  GWEN_Buffer_AppendString(tbuf, "PASSWORD_STORE_");
278  GWEN_Text_UnescapeToBufferTolerant(sto->fileName, tbuf);
279 
280  if (sto->pw[0]==0) {
282  I18N("Enter Password"),
283  I18N("Please enter the password for the password store.\n"
284  "<html>"
285  "Please enter the password for the <b>password store</b>.</br>"
286  "</html>"),
287  sto->pw,
288  4,
289  sizeof(sto->pw)-1,
290  0);
291  if (rv<0) {
292  DBG_INFO(GWEN_LOGDOMAIN, "here (%d)", rv);
293  GWEN_Buffer_free(tbuf);
294  GWEN_Buffer_free(sbuf);
295  return rv;
296  }
297  }
298 
299  pos1=GWEN_Buffer_GetPos(secbuf);
300 
301  rv=GWEN_SmallTresor_Decrypt((const uint8_t*) GWEN_Buffer_GetStart(sbuf),
303  sto->pw,
304  secbuf,
305  GWEN_PASSWDSTORE_PW_ITERATIONS,
306  GWEN_PASSWDSTORE_CRYPT_ITERATIONS);
307  GWEN_Buffer_free(tbuf);
308  if (rv<0) {
309  DBG_INFO(GWEN_LOGDOMAIN, "here (%d)", rv);
310  /* error, reset password */
311  memset(sto->pw, 0, sizeof(sto->pw));
312  }
313  else {
314  /* check and remove hash */
315  pos2=GWEN_Buffer_GetPos(secbuf);
316  len=pos2-pos1;
317 
318  if (len>=20) {
319  const uint8_t *p1;
320  const uint8_t *p2;
321 
322  p1=(const uint8_t*)GWEN_Buffer_GetStart(secbuf)+pos1; /* start of decrypted data */
323  p2=(const uint8_t*)GWEN_Buffer_GetStart(secbuf)+(pos2-20); /* start of hash */
324 
325  rv=GWEN_PasswordStore_CheckDigest(p1, len-20, p2);
326  if (rv<0) {
327  DBG_INFO(GWEN_LOGDOMAIN, "here (%d)", rv);
328  GWEN_Buffer_free(sbuf);
329  return rv;
330  }
331  else {
332  GWEN_Buffer_Crop(secbuf, 0, pos2-20);
333  break;
334  }
335  }
336  else {
337  DBG_ERROR(GWEN_LOGDOMAIN, "Bad data size (smaller than 20 bytes)");
338  /* reset buffer */
339  GWEN_Buffer_Crop(secbuf, 0, pos1);
340  GWEN_Buffer_free(sbuf);
341  return rv;
342  }
343  }
344  } /* for */
345 
346  GWEN_Buffer_free(sbuf);
347 
348  return 0;
349 }
350 
351 
352 
353 static int GWEN_PasswordStore_EncryptWriteFile(GWEN_PASSWD_STORE *sto, const uint8_t *sec, uint32_t len) {
354  int rv;
355  GWEN_BUFFER *sbuf;
356  GWEN_BUFFER *tbuf;
357 
358  /* make sure the data dir exists */
359  DBG_ERROR(0, "Looking for [%s]", sto->fileName);
361  if (rv<0) {
362  DBG_INFO(GWEN_LOGDOMAIN, "here (%d)", rv);
363  return rv;
364  }
365 
366  tbuf=GWEN_Buffer_new(0, 256, 0, 1);
367  GWEN_Buffer_AppendString(tbuf, "PASSWORD_STORE_");
368  GWEN_Text_UnescapeToBufferTolerant(sto->fileName, tbuf);
369 
370  /* ask for passwd if not already set */
371  if (sto->pw[0]==0) {
372  if (sto->isNew) {
374  I18N("Create New Password Store"),
375  I18N(
376  "You are about to create a new password store.\n"
377  "Passwords you store here will be encrypted with a passphrase\n"
378  "which you must enter now.\n"
379  "\n"
380 
381  "Later you will only need to remember the passphrase for the\n"
382  "password store, not all the individuell passwords.\n"
383  "\n"
384  "WARNING: Storing your passwords in the password store\n"
385  "can be considered a security risk, especially if the passphrase protecting it\n"
386  "is not strong enough!\n"
387  "\n"
388  "You can safely abort this step, in which case your passwords will not be stored.\n"
389  "\n"
390  "Please enter the passphrase for the password store to be created or abort.\n"
391  "<html>"
392  "<p>You are about to create a new <b>password store</b>.</p>"
393  "<br>"
394  "<p>Passwords you store here will be encrypted with a passphrase "
395  "which you must enter now.</p>"
396  "<p>Later you will only need to remember the passphrase for the "
397  "password store, not all the individuell passwords.<p>"
398  "<p><font color=\"red\">"
399  "<b>Warning:</b> Storing your passwords in the password store "
400  "can be considered a <b>security risk</b>, especially if the passphrase protecting it "
401  "is not strong enough!"
402  "</font></p>"
403  "<p><b>You can safely abort this step</b>, in which case your passwords will not be stored.</p>"
404  "<br>"
405  "<p>Please enter the passphrase for the password store to be created or abort.</p>"
406  "</html>"),
407  sto->pw,
408  4,
409  sizeof(sto->pw)-1,
410  0);
411 
412  }
413  else
415  I18N("Enter Password"),
416  I18N("Please enter the password for the password store.\n"
417  "<html>"
418  "Please enter the password for the <b>password store</b>.</br>"
419  "</html>"),
420  sto->pw,
421  4,
422  sizeof(sto->pw)-1,
423  0);
424  if (rv<0) {
425  DBG_INFO(GWEN_LOGDOMAIN, "here (%d)", rv);
426  GWEN_Buffer_free(tbuf);
427  return rv;
428  }
429  }
430 
431  GWEN_Buffer_free(tbuf);
432 
433  /* prepare data to write */
434  sbuf=GWEN_Buffer_new(0, 256, 0, 1);
435  tbuf=GWEN_Buffer_new(0, len+20, 0, 1);
436 
437  /* add clear text data */
438  GWEN_Buffer_AppendBytes(tbuf, (const char*) sec, len);
439 
440  /* add hash (20 bytes) */
441  rv=GWEN_PasswordStore_Digest((const uint8_t*) sec, len, tbuf);
442  if (rv<0) {
443  DBG_INFO(GWEN_LOGDOMAIN, "here (%d)", rv);
444  GWEN_Buffer_free(tbuf);
445  GWEN_Buffer_free(sbuf);
446  return rv;
447  }
448 
449  /* encrypt cleartext */
450  rv=GWEN_SmallTresor_Encrypt((const uint8_t*) GWEN_Buffer_GetStart(tbuf),
452  sto->pw,
453  sbuf,
454  GWEN_PASSWDSTORE_PW_ITERATIONS,
455  GWEN_PASSWDSTORE_CRYPT_ITERATIONS);
456  if (rv<0) {
457  DBG_INFO(GWEN_LOGDOMAIN, "here (%d)", rv);
458  GWEN_Buffer_free(tbuf);
459  GWEN_Buffer_free(sbuf);
460  return rv;
461  }
463  GWEN_Buffer_free(tbuf);
464 
465  /* write file */
466  rv=writeFile(sto->fileName,
467  GWEN_Buffer_GetStart(sbuf),
469  if (rv<0) {
470  DBG_INFO(GWEN_LOGDOMAIN, "here (%d)", rv);
471  GWEN_Buffer_free(sbuf);
472  return rv;
473  }
474 
475  GWEN_Buffer_free(sbuf);
476 
477  return 0;
478 }
479 
480 
481 
482 
484  GWEN_BUFFER *tbuf;
485  int rv;
486 
487  tbuf=GWEN_Buffer_new(0, 256, 0, 1);
488 
490  if (rv<0) {
491  DBG_INFO(GWEN_LOGDOMAIN, "here (%d)", rv);
492  GWEN_Buffer_free(tbuf);
493  return rv;
494  }
495  sto->isNew=0;
496 
498 
499  sto->dbPasswords=GWEN_DB_Group_new("passwords");
500  rv=GWEN_DB_ReadFromString(sto->dbPasswords,
501  GWEN_Buffer_GetStart(tbuf),
504  if (rv<0) {
505  DBG_INFO(GWEN_LOGDOMAIN, "here (%d)", rv);
507  GWEN_Buffer_free(tbuf);
508  return rv;
509  }
511 
513  GWEN_Buffer_free(tbuf);
514  return 0;
515 }
516 
517 
518 
520  if (sto->dbPasswords) {
521  GWEN_BUFFER *tbuf;
522  int rv;
523 
524  tbuf=GWEN_Buffer_new(0, 256, 0, 1);
525  rv=GWEN_DB_WriteToBuffer(sto->dbPasswords, tbuf, GWEN_DB_FLAGS_DEFAULT);
526  if (rv<0) {
527  DBG_INFO(GWEN_LOGDOMAIN, "here (%d)", rv);
529  GWEN_Buffer_free(tbuf);
530  return rv;
531  }
532 
534  (const uint8_t*) GWEN_Buffer_GetStart(tbuf),
536  if (rv<0) {
537  DBG_INFO(GWEN_LOGDOMAIN, "here (%d)", rv);
539  GWEN_Buffer_free(tbuf);
540  return rv;
541  }
542 
544  GWEN_Buffer_free(tbuf);
545  return 0;
546  }
547  else {
548  DBG_INFO(GWEN_LOGDOMAIN, "No password db");
549  return GWEN_ERROR_INTERNAL;
550  }
551 }
552 
553 
554 
555 
556 static int GWEN_PasswordStore__SetPassword(GWEN_PASSWD_STORE *sto, const char *token, const char *secret) {
557  GWEN_BUFFER *buf;
558 
559  buf=GWEN_Buffer_new(0, 64, 0, 1);
561 
562  if (secret==NULL)
563  GWEN_DB_DeleteVar(sto->dbPasswords, GWEN_Buffer_GetStart(buf));
564  else
566  GWEN_Buffer_GetStart(buf), secret);
568  GWEN_Buffer_free(buf);
569 
570  return 0;
571 }
572 
573 
574 
575 static int GWEN_PasswordStore__GetPassword(GWEN_PASSWD_STORE *sto, const char *token, char *buffer, int minLen, int maxLen) {
576  GWEN_BUFFER *buf;
577  const char *s;
578 
579  buf=GWEN_Buffer_new(0, 256, 0, 1);
581 
582  s=GWEN_DB_GetCharValue(sto->dbPasswords,
584  0, NULL);
585  if (s) {
586  int i;
587 
588  i=strlen(s);
589  if (i>=minLen && i < maxLen) {
590  memmove(buffer, s, i+1);
591  GWEN_Buffer_free(buf);
592  return 0;
593  }
594  else {
595  DBG_ERROR(GWEN_LOGDOMAIN, "Stored password [%s] is not within size limits (%d), rejecting.",
596  GWEN_Buffer_GetStart(buf), i);
597  }
598  }
599 
600  GWEN_Buffer_free(buf);
601  return GWEN_ERROR_NOT_FOUND;
602 }
603 
604 
605 
606 
607 
608 int GWEN_PasswordStore_SetPassword(GWEN_PASSWD_STORE *sto, const char *token, const char *secret) {
609  GWEN_FSLOCK *lck;
611  int rv;
612 
613  /* make sure path exists */
615  if (rv<0) {
616  DBG_INFO(GWEN_LOGDOMAIN, "here (%d)", rv);
617  return rv;
618  }
619 
620  /* lock file */
621  lck=GWEN_FSLock_new(sto->fileName, GWEN_FSLock_TypeFile);
622  rs=GWEN_FSLock_Lock(lck, 60*1000, 0);
623  if (rs!=GWEN_FSLock_ResultOk) {
624  DBG_INFO(GWEN_LOGDOMAIN, "here (%d)", rs);
625  return GWEN_ERROR_IO;
626  }
627 
628  /* read and decrypt file */
630  if (rv<0) {
631  if (rv==GWEN_ERROR_NOT_FOUND || rv==GWEN_ERROR_NO_DATA) {
632  DBG_INFO(GWEN_LOGDOMAIN, "Will create password store [%s]", sto->fileName);
633  if (sto->dbPasswords==NULL) {
634  sto->dbPasswords=GWEN_DB_Group_new("passwords");
636  }
637  sto->isNew=1;
638  }
639  else {
640  DBG_INFO(GWEN_LOGDOMAIN, "here (%d)", rv);
641  GWEN_FSLock_Unlock(lck);
642  GWEN_FSLock_free(lck);
643  return rv;
644  }
645  }
646 
647  /* set password in db */
648  rv=GWEN_PasswordStore__SetPassword(sto, token, secret);
649  if (rv<0) {
650  DBG_INFO(GWEN_LOGDOMAIN, "here (%d)", rv);
651  GWEN_FSLock_Unlock(lck);
652  GWEN_FSLock_free(lck);
653  return rv;
654  }
655 
656  /* write file back */
658  if (rv<0) {
659  DBG_INFO(GWEN_LOGDOMAIN, "here (%d)", rv);
660  GWEN_FSLock_Unlock(lck);
661  GWEN_FSLock_free(lck);
662  return rv;
663  }
664 
665  /* unlock file */
666  GWEN_FSLock_Unlock(lck);
667  GWEN_FSLock_free(lck);
668 
669  /* release passwords */
671 
672  return 0;
673 }
674 
675 
676 
677 
678 int GWEN_PasswordStore_GetPassword(GWEN_PASSWD_STORE *sto, const char *token, char *buffer, int minLen, int maxLen) {
679  int rv;
680  GWEN_FSLOCK *lck;
682 
683  /* make sure path exists */
685  if (rv<0) {
686  DBG_INFO(GWEN_LOGDOMAIN, "here (%d)", rv);
687  return rv;
688  }
689 
690  /* lock file */
691  lck=GWEN_FSLock_new(sto->fileName, GWEN_FSLock_TypeFile);
692  rs=GWEN_FSLock_Lock(lck, 60*1000, 0);
693  if (rs!=GWEN_FSLock_ResultOk) {
694  DBG_INFO(GWEN_LOGDOMAIN, "here (%d)", rs);
695  return GWEN_ERROR_IO;
696  }
697 
698  /* read and decode file */
700  if (rv<0) {
701  DBG_INFO(GWEN_LOGDOMAIN, "here (%d)", rv);
702  GWEN_FSLock_Unlock(lck);
703  GWEN_FSLock_free(lck);
704  return rv;
705  }
706 
707  /* unlock file */
708  GWEN_FSLock_Unlock(lck);
709  GWEN_FSLock_free(lck);
710 
711  /* finally get password, if possible */
712  rv=GWEN_PasswordStore__GetPassword(sto, token, buffer, minLen, maxLen);
713  if (rv<0) {
714  DBG_INFO(GWEN_LOGDOMAIN, "here (%d)", rv);
715  return rv;
716  }
717 
718  /* release passwords */
720 
721  return 0;
722 }
723 
724 
725 
726