2005-10-23 Moritz Schulte <moritz@g10code.com>
[poldi.git] / src / common / support.c
1 /* support.c - PAM authentication via OpenPGP smartcards.
2    Copyright (C) 2004, 2005 g10 Code GmbH
3  
4    This file is part of Poldi.
5   
6    Poldi is free software; you can redistribute it and/or modify it
7    under the terms of the GNU General Public License as published by
8    the Free Software Foundation; either version 2 of the License, or
9    (at your option) any later version.
10   
11    Poldi is distributed in the hope that it will be useful, but
12    WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14    General Public License for more details.
15   
16    You should have received a copy of the GNU General Public License
17    along with this program; if not, write to the Free Software
18    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
19    02111-1307, USA.  */
20
21 #include <config.h>
22
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <assert.h>
26 #include <sys/mman.h>
27 #include <unistd.h>
28 #include <fcntl.h>
29 #include <sys/stat.h>
30 #include <errno.h>
31 #include <stdarg.h>
32 #include <pwd.h>
33
34 #include <gcrypt.h>
35
36 #include "support.h"
37 #include "defs.h"
38
39 #include <jnlib/xmalloc.h>
40 #include <jnlib/stringhelp.h>
41
42 #define CHALLENGE_MD_ALGORITHM GCRY_MD_SHA1
43
44 gpg_error_t
45 challenge_generate (unsigned char **challenge, size_t *challenge_n)
46 {
47   gpg_error_t err = GPG_ERR_NO_ERROR;
48   unsigned char *challenge_new = NULL;
49   size_t challenge_new_n = gcry_md_get_algo_dlen (CHALLENGE_MD_ALGORITHM);
50
51   challenge_new = malloc (challenge_new_n);
52   if (! challenge_new)
53     err = gpg_err_code_from_errno (errno);
54   else
55     {
56       gcry_create_nonce (challenge_new, challenge_new_n);
57       *challenge = challenge_new;
58       *challenge_n = challenge_new_n;
59     }
60
61   return err;
62 }
63
64 static gpg_error_t
65 challenge_verify_sexp (gcry_sexp_t sexp_key,
66                        unsigned char *challenge, size_t challenge_n,
67                        unsigned char *response, size_t response_n)
68 {
69   gpg_error_t err = GPG_ERR_NO_ERROR;
70   gcry_sexp_t sexp_signature = NULL;
71   gcry_sexp_t sexp_data = NULL;
72   gcry_mpi_t mpi_signature = NULL;
73
74   /* Convert buffers into MPIs.  */
75   if (! err)
76     {
77       if (gcry_mpi_scan (&mpi_signature, GCRYMPI_FMT_USG, response, response_n,
78                          NULL))
79         err = GPG_ERR_INTERNAL; /* FIXME.  */
80     }
81
82   /* Create according S-Expressions.  */
83   if (! err)
84     err = gcry_sexp_build (&sexp_data, NULL,
85                            "(data (flags pkcs1) (hash sha1 %b))",
86                            challenge_n, challenge);
87   if (! err)
88     err = gcry_sexp_build (&sexp_signature, NULL, "(sig-val (rsa (s %m)))",
89                            mpi_signature);
90
91   /* Verify.  */
92   if (! err)
93     err = gcry_pk_verify (sexp_signature, sexp_data, sexp_key);
94
95   if (sexp_data)
96     gcry_sexp_release (sexp_data);
97   if (sexp_signature)
98     gcry_sexp_release (sexp_signature);
99   if (mpi_signature)
100     gcry_mpi_release (mpi_signature);
101
102   return err;
103 }
104
105 gpg_error_t
106 challenge_verify (gcry_sexp_t key,
107                   unsigned char *challenge, size_t challenge_n,
108                   unsigned char *response, size_t response_n)
109 {
110   gpg_error_t err = GPG_ERR_NO_ERROR;
111
112   err = challenge_verify_sexp (key,
113                                challenge, challenge_n, response, response_n);
114
115   return err;
116 }
117
118 static gpg_error_t
119 usersdb_translate (const char *serialno, const char *username, char **found)
120 {
121   const char *delimiters = "\t\n ";
122   gpg_error_t err;
123   FILE *usersdb;
124   char *line;
125   char *line_serialno;
126   char *line_username;
127   char *token_found;
128   size_t line_n;
129   ssize_t ret;
130
131   err = 0;
132   line = NULL;
133   token_found = NULL;
134   line_serialno = NULL;
135   line_username = NULL;
136
137   usersdb = fopen (POLDI_USERS_DB_FILE, "r");
138   if (! usersdb)
139     {
140       err = gpg_error_from_errno (errno);
141       goto out;
142     }
143
144   while (1)
145     {
146       /* Get next line.  */
147       line = NULL;
148       line_n = 0;
149       ret = getline (&line, &line_n, usersdb);
150       if (ret == -1)
151         {
152           if (ferror (usersdb))
153             err = gpg_error_from_errno (errno);
154           else
155             err = gpg_error (GPG_ERR_NOT_FOUND);
156           break;
157         }
158
159       line_serialno = strtok (line, delimiters);
160       if (! line_serialno)
161         {
162           err = gpg_error (GPG_ERR_INTERNAL); /* FIXME?  */
163           break;
164         }
165
166       line_username = strtok (NULL, delimiters);
167       if (! line_username)
168         {
169           err = gpg_error (GPG_ERR_INTERNAL); /* FIXME?  */
170           break;
171         }
172
173       if (serialno)
174         {
175           if (! strcmp (serialno, line_serialno))
176             {
177               if (found)
178                 {
179                   token_found = strdup (line_username);
180                   if (! token_found)
181                     err = gpg_error_from_errno (errno);
182                 }
183               break;
184             }
185         }
186       else
187         {
188           if (! strcmp (username, line_username))
189             {
190               if (found)
191                 {
192                   token_found = strdup (line_serialno);
193                   if (! token_found)
194                     err = gpg_error_from_errno (errno);
195                 }
196               break;
197             }
198         }
199
200       free (line);
201     }
202   if (err)
203     goto out;
204
205   if (found)
206     *found = token_found;
207
208  out:
209
210   if (usersdb)
211     fclose (usersdb);
212   free (line);
213
214   return err;
215 }
216
217 gpg_error_t
218 usersdb_lookup_by_serialno (const char *serialno, char **username)
219 {
220   return usersdb_translate (serialno, NULL, username);
221 }
222
223 gpg_error_t
224 usersdb_lookup_by_username (const char *username, char **serialno)
225 {
226   return usersdb_translate (NULL, username, serialno);
227 }
228
229 gpg_error_t
230 usersdb_add_entry (const char *username, const char *serialno)
231 {
232   char users_file[] = POLDI_USERS_DB_FILE;
233   FILE *users_file_fp;
234   gpg_error_t err;
235   int ret;
236
237   users_file_fp = NULL;
238   
239   users_file_fp = fopen (users_file, "a");
240   if (! users_file_fp)
241     {
242       err = gpg_error_from_errno (errno);
243       goto out;
244     }
245
246   fprintf (users_file_fp, "%s\t%s\n", serialno, username);
247   if (ferror (users_file_fp))
248     {
249       err = gpg_error_from_errno (errno);
250       goto out;
251     }
252   
253   ret = fclose (users_file_fp);
254   users_file_fp = NULL;
255   if (ret)
256     {
257       err = gpg_error_from_errno (errno);
258       goto out;
259     }
260   
261   err = 0;
262
263  out:
264
265   if (users_file_fp)
266     fclose (users_file_fp);
267
268   return err;
269 }
270
271 gpg_error_t
272 usersdb_remove_entry (const char *username, const char *serialno)
273 {
274   char users_file_old[] = POLDI_USERS_DB_FILE;
275   char users_file_new[] = POLDI_USERS_DB_FILE ".new";
276   char delimiters[] = "\t\n ";
277   FILE *users_file_old_fp;
278   FILE *users_file_new_fp;
279   char *line;
280   char *line_serialno;
281   char *line_username;
282   size_t line_n;
283   ssize_t ret;
284   gpg_error_t err;
285
286   line_n = 0;
287   line = NULL;
288   users_file_old_fp = NULL;
289   users_file_new_fp = NULL;
290
291   users_file_old_fp = fopen (users_file_old, "r");
292   if (! users_file_old_fp)
293     {
294       err = gpg_error_from_errno (errno);
295       goto out;
296     }
297   users_file_new_fp = fopen (users_file_new, "w");
298   if (! users_file_new_fp)
299     {
300       err = gpg_error_from_errno (errno);
301       goto out;
302     }
303
304   err = 0;
305   while (1)
306     {
307       ret = getline (&line, &line_n, users_file_old_fp);
308       if (ret == -1)
309         {
310           if (ferror (users_file_old_fp))
311             err = gpg_error_from_errno (errno);
312           break;
313         }
314
315       line_serialno = strtok (line, delimiters);
316       if (! line_serialno)
317         {
318           err = gpg_error (GPG_ERR_INTERNAL); /* FIXME?  */
319           break;
320         }
321
322       line_username = strtok (NULL, delimiters);
323       if (! line_username)
324         {
325           err = gpg_error (GPG_ERR_INTERNAL); /* FIXME?  */
326           break;
327         }
328
329       if ((username && strcmp (username, line_username))
330           || (serialno && strcmp (serialno, line_serialno)))
331         fprintf (users_file_new_fp, "%s\t%s\n", line_serialno, line_username);
332
333       free (line);
334       line = NULL;
335       line_n = 0;
336     }
337
338   fclose (users_file_old_fp);   /* FIXME: it's alright to ignore
339                                    errors here, right?  */
340   users_file_old_fp = NULL;
341   
342   ret = fclose (users_file_new_fp);
343   users_file_new_fp = NULL;
344   if (ret)
345     {
346       err = gpg_error_from_errno (errno);
347       goto out;
348     }
349
350   ret = rename (users_file_new, users_file_old);
351   if (ret == -1)
352     err = gpg_error_from_errno (errno);
353
354  out:
355
356   free (line);
357   if (users_file_old_fp)
358     fclose (users_file_old_fp);
359   if (users_file_new_fp)
360     fclose (users_file_new_fp);
361
362   return err;
363 }
364
365 gpg_error_t
366 sexp_to_string (gcry_sexp_t sexp, char **sexp_string)
367 {
368   gpg_error_t err;
369   char *buffer;
370   size_t buffer_n;
371   int fmt;
372
373   buffer = NULL;
374   fmt = GCRYSEXP_FMT_ADVANCED;
375
376   buffer_n = gcry_sexp_sprint (sexp, fmt, NULL, 0);
377   if (! buffer_n)
378     {
379       err = gpg_error (GPG_ERR_INTERNAL); /* ? */
380       goto out;
381     }
382
383   buffer = malloc (buffer_n);
384   if (! buffer)
385     {
386       err = gpg_error_from_errno (errno);
387       goto out;
388     }
389
390   buffer_n = gcry_sexp_sprint (sexp, fmt, buffer, buffer_n);
391   if (! buffer_n)
392     {
393       err = gpg_error (GPG_ERR_INTERNAL);
394       goto out;
395     }
396
397   *sexp_string = buffer;
398   err = 0;
399   
400  out:
401
402   if (err)
403     free (buffer);
404
405   return err;
406 }
407
408 gpg_error_t
409 string_to_sexp (gcry_sexp_t *sexp, char *string)
410 {
411   gpg_error_t err;
412
413   err = gcry_sexp_sscan (sexp, NULL, string, strlen (string));
414
415   return err;
416 }
417
418 gpg_error_t
419 file_to_string (const char *filename, char **string)
420 {
421   gpg_error_t err;
422   struct stat statbuf;
423   char *string_new;
424   FILE *fp;
425   int ret;
426
427   fp = NULL;
428   string_new = NULL;
429
430   ret = stat (filename, &statbuf);
431   if (ret)
432     {
433       err = gpg_error_from_errno (errno);
434       goto out;
435     }
436
437   if (statbuf.st_size)
438     {
439       fp = fopen (filename, "r");
440       if (! fp)
441         {
442           err = gpg_error_from_errno (errno);
443           goto out;
444         }
445       string_new = malloc (statbuf.st_size + 1);
446       if (! string_new)
447         {
448           err = gpg_error_from_errno (errno);
449           goto out;
450         }
451       ret = fread (string_new, statbuf.st_size, 1, fp);
452       if (ret != 1)
453         {
454           err = gpg_error_from_errno (errno);
455           goto out;
456         }
457       string_new[statbuf.st_size] = 0;
458       fclose (fp);              /* FIXME?  */
459       fp = NULL;
460     }
461
462   err = 0;
463   *string = string_new;
464
465  out:
466
467   if (fp)
468     fclose (fp);
469
470   if (err)
471     free (string_new);
472
473   return err;
474 }
475
476 \f
477
478 gpg_error_t
479 key_filename_construct (char **filename, const char *serialno)
480 {
481   char *path;
482
483   path = make_filename (POLDI_KEY_DIRECTORY, serialno, NULL);
484   *filename = path;
485
486   return 0;
487 }
488
489 gpg_error_t
490 lookup_own_username (const char **username)
491 {
492   struct passwd *pwent;
493   gpg_error_t err;
494   uid_t uid;
495
496   uid = getuid ();
497   pwent = getpwuid (uid);
498   if (! pwent)
499     err = gpg_error_from_errno (errno);
500   else
501     {
502       *username = pwent->pw_name;
503       err = 0;
504     }
505
506   return err;
507 }
508
509 /* END */