g13: Add commands --suspend and --remove.
[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
37
38 /* Local data for this module.  A pointer to this is stored in the
39    CTRL object of each connection.  */
40 struct call_syshelp_s
41 {
42   assuan_context_t assctx;  /* The Assuan context for the current
43                                g13-syshep connection.  */
44 };
45
46
47 /* Parameter used with the CREATE command.  */
48 struct create_parm_s
49 {
50   assuan_context_t ctx;
51   ctrl_t ctrl;
52   membuf_t plaintext;
53   unsigned int expect_plaintext:1;
54   unsigned int got_plaintext:1;
55 };
56
57
58 /* Parameter used with the MOUNT command.  */
59 struct mount_parm_s
60 {
61   assuan_context_t ctx;
62   ctrl_t ctrl;
63   const void *keyblob;
64   size_t keybloblen;
65 };
66
67
68
69
70 \f
71 /* Fork off the syshelp tool if this has not already been done.  On
72    success stores the current Assuan context for the syshelp tool at
73    R_CTX.  */
74 static gpg_error_t
75 start_syshelp (ctrl_t ctrl, assuan_context_t *r_ctx)
76 {
77   gpg_error_t err;
78   assuan_context_t ctx;
79   assuan_fd_t no_close_list[3];
80   int i;
81
82   *r_ctx = NULL;
83
84   if (ctrl->syshelp_local && (*r_ctx = ctrl->syshelp_local->assctx))
85     return 0; /* Already set.  */
86
87   if (opt.verbose)
88     log_info ("starting a new syshelp\n");
89
90   if (!ctrl->syshelp_local)
91     {
92       ctrl->syshelp_local = xtrycalloc (1, sizeof *ctrl->syshelp_local);
93       if (!ctrl->syshelp_local)
94         return gpg_error_from_syserror ();
95     }
96
97   if (es_fflush (NULL))
98     {
99       err = gpg_error_from_syserror ();
100       log_error ("error flushing pending output: %s\n", gpg_strerror (err));
101       return err;
102     }
103
104   i = 0;
105   if (log_get_fd () != -1)
106     no_close_list[i++] = assuan_fd_from_posix_fd (log_get_fd ());
107   no_close_list[i++] = assuan_fd_from_posix_fd (es_fileno (es_stderr));
108   no_close_list[i] = ASSUAN_INVALID_FD;
109
110   err = assuan_new (&ctx);
111   if (err)
112     {
113       log_error ("can't allocate assuan context: %s\n", gpg_strerror (err));
114       return err;
115     }
116
117   /* Call userv to start g13-syshelp.  This userv script needs to be
118    * installed under the name "gnupg-g13-syshelp":
119    *
120    *   if ( glob service-user root
121    *      )
122    *       reset
123    *       suppress-args
124    *       execute /home/wk/b/gnupg/g13/g13-syshelp -v
125    *   else
126    *       error Nothing to do for this service-user
127    *   fi
128    *   quit
129    */
130   {
131     const char *argv[4];
132
133     argv[0] = "userv";
134     argv[1] = "root";
135     argv[2] = "gnupg-g13-syshelp";
136     argv[3] = NULL;
137
138     err = assuan_pipe_connect (ctx, "/usr/bin/userv", argv,
139                                no_close_list, NULL, NULL, 0);
140   }
141   if (err)
142     {
143       log_error ("can't connect to '%s': %s %s\n",
144                  "g13-syshelp", gpg_strerror (err), gpg_strsource (err));
145       log_info ("(is userv and its gnupg-g13-syshelp script installed?)\n");
146       assuan_release (ctx);
147       return err;
148     }
149
150   *r_ctx = ctrl->syshelp_local->assctx = ctx;
151
152   if (DBG_IPC)
153     log_debug ("connection to g13-syshelp established\n");
154
155   return 0;
156 }
157
158
159 /* Release local resources associated with CTRL.  */
160 void
161 call_syshelp_release (ctrl_t ctrl)
162 {
163   if (!ctrl)
164     return;
165   if (ctrl->syshelp_local)
166     {
167       assuan_release (ctrl->syshelp_local->assctx);
168       ctrl->syshelp_local->assctx = NULL;
169       xfree (ctrl->syshelp_local);
170       ctrl->syshelp_local = NULL;
171     }
172 }
173
174
175 /* Send the DEVICE command to the syshelper.  FNAME is the name of the
176    device.  */
177 gpg_error_t
178 call_syshelp_set_device (ctrl_t ctrl, const char *fname)
179 {
180   gpg_error_t err;
181   assuan_context_t ctx;
182   char *line = NULL;
183
184   err = start_syshelp (ctrl, &ctx);
185   if (err)
186     goto leave;
187
188   line = xtryasprintf ("DEVICE %s", fname);
189   if (!line)
190     {
191       err = gpg_error_from_syserror ();
192       goto leave;
193     }
194   err = assuan_transact (ctx, line, NULL, NULL, NULL, NULL, NULL, NULL);
195
196  leave:
197   xfree (line);
198   return err;
199 }
200
201
202 \f
203 static gpg_error_t
204 create_status_cb (void *opaque, const char *line)
205 {
206   struct create_parm_s *parm = opaque;
207
208   if (has_leading_keyword (line, "PLAINTEXT_FOLLOWS"))
209     parm->expect_plaintext = 1;
210
211   return 0;
212 }
213
214
215 static gpg_error_t
216 create_data_cb (void *opaque, const void *data, size_t datalen)
217 {
218   struct create_parm_s *parm = opaque;
219   gpg_error_t err = 0;
220
221   if (!parm->expect_plaintext)
222     {
223       log_error ("status line for data missing\n");
224       err = gpg_error (GPG_ERR_UNEXPECTED);
225     }
226   else if (data)
227     {
228       put_membuf (&parm->plaintext, data, datalen);
229     }
230   else
231     {
232       parm->expect_plaintext = 0;
233       parm->got_plaintext = 1;
234     }
235
236   return err;
237 }
238
239
240 static gpg_error_t
241 create_inq_cb (void *opaque, const char *line)
242 {
243   struct create_parm_s *parm = opaque;
244   gpg_error_t err;
245
246   if (has_leading_keyword (line, "ENCKEYBLOB"))
247     {
248       void *plaintext;
249       size_t plaintextlen;
250
251       if (!parm->got_plaintext)
252         err = gpg_error (GPG_ERR_UNEXPECTED);
253       else if (!(plaintext = get_membuf (&parm->plaintext, &plaintextlen)))
254         err = gpg_error_from_syserror ();
255       else
256         {
257           void *ciphertext;
258           size_t ciphertextlen;
259
260           log_printhex ("plain", plaintext, plaintextlen);
261           err = g13_encrypt_keyblob (parm->ctrl,
262                                      plaintext, plaintextlen,
263                                      &ciphertext, &ciphertextlen);
264           wipememory (plaintext, plaintextlen);
265           xfree (plaintext);
266           if (err)
267             log_error ("error encrypting keyblob: %s\n", gpg_strerror (err));
268           else
269             {
270               err = assuan_send_data (parm->ctx, ciphertext, ciphertextlen);
271               xfree (ciphertext);
272               if (err)
273                 log_error ("sending ciphertext to g13-syshelp failed: %s\n",
274                            gpg_strerror (err));
275             }
276         }
277     }
278   else
279     err = gpg_error (GPG_ERR_ASS_UNKNOWN_INQUIRE);
280
281   return err;
282 }
283
284
285 /* Run the CREATE command on the current device.  CONTTYPES gives the
286    requested content type for the new container.  */
287 gpg_error_t
288 call_syshelp_run_create (ctrl_t ctrl, int conttype)
289 {
290   gpg_error_t err;
291   assuan_context_t ctx;
292   struct create_parm_s parm;
293
294   memset (&parm, 0, sizeof parm);
295
296   err = start_syshelp (ctrl, &ctx);
297   if (err)
298     goto leave;
299
300   /* tty_get ("waiting for debugger"); */
301   /* tty_kill_prompt (); */
302
303   parm.ctx = ctx;
304   parm.ctrl = ctrl;
305   init_membuf (&parm.plaintext, 512);
306   if (conttype == CONTTYPE_DM_CRYPT)
307     {
308       err = assuan_transact (ctx, "CREATE dm-crypt",
309                              create_data_cb, &parm,
310                              create_inq_cb, &parm,
311                              create_status_cb, &parm);
312     }
313   else
314     {
315       log_error ("invalid backend type %d given\n", conttype);
316       err = GPG_ERR_INTERNAL;
317       goto leave;
318     }
319
320  leave:
321   xfree (get_membuf (&parm.plaintext, NULL));
322   return err;
323 }
324
325
326 \f
327 static gpg_error_t
328 mount_status_cb (void *opaque, const char *line)
329 {
330   struct mount_parm_s *parm = opaque;
331
332   /* Nothing right now.  */
333   (void)parm;
334   (void)line;
335
336   return 0;
337 }
338
339
340 /* Inquire callback for MOUNT and RESUME.  */
341 static gpg_error_t
342 mount_inq_cb (void *opaque, const char *line)
343 {
344   struct mount_parm_s *parm = opaque;
345   gpg_error_t err;
346
347   if (has_leading_keyword (line, "KEYBLOB"))
348     {
349       int setconfidential = !assuan_get_flag (parm->ctx, ASSUAN_CONFIDENTIAL);
350
351       if (setconfidential)
352         assuan_begin_confidential (parm->ctx);
353       err = assuan_send_data (parm->ctx, parm->keyblob, parm->keybloblen);
354       if (setconfidential)
355         assuan_end_confidential (parm->ctx);
356       if (err)
357         log_error ("sending keyblob to g13-syshelp failed: %s\n",
358                    gpg_strerror (err));
359     }
360   else
361     err = gpg_error (GPG_ERR_ASS_UNKNOWN_INQUIRE);
362
363   return err;
364 }
365
366
367 /*
368  * Run the MOUNT command on the current device.  CONTTYPES gives the
369  * requested content type for the new container.  MOUNTPOINT the
370  * desired mount point or NULL for default.
371  */
372 gpg_error_t
373 call_syshelp_run_mount (ctrl_t ctrl, int conttype, const char *mountpoint,
374                         tupledesc_t tuples)
375 {
376   gpg_error_t err;
377   assuan_context_t ctx;
378   struct mount_parm_s parm;
379
380   memset (&parm, 0, sizeof parm);
381
382   err = start_syshelp (ctrl, &ctx);
383   if (err)
384     goto leave;
385
386   /* tty_get ("waiting for debugger"); */
387   /* tty_kill_prompt (); */
388
389   parm.ctx = ctx;
390   parm.ctrl = ctrl;
391   if (conttype == CONTTYPE_DM_CRYPT)
392     {
393       ref_tupledesc (tuples);
394       parm.keyblob = get_tupledesc_data (tuples, &parm.keybloblen);
395       err = assuan_transact (ctx, "MOUNT dm-crypt",
396                              NULL, NULL,
397                              mount_inq_cb, &parm,
398                              mount_status_cb, &parm);
399       unref_tupledesc (tuples);
400     }
401   else
402     {
403       (void)mountpoint; /* Not used.  */
404       log_error ("invalid backend type %d given\n", conttype);
405       err = GPG_ERR_INTERNAL;
406       goto leave;
407     }
408
409  leave:
410   return err;
411 }
412
413
414 \f
415 /*
416  * Run the SUSPEND command on the current device.  CONTTYPES gives the
417  * requested content type for the new container.
418  */
419 gpg_error_t
420 call_syshelp_run_suspend (ctrl_t ctrl, int conttype)
421 {
422   gpg_error_t err;
423   assuan_context_t ctx;
424
425   err = start_syshelp (ctrl, &ctx);
426   if (err)
427     goto leave;
428
429   if (conttype == CONTTYPE_DM_CRYPT)
430     {
431       err = assuan_transact (ctx, "SUSPEND dm-crypt",
432                              NULL, NULL,
433                              NULL, NULL,
434                              NULL, NULL);
435     }
436   else
437     {
438       log_error ("invalid backend type %d given\n", conttype);
439       err = GPG_ERR_INTERNAL;
440       goto leave;
441     }
442
443  leave:
444   return err;
445 }
446
447
448 \f
449 /* Run the RESUME command on the current device.  CONTTYPES gives the
450    requested content type for the container.  */
451 gpg_error_t
452 call_syshelp_run_resume (ctrl_t ctrl, int conttype, tupledesc_t tuples)
453 {
454   gpg_error_t err;
455   assuan_context_t ctx;
456   struct mount_parm_s parm;
457
458   memset (&parm, 0, sizeof parm);
459
460   err = start_syshelp (ctrl, &ctx);
461   if (err)
462     goto leave;
463
464   /* tty_get ("waiting for debugger"); */
465   /* tty_kill_prompt (); */
466
467   parm.ctx = ctx;
468   parm.ctrl = ctrl;
469   if (conttype == CONTTYPE_DM_CRYPT)
470     {
471       ref_tupledesc (tuples);
472       parm.keyblob = get_tupledesc_data (tuples, &parm.keybloblen);
473       err = assuan_transact (ctx, "RESUME dm-crypt",
474                              NULL, NULL,
475                              mount_inq_cb, &parm,
476                              NULL, NULL);
477       unref_tupledesc (tuples);
478     }
479   else
480     {
481       log_error ("invalid backend type %d given\n", conttype);
482       err = GPG_ERR_INTERNAL;
483       goto leave;
484     }
485
486  leave:
487   return err;
488 }