g13: New command --find-device.
[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 <http://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 "i18n.h"
32 #include "g13tuple.h"
33 #include "keyblob.h"
34 #include "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 /* Send the DEVICE command to the syshelper.  FNAME is the name of the
239    device.  */
240 gpg_error_t
241 call_syshelp_set_device (ctrl_t ctrl, const char *fname)
242 {
243   gpg_error_t err;
244   assuan_context_t ctx;
245   char *line = NULL;
246
247   err = start_syshelp (ctrl, &ctx);
248   if (err)
249     goto leave;
250
251   line = xtryasprintf ("DEVICE %s", fname);
252   if (!line)
253     {
254       err = gpg_error_from_syserror ();
255       goto leave;
256     }
257   err = assuan_transact (ctx, line, NULL, NULL, NULL, NULL, NULL, NULL);
258
259  leave:
260   xfree (line);
261   return err;
262 }
263
264
265 \f
266 static gpg_error_t
267 create_status_cb (void *opaque, const char *line)
268 {
269   struct create_parm_s *parm = opaque;
270
271   if (has_leading_keyword (line, "PLAINTEXT_FOLLOWS"))
272     parm->expect_plaintext = 1;
273
274   return 0;
275 }
276
277
278 static gpg_error_t
279 create_data_cb (void *opaque, const void *data, size_t datalen)
280 {
281   struct create_parm_s *parm = opaque;
282   gpg_error_t err = 0;
283
284   if (!parm->expect_plaintext)
285     {
286       log_error ("status line for data missing\n");
287       err = gpg_error (GPG_ERR_UNEXPECTED);
288     }
289   else if (data)
290     {
291       put_membuf (&parm->plaintext, data, datalen);
292     }
293   else
294     {
295       parm->expect_plaintext = 0;
296       parm->got_plaintext = 1;
297     }
298
299   return err;
300 }
301
302
303 static gpg_error_t
304 create_inq_cb (void *opaque, const char *line)
305 {
306   struct create_parm_s *parm = opaque;
307   gpg_error_t err;
308
309   if (has_leading_keyword (line, "ENCKEYBLOB"))
310     {
311       void *plaintext;
312       size_t plaintextlen;
313
314       if (!parm->got_plaintext)
315         err = gpg_error (GPG_ERR_UNEXPECTED);
316       else if (!(plaintext = get_membuf (&parm->plaintext, &plaintextlen)))
317         err = gpg_error_from_syserror ();
318       else
319         {
320           void *ciphertext;
321           size_t ciphertextlen;
322
323           log_printhex ("plain", plaintext, plaintextlen);
324           err = g13_encrypt_keyblob (parm->ctrl,
325                                      plaintext, plaintextlen,
326                                      &ciphertext, &ciphertextlen);
327           wipememory (plaintext, plaintextlen);
328           xfree (plaintext);
329           if (err)
330             log_error ("error encrypting keyblob: %s\n", gpg_strerror (err));
331           else
332             {
333               err = assuan_send_data (parm->ctx, ciphertext, ciphertextlen);
334               xfree (ciphertext);
335               if (err)
336                 log_error ("sending ciphertext to g13-syshelp failed: %s\n",
337                            gpg_strerror (err));
338             }
339         }
340     }
341   else
342     err = gpg_error (GPG_ERR_ASS_UNKNOWN_INQUIRE);
343
344   return err;
345 }
346
347
348 /* Run the CREATE command on the current device.  CONTTYPES gives the
349    requested content type for the new container.  */
350 gpg_error_t
351 call_syshelp_run_create (ctrl_t ctrl, int conttype)
352 {
353   gpg_error_t err;
354   assuan_context_t ctx;
355   struct create_parm_s parm;
356
357   memset (&parm, 0, sizeof parm);
358
359   err = start_syshelp (ctrl, &ctx);
360   if (err)
361     goto leave;
362
363   /* tty_get ("waiting for debugger"); */
364   /* tty_kill_prompt (); */
365
366   parm.ctx = ctx;
367   parm.ctrl = ctrl;
368   init_membuf (&parm.plaintext, 512);
369   if (conttype == CONTTYPE_DM_CRYPT)
370     {
371       err = assuan_transact (ctx, "CREATE dm-crypt",
372                              create_data_cb, &parm,
373                              create_inq_cb, &parm,
374                              create_status_cb, &parm);
375     }
376   else
377     {
378       log_error ("invalid backend type %d given\n", conttype);
379       err = GPG_ERR_INTERNAL;
380       goto leave;
381     }
382
383  leave:
384   xfree (get_membuf (&parm.plaintext, NULL));
385   return err;
386 }
387
388
389 \f
390 static gpg_error_t
391 mount_status_cb (void *opaque, const char *line)
392 {
393   struct mount_parm_s *parm = opaque;
394
395   /* Nothing right now.  */
396   (void)parm;
397   (void)line;
398
399   return 0;
400 }
401
402
403 /* Inquire callback for MOUNT and RESUME.  */
404 static gpg_error_t
405 mount_inq_cb (void *opaque, const char *line)
406 {
407   struct mount_parm_s *parm = opaque;
408   gpg_error_t err;
409
410   if (has_leading_keyword (line, "KEYBLOB"))
411     {
412       int setconfidential = !assuan_get_flag (parm->ctx, ASSUAN_CONFIDENTIAL);
413
414       if (setconfidential)
415         assuan_begin_confidential (parm->ctx);
416       err = assuan_send_data (parm->ctx, parm->keyblob, parm->keybloblen);
417       if (setconfidential)
418         assuan_end_confidential (parm->ctx);
419       if (err)
420         log_error ("sending keyblob to g13-syshelp failed: %s\n",
421                    gpg_strerror (err));
422     }
423   else
424     err = gpg_error (GPG_ERR_ASS_UNKNOWN_INQUIRE);
425
426   return err;
427 }
428
429
430 /*
431  * Run the MOUNT command on the current device.  CONTTYPES gives the
432  * requested content type for the new container.  MOUNTPOINT the
433  * desired mount point or NULL for default.
434  */
435 gpg_error_t
436 call_syshelp_run_mount (ctrl_t ctrl, int conttype, const char *mountpoint,
437                         tupledesc_t tuples)
438 {
439   gpg_error_t err;
440   assuan_context_t ctx;
441   struct mount_parm_s parm;
442
443   memset (&parm, 0, sizeof parm);
444
445   err = start_syshelp (ctrl, &ctx);
446   if (err)
447     goto leave;
448
449   /* tty_get ("waiting for debugger"); */
450   /* tty_kill_prompt (); */
451
452   parm.ctx = ctx;
453   parm.ctrl = ctrl;
454   if (conttype == CONTTYPE_DM_CRYPT)
455     {
456       ref_tupledesc (tuples);
457       parm.keyblob = get_tupledesc_data (tuples, &parm.keybloblen);
458       err = assuan_transact (ctx, "MOUNT dm-crypt",
459                              NULL, NULL,
460                              mount_inq_cb, &parm,
461                              mount_status_cb, &parm);
462       unref_tupledesc (tuples);
463     }
464   else
465     {
466       (void)mountpoint; /* Not used.  */
467       log_error ("invalid backend type %d given\n", conttype);
468       err = GPG_ERR_INTERNAL;
469       goto leave;
470     }
471
472  leave:
473   return err;
474 }
475
476
477 \f
478 /*
479  * Run the SUSPEND command on the current device.  CONTTYPES gives the
480  * requested content type for the new container.
481  */
482 gpg_error_t
483 call_syshelp_run_suspend (ctrl_t ctrl, int conttype)
484 {
485   gpg_error_t err;
486   assuan_context_t ctx;
487
488   err = start_syshelp (ctrl, &ctx);
489   if (err)
490     goto leave;
491
492   if (conttype == CONTTYPE_DM_CRYPT)
493     {
494       err = assuan_transact (ctx, "SUSPEND dm-crypt",
495                              NULL, NULL,
496                              NULL, NULL,
497                              NULL, NULL);
498     }
499   else
500     {
501       log_error ("invalid backend type %d given\n", conttype);
502       err = GPG_ERR_INTERNAL;
503       goto leave;
504     }
505
506  leave:
507   return err;
508 }
509
510
511 \f
512 /* Run the RESUME command on the current device.  CONTTYPES gives the
513    requested content type for the container.  */
514 gpg_error_t
515 call_syshelp_run_resume (ctrl_t ctrl, int conttype, tupledesc_t tuples)
516 {
517   gpg_error_t err;
518   assuan_context_t ctx;
519   struct mount_parm_s parm;
520
521   memset (&parm, 0, sizeof parm);
522
523   err = start_syshelp (ctrl, &ctx);
524   if (err)
525     goto leave;
526
527   /* tty_get ("waiting for debugger"); */
528   /* tty_kill_prompt (); */
529
530   parm.ctx = ctx;
531   parm.ctrl = ctrl;
532   if (conttype == CONTTYPE_DM_CRYPT)
533     {
534       ref_tupledesc (tuples);
535       parm.keyblob = get_tupledesc_data (tuples, &parm.keybloblen);
536       err = assuan_transact (ctx, "RESUME dm-crypt",
537                              NULL, NULL,
538                              mount_inq_cb, &parm,
539                              NULL, NULL);
540       unref_tupledesc (tuples);
541     }
542   else
543     {
544       log_error ("invalid backend type %d given\n", conttype);
545       err = GPG_ERR_INTERNAL;
546       goto leave;
547     }
548
549  leave:
550   return err;
551 }