Use the gpgrt log functions if possible.
[gnupg.git] / g13 / call-syshelp.c
1 /* call-syshelp.c - Communication with g13-syshelp
2  * Copyright (C) 2015 Werner Koch
3  *
4  * This file is part of GnuPG.
5  *
6  * GnuPG is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * GnuPG is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU 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, see <https://www.gnu.org/licenses/>.
18  */
19
20 #include <config.h>
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <errno.h>
25 #include <time.h>
26 #include <assert.h>
27 #include <npth.h>
28
29 #include "g13.h"
30 #include <assuan.h>
31 #include "../common/i18n.h"
32 #include "g13tuple.h"
33 #include "keyblob.h"
34 #include "../common/membuf.h"
35 #include "create.h"
36 #include "call-syshelp.h"
37
38
39 /* Local data for this module.  A pointer to this is stored in the
40    CTRL object of each connection.  */
41 struct call_syshelp_s
42 {
43   assuan_context_t assctx;  /* The Assuan context for the current
44                                g13-syshep connection.  */
45 };
46
47
48 /* Parameter used with the CREATE command.  */
49 struct create_parm_s
50 {
51   assuan_context_t ctx;
52   ctrl_t ctrl;
53   membuf_t plaintext;
54   unsigned int expect_plaintext:1;
55   unsigned int got_plaintext:1;
56 };
57
58
59 /* Parameter used with the MOUNT command.  */
60 struct mount_parm_s
61 {
62   assuan_context_t ctx;
63   ctrl_t ctrl;
64   const void *keyblob;
65   size_t keybloblen;
66 };
67
68
69
70
71 \f
72 /* Fork off the syshelp tool if this has not already been done.  On
73    success stores the current Assuan context for the syshelp tool at
74    R_CTX.  */
75 static gpg_error_t
76 start_syshelp (ctrl_t ctrl, assuan_context_t *r_ctx)
77 {
78   gpg_error_t err;
79   assuan_context_t ctx;
80   assuan_fd_t no_close_list[3];
81   int i;
82
83   *r_ctx = NULL;
84
85   if (ctrl->syshelp_local && (*r_ctx = ctrl->syshelp_local->assctx))
86     return 0; /* Already set.  */
87
88   if (opt.verbose)
89     log_info ("starting a new syshelp\n");
90
91   if (!ctrl->syshelp_local)
92     {
93       ctrl->syshelp_local = xtrycalloc (1, sizeof *ctrl->syshelp_local);
94       if (!ctrl->syshelp_local)
95         return gpg_error_from_syserror ();
96     }
97
98   if (es_fflush (NULL))
99     {
100       err = gpg_error_from_syserror ();
101       log_error ("error flushing pending output: %s\n", gpg_strerror (err));
102       return err;
103     }
104
105   i = 0;
106   if (log_get_fd () != -1)
107     no_close_list[i++] = assuan_fd_from_posix_fd (log_get_fd ());
108   no_close_list[i++] = assuan_fd_from_posix_fd (es_fileno (es_stderr));
109   no_close_list[i] = ASSUAN_INVALID_FD;
110
111   err = assuan_new (&ctx);
112   if (err)
113     {
114       log_error ("can't allocate assuan context: %s\n", gpg_strerror (err));
115       return err;
116     }
117
118   /* Call userv to start g13-syshelp.  This userv script needs to be
119    * installed under the name "gnupg-g13-syshelp":
120    *
121    *   if ( glob service-user root
122    *      )
123    *       reset
124    *       suppress-args
125    *       execute /home/wk/b/gnupg/g13/g13-syshelp -v
126    *   else
127    *       error Nothing to do for this service-user
128    *   fi
129    *   quit
130    */
131   {
132     const char *argv[4];
133
134     argv[0] = "userv";
135     argv[1] = "root";
136     argv[2] = "gnupg-g13-syshelp";
137     argv[3] = NULL;
138
139     err = assuan_pipe_connect (ctx, "/usr/bin/userv", argv,
140                                no_close_list, NULL, NULL, 0);
141   }
142   if (err)
143     {
144       log_error ("can't connect to '%s': %s %s\n",
145                  "g13-syshelp", gpg_strerror (err), gpg_strsource (err));
146       log_info ("(is userv and its gnupg-g13-syshelp script installed?)\n");
147       assuan_release (ctx);
148       return err;
149     }
150
151   *r_ctx = ctrl->syshelp_local->assctx = ctx;
152
153   if (DBG_IPC)
154     log_debug ("connection to g13-syshelp established\n");
155
156   return 0;
157 }
158
159
160 /* Release local resources associated with CTRL.  */
161 void
162 call_syshelp_release (ctrl_t ctrl)
163 {
164   if (!ctrl)
165     return;
166   if (ctrl->syshelp_local)
167     {
168       assuan_release (ctrl->syshelp_local->assctx);
169       ctrl->syshelp_local->assctx = NULL;
170       xfree (ctrl->syshelp_local);
171       ctrl->syshelp_local = NULL;
172     }
173 }
174
175
176 \f
177 /* Staus callback for call_syshelp_find_device.  */
178 static gpg_error_t
179 finddevice_status_cb (void *opaque, const char *line)
180 {
181   char **r_blockdev = opaque;
182   char *p;
183
184   if ((p = has_leading_keyword (line, "BLOCKDEV")) && *p && !*r_blockdev)
185     {
186       *r_blockdev = xtrystrdup (p);
187       if (!*r_blockdev)
188         return gpg_error_from_syserror ();
189     }
190
191   return 0;
192 }
193
194
195 /* Send the FINDDEVICE command to the syshelper.  On success the name
196  * of the block device is stored at R_BLOCKDEV. */
197 gpg_error_t
198 call_syshelp_find_device (ctrl_t ctrl, const char *name, char **r_blockdev)
199 {
200   gpg_error_t err;
201   assuan_context_t ctx;
202   char *line = NULL;
203   char *blockdev = NULL;  /* The result.  */
204
205   *r_blockdev = NULL;
206
207   err = start_syshelp (ctrl, &ctx);
208   if (err)
209     goto leave;
210
211   line = xtryasprintf ("FINDDEVICE %s", name);
212   if (!line)
213     {
214       err = gpg_error_from_syserror ();
215       goto leave;
216     }
217   err = assuan_transact (ctx, line, NULL, NULL, NULL, NULL,
218                          finddevice_status_cb, &blockdev);
219   if (err)
220     goto leave;
221   if (!blockdev)
222     {
223       log_error ("status line for successful FINDDEVICE missing\n");
224       err = gpg_error (GPG_ERR_UNEXPECTED);
225       goto leave;
226     }
227   *r_blockdev = blockdev;
228   blockdev = NULL;
229
230  leave:
231   xfree (blockdev);
232   xfree (line);
233   return err;
234 }
235
236
237 \f
238 static gpg_error_t
239 getkeyblob_data_cb (void *opaque, const void *data, size_t datalen)
240 {
241   membuf_t *mb = opaque;
242
243   if (data)
244     put_membuf (mb, data, datalen);
245
246   return 0;
247 }
248
249
250 /* Send the GTEKEYBLOB command to the syshelper.  On success the
251  * encrypted keyblpob is stored at (R_ENCKEYBLOB,R_ENCKEYBLOBLEN).  */
252 gpg_error_t
253 call_syshelp_get_keyblob (ctrl_t ctrl,
254                           void **r_enckeyblob, size_t *r_enckeybloblen)
255 {
256   gpg_error_t err;
257   assuan_context_t ctx;
258   membuf_t mb;
259
260   *r_enckeyblob = NULL;
261   *r_enckeybloblen = 0;
262   init_membuf (&mb, 512);
263
264   err = start_syshelp (ctrl, &ctx);
265   if (err)
266     goto leave;
267
268   err = assuan_transact (ctx, "GETKEYBLOB",
269                          getkeyblob_data_cb, &mb,
270                          NULL, NULL, NULL, NULL);
271   if (err)
272     goto leave;
273   *r_enckeyblob = get_membuf (&mb, r_enckeybloblen);
274   if (!*r_enckeyblob)
275     err = gpg_error_from_syserror ();
276
277  leave:
278   xfree (get_membuf (&mb, NULL));
279   return err;
280 }
281
282
283 \f
284 /* Send the DEVICE command to the syshelper.  FNAME is the name of the
285    device.  */
286 gpg_error_t
287 call_syshelp_set_device (ctrl_t ctrl, const char *fname)
288 {
289   gpg_error_t err;
290   assuan_context_t ctx;
291   char *line = NULL;
292
293   err = start_syshelp (ctrl, &ctx);
294   if (err)
295     goto leave;
296
297   line = xtryasprintf ("DEVICE %s", fname);
298   if (!line)
299     {
300       err = gpg_error_from_syserror ();
301       goto leave;
302     }
303   err = assuan_transact (ctx, line, NULL, NULL, NULL, NULL, NULL, NULL);
304
305  leave:
306   xfree (line);
307   return err;
308 }
309
310
311 \f
312 static gpg_error_t
313 create_status_cb (void *opaque, const char *line)
314 {
315   struct create_parm_s *parm = opaque;
316
317   if (has_leading_keyword (line, "PLAINTEXT_FOLLOWS"))
318     parm->expect_plaintext = 1;
319
320   return 0;
321 }
322
323
324 static gpg_error_t
325 create_data_cb (void *opaque, const void *data, size_t datalen)
326 {
327   struct create_parm_s *parm = opaque;
328   gpg_error_t err = 0;
329
330   if (!parm->expect_plaintext)
331     {
332       log_error ("status line for data missing\n");
333       err = gpg_error (GPG_ERR_UNEXPECTED);
334     }
335   else if (data)
336     {
337       put_membuf (&parm->plaintext, data, datalen);
338     }
339   else
340     {
341       parm->expect_plaintext = 0;
342       parm->got_plaintext = 1;
343     }
344
345   return err;
346 }
347
348
349 static gpg_error_t
350 create_inq_cb (void *opaque, const char *line)
351 {
352   struct create_parm_s *parm = opaque;
353   gpg_error_t err;
354
355   if (has_leading_keyword (line, "ENCKEYBLOB"))
356     {
357       void *plaintext;
358       size_t plaintextlen;
359
360       if (!parm->got_plaintext)
361         err = gpg_error (GPG_ERR_UNEXPECTED);
362       else if (!(plaintext = get_membuf (&parm->plaintext, &plaintextlen)))
363         err = gpg_error_from_syserror ();
364       else
365         {
366           void *ciphertext;
367           size_t ciphertextlen;
368
369           log_printhex (plaintext, plaintextlen, "plain");
370           err = g13_encrypt_keyblob (parm->ctrl,
371                                      plaintext, plaintextlen,
372                                      &ciphertext, &ciphertextlen);
373           wipememory (plaintext, plaintextlen);
374           xfree (plaintext);
375           if (err)
376             log_error ("error encrypting keyblob: %s\n", gpg_strerror (err));
377           else
378             {
379               err = assuan_send_data (parm->ctx, ciphertext, ciphertextlen);
380               xfree (ciphertext);
381               if (err)
382                 log_error ("sending ciphertext to g13-syshelp failed: %s\n",
383                            gpg_strerror (err));
384             }
385         }
386     }
387   else
388     err = gpg_error (GPG_ERR_ASS_UNKNOWN_INQUIRE);
389
390   return err;
391 }
392
393
394 /* Run the CREATE command on the current device.  CONTTYPES gives the
395    requested content type for the new container.  */
396 gpg_error_t
397 call_syshelp_run_create (ctrl_t ctrl, int conttype)
398 {
399   gpg_error_t err;
400   assuan_context_t ctx;
401   struct create_parm_s parm;
402
403   memset (&parm, 0, sizeof parm);
404
405   err = start_syshelp (ctrl, &ctx);
406   if (err)
407     goto leave;
408
409   /* tty_get ("waiting for debugger"); */
410   /* tty_kill_prompt (); */
411
412   parm.ctx = ctx;
413   parm.ctrl = ctrl;
414   init_membuf (&parm.plaintext, 512);
415   if (conttype == CONTTYPE_DM_CRYPT)
416     {
417       err = assuan_transact (ctx, "CREATE dm-crypt",
418                              create_data_cb, &parm,
419                              create_inq_cb, &parm,
420                              create_status_cb, &parm);
421     }
422   else
423     {
424       log_error ("invalid backend type %d given\n", conttype);
425       err = GPG_ERR_INTERNAL;
426       goto leave;
427     }
428
429  leave:
430   xfree (get_membuf (&parm.plaintext, NULL));
431   return err;
432 }
433
434
435 \f
436 static gpg_error_t
437 mount_status_cb (void *opaque, const char *line)
438 {
439   struct mount_parm_s *parm = opaque;
440
441   /* Nothing right now.  */
442   (void)parm;
443   (void)line;
444
445   return 0;
446 }
447
448
449 /* Inquire callback for MOUNT and RESUME.  */
450 static gpg_error_t
451 mount_inq_cb (void *opaque, const char *line)
452 {
453   struct mount_parm_s *parm = opaque;
454   gpg_error_t err;
455
456   if (has_leading_keyword (line, "KEYBLOB"))
457     {
458       int setconfidential = !assuan_get_flag (parm->ctx, ASSUAN_CONFIDENTIAL);
459
460       if (setconfidential)
461         assuan_begin_confidential (parm->ctx);
462       err = assuan_send_data (parm->ctx, parm->keyblob, parm->keybloblen);
463       if (setconfidential)
464         assuan_end_confidential (parm->ctx);
465       if (err)
466         log_error ("sending keyblob to g13-syshelp failed: %s\n",
467                    gpg_strerror (err));
468     }
469   else
470     err = gpg_error (GPG_ERR_ASS_UNKNOWN_INQUIRE);
471
472   return err;
473 }
474
475
476 /*
477  * Run the MOUNT command on the current device.  CONTTYPES gives the
478  * requested content type for the new container.  MOUNTPOINT the
479  * desired mount point or NULL for default.
480  */
481 gpg_error_t
482 call_syshelp_run_mount (ctrl_t ctrl, int conttype, const char *mountpoint,
483                         tupledesc_t tuples)
484 {
485   gpg_error_t err;
486   assuan_context_t ctx;
487   struct mount_parm_s parm;
488
489   memset (&parm, 0, sizeof parm);
490
491   err = start_syshelp (ctrl, &ctx);
492   if (err)
493     goto leave;
494
495   /* tty_get ("waiting for debugger"); */
496   /* tty_kill_prompt (); */
497
498   parm.ctx = ctx;
499   parm.ctrl = ctrl;
500   if (conttype == CONTTYPE_DM_CRYPT)
501     {
502       ref_tupledesc (tuples);
503       parm.keyblob = get_tupledesc_data (tuples, &parm.keybloblen);
504       err = assuan_transact (ctx, "MOUNT dm-crypt",
505                              NULL, NULL,
506                              mount_inq_cb, &parm,
507                              mount_status_cb, &parm);
508       unref_tupledesc (tuples);
509     }
510   else
511     {
512       (void)mountpoint; /* Not used.  */
513       log_error ("invalid backend type %d given\n", conttype);
514       err = GPG_ERR_INTERNAL;
515       goto leave;
516     }
517
518  leave:
519   return err;
520 }
521
522
523 \f
524 /*
525  * Run the UMOUNT command on the current device.  CONTTYPES gives the
526  * content type of the container (fixme: Do we really need this?).
527  */
528 gpg_error_t
529 call_syshelp_run_umount (ctrl_t ctrl, int conttype)
530 {
531   gpg_error_t err;
532   assuan_context_t ctx;
533
534   err = start_syshelp (ctrl, &ctx);
535   if (err)
536     goto leave;
537
538   if (conttype == CONTTYPE_DM_CRYPT)
539     {
540       err = assuan_transact (ctx, "UMOUNT dm-crypt",
541                              NULL, NULL,
542                              NULL, NULL,
543                              NULL, NULL);
544     }
545   else
546     {
547       log_error ("invalid backend type %d given\n", conttype);
548       err = GPG_ERR_INTERNAL;
549       goto leave;
550     }
551
552  leave:
553   return err;
554 }
555
556
557 \f
558 /*
559  * Run the SUSPEND command on the current device.  CONTTYPES gives the
560  * requested content type for the new container.
561  */
562 gpg_error_t
563 call_syshelp_run_suspend (ctrl_t ctrl, int conttype)
564 {
565   gpg_error_t err;
566   assuan_context_t ctx;
567
568   err = start_syshelp (ctrl, &ctx);
569   if (err)
570     goto leave;
571
572   if (conttype == CONTTYPE_DM_CRYPT)
573     {
574       err = assuan_transact (ctx, "SUSPEND dm-crypt",
575                              NULL, NULL,
576                              NULL, NULL,
577                              NULL, NULL);
578     }
579   else
580     {
581       log_error ("invalid backend type %d given\n", conttype);
582       err = GPG_ERR_INTERNAL;
583       goto leave;
584     }
585
586  leave:
587   return err;
588 }
589
590
591 \f
592 /* Run the RESUME command on the current device.  CONTTYPES gives the
593    requested content type for the container.  */
594 gpg_error_t
595 call_syshelp_run_resume (ctrl_t ctrl, int conttype, tupledesc_t tuples)
596 {
597   gpg_error_t err;
598   assuan_context_t ctx;
599   struct mount_parm_s parm;
600
601   memset (&parm, 0, sizeof parm);
602
603   err = start_syshelp (ctrl, &ctx);
604   if (err)
605     goto leave;
606
607   /* tty_get ("waiting for debugger"); */
608   /* tty_kill_prompt (); */
609
610   parm.ctx = ctx;
611   parm.ctrl = ctrl;
612   if (conttype == CONTTYPE_DM_CRYPT)
613     {
614       ref_tupledesc (tuples);
615       parm.keyblob = get_tupledesc_data (tuples, &parm.keybloblen);
616       err = assuan_transact (ctx, "RESUME dm-crypt",
617                              NULL, NULL,
618                              mount_inq_cb, &parm,
619                              NULL, NULL);
620       unref_tupledesc (tuples);
621     }
622   else
623     {
624       log_error ("invalid backend type %d given\n", conttype);
625       err = GPG_ERR_INTERNAL;
626       goto leave;
627     }
628
629  leave:
630   return err;
631 }