chiark / gitweb /
doc: Document summary values of TOFU_STATS
[gnupg2.git] / g13 / sh-cmd.c
1 /* sh-cmd.c - The Assuan server for 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 <stdarg.h>
25 #include <errno.h>
26 #include <assert.h>
27
28 #include "g13-syshelp.h"
29 #include <assuan.h>
30 #include "i18n.h"
31 #include "keyblob.h"
32
33
34 /* Local data for this server module.  A pointer to this is stored in
35    the CTRL object of each connection.  */
36 struct server_local_s
37 {
38   /* The Assuan contect we are working on.  */
39   assuan_context_t assuan_ctx;
40
41   /* The malloced name of the device.  */
42   char *devicename;
43
44   /* A stream open for read of the device set by the DEVICE command or
45      NULL if no DEVICE command has been used.  */
46   estream_t devicefp;
47 };
48
49
50
51 \f
52 /* Local prototypes.  */
53
54
55
56 \f
57 /*
58    Helper functions.
59  */
60
61 /* Set an error and a description.  */
62 #define set_error(e,t) assuan_set_error (ctx, gpg_error (e), (t))
63 #define set_error_fail_cmd() set_error (GPG_ERR_NOT_INITIALIZED, \
64                                         "not called via userv or unknown user")
65
66
67 /* Skip over options.  Blanks after the options are also removed.  */
68 static char *
69 skip_options (const char *line)
70 {
71   while (spacep (line))
72     line++;
73   while ( *line == '-' && line[1] == '-' )
74     {
75       while (*line && !spacep (line))
76         line++;
77       while (spacep (line))
78         line++;
79     }
80   return (char*)line;
81 }
82
83
84 /* Check whether the option NAME appears in LINE.  */
85 /* static int */
86 /* has_option (const char *line, const char *name) */
87 /* { */
88 /*   const char *s; */
89 /*   int n = strlen (name); */
90
91 /*   s = strstr (line, name); */
92 /*   if (s && s >= skip_options (line)) */
93 /*     return 0; */
94 /*   return (s && (s == line || spacep (s-1)) && (!s[n] || spacep (s+n))); */
95 /* } */
96
97
98 /* Helper to print a message while leaving a command.  */
99 static gpg_error_t
100 leave_cmd (assuan_context_t ctx, gpg_error_t err)
101 {
102   if (err)
103     {
104       const char *name = assuan_get_command_name (ctx);
105       if (!name)
106         name = "?";
107       if (gpg_err_source (err) == GPG_ERR_SOURCE_DEFAULT)
108         log_error ("command '%s' failed: %s\n", name,
109                    gpg_strerror (err));
110       else
111         log_error ("command '%s' failed: %s <%s>\n", name,
112                    gpg_strerror (err), gpg_strsource (err));
113     }
114   return err;
115 }
116
117
118
119 \f
120 /* The handler for Assuan OPTION commands.  */
121 static gpg_error_t
122 option_handler (assuan_context_t ctx, const char *key, const char *value)
123 {
124   ctrl_t ctrl = assuan_get_pointer (ctx);
125   gpg_error_t err = 0;
126
127   (void)ctrl;
128   (void)key;
129   (void)value;
130
131   if (ctrl->fail_all_cmds)
132     err = set_error_fail_cmd ();
133   else
134     err = gpg_error (GPG_ERR_UNKNOWN_OPTION);
135
136   return err;
137 }
138
139
140 /* The handler for an Assuan RESET command.  */
141 static gpg_error_t
142 reset_notify (assuan_context_t ctx, char *line)
143 {
144   ctrl_t ctrl = assuan_get_pointer (ctx);
145
146   (void)line;
147
148   xfree (ctrl->server_local->devicename);
149   ctrl->server_local->devicename = NULL;
150   es_fclose (ctrl->server_local->devicefp);
151   ctrl->server_local->devicefp = NULL;
152   ctrl->devti = NULL;
153
154   assuan_close_input_fd (ctx);
155   assuan_close_output_fd (ctx);
156   return 0;
157 }
158
159
160 static const char hlp_finddevice[] =
161   "FINDDEVICE <name>\n"
162   "\n"
163   "Find the device matching NAME.  NAME be any identifier from\n"
164   "g13tab permissible for the user.  The corresponding block\n"
165   "device is returned using a status line.";
166 static gpg_error_t
167 cmd_finddevice (assuan_context_t ctx, char *line)
168 {
169   ctrl_t ctrl = assuan_get_pointer (ctx);
170   gpg_error_t err = 0;
171   tab_item_t ti;
172   const char *s;
173   const char *name;
174
175   name = skip_options (line);
176
177   /* Are we allowed to use the given device?  We check several names:
178    *  1. The full block device
179    *  2. The label
180    *  3. The final part of the block device if NAME does not have a slash.
181    *  4. The mountpoint
182    */
183   for (ti=ctrl->client.tab; ti; ti = ti->next)
184     if (!strcmp (name, ti->blockdev))
185       break;
186   if (!ti)
187     {
188       for (ti=ctrl->client.tab; ti; ti = ti->next)
189         if (ti->label && !strcmp (name, ti->label))
190           break;
191     }
192   if (!ti && !strchr (name, '/'))
193     {
194       for (ti=ctrl->client.tab; ti; ti = ti->next)
195         {
196           s = strrchr (ti->blockdev, '/');
197           if (s && s[1] && !strcmp (name, s+1))
198             break;
199         }
200     }
201   if (!ti)
202     {
203       for (ti=ctrl->client.tab; ti; ti = ti->next)
204         if (ti->mountpoint && !strcmp (name, ti->mountpoint))
205           break;
206     }
207
208   if (!ti)
209     {
210       err = set_error (GPG_ERR_NOT_FOUND, "device not configured for user");
211       goto leave;
212     }
213
214   /* Check whether we have permissions to open the device.  */
215   {
216     estream_t fp = es_fopen (ti->blockdev, "rb");
217     if (!fp)
218       {
219         err = gpg_error_from_syserror ();
220         log_error ("error opening '%s': %s\n",
221                    ti->blockdev, gpg_strerror (err));
222         goto leave;
223       }
224     es_fclose (fp);
225   }
226
227   err = g13_status (ctrl, STATUS_BLOCKDEV, ti->blockdev, NULL);
228   if (err)
229     return err;
230
231  leave:
232   return leave_cmd (ctx, err);
233 }
234
235
236 static const char hlp_device[] =
237   "DEVICE <name>\n"
238   "\n"
239   "Set the device used by further commands.\n"
240   "A device name or a PARTUUID string may be used.\n"
241   "Access to that device (by the g13 system) is locked\n"
242   "until a new DEVICE command or end of this process\n";
243 static gpg_error_t
244 cmd_device (assuan_context_t ctx, char *line)
245 {
246   ctrl_t ctrl = assuan_get_pointer (ctx);
247   gpg_error_t err = 0;
248   tab_item_t ti;
249   estream_t fp = NULL;
250
251   line = skip_options (line);
252
253 /* # warning hardwired to /dev/sdb1 ! */
254 /*   if (strcmp (line, "/dev/sdb1")) */
255 /*     { */
256 /*       err = gpg_error (GPG_ERR_ENOENT); */
257 /*       goto leave; */
258 /*     } */
259
260   /* Always close an open device stream of this session. */
261   xfree (ctrl->server_local->devicename);
262   ctrl->server_local->devicename = NULL;
263   es_fclose (ctrl->server_local->devicefp);
264   ctrl->server_local->devicefp = NULL;
265
266   /* Are we allowed to use the given device?  */
267   for (ti=ctrl->client.tab; ti; ti = ti->next)
268     if (!strcmp (line, ti->blockdev))
269       break;
270   if (!ti)
271     {
272       err = set_error (GPG_ERR_EACCES, "device not configured for user");
273       goto leave;
274     }
275
276   ctrl->server_local->devicename = xtrystrdup (line);
277   if (!ctrl->server_local->devicename)
278     {
279       err = gpg_error_from_syserror ();
280       goto leave;
281     }
282
283
284   /* Check whether we have permissions to open the device and keep an
285      FD open.  */
286   fp = es_fopen (ctrl->server_local->devicename, "rb");
287   if (!fp)
288     {
289       err = gpg_error_from_syserror ();
290       log_error ("error opening '%s': %s\n",
291                  ctrl->server_local->devicename, gpg_strerror (err));
292       goto leave;
293     }
294
295   es_fclose (ctrl->server_local->devicefp);
296   ctrl->server_local->devicefp = fp;
297   fp = NULL;
298   ctrl->devti = ti;
299
300   /* Fixme: Take some kind of lock.  */
301
302  leave:
303   es_fclose (fp);
304   if (err)
305     {
306       xfree (ctrl->server_local->devicename);
307       ctrl->server_local->devicename = NULL;
308       ctrl->devti = NULL;
309     }
310   return leave_cmd (ctx, err);
311 }
312
313
314 static const char hlp_create[] =
315   "CREATE <type>\n"
316   "\n"
317   "Create a new encrypted partition on the current device.\n"
318   "<type> must be \"dm-crypt\" for now.";
319 static gpg_error_t
320 cmd_create (assuan_context_t ctx, char *line)
321 {
322   ctrl_t ctrl = assuan_get_pointer (ctx);
323   gpg_error_t err = 0;
324   estream_t fp = NULL;
325
326   line = skip_options (line);
327   if (strcmp (line, "dm-crypt"))
328     {
329       err = set_error (GPG_ERR_INV_ARG, "Type must be \"dm-crypt\"");
330       goto leave;
331     }
332
333   if (!ctrl->server_local->devicename
334       || !ctrl->server_local->devicefp
335       || !ctrl->devti)
336     {
337       err = set_error (GPG_ERR_ENOENT, "No device has been set");
338       goto leave;
339     }
340
341   err = sh_is_empty_partition (ctrl->server_local->devicename);
342   if (err)
343     {
344       if (gpg_err_code (err) == GPG_ERR_FALSE)
345         err = gpg_error (GPG_ERR_CONFLICT);
346       err = assuan_set_error (ctx, err, "Partition is not empty");
347       goto leave;
348     }
349
350   /* We need a writeable stream to create the container.  */
351   fp = es_fopen (ctrl->server_local->devicename, "r+b");
352   if (!fp)
353     {
354       err = gpg_error_from_syserror ();
355       log_error ("error opening '%s': %s\n",
356                  ctrl->server_local->devicename, gpg_strerror (err));
357       goto leave;
358     }
359   if (es_setvbuf (fp, NULL, _IONBF, 0))
360     {
361       err = gpg_error_from_syserror ();
362       log_error ("error setting '%s' to _IONBF: %s\n",
363                  ctrl->server_local->devicename, gpg_strerror (err));
364       goto leave;
365     }
366
367   err = sh_dmcrypt_create_container (ctrl,
368                                      ctrl->server_local->devicename,
369                                      fp);
370   if (es_fclose (fp))
371     {
372       gpg_error_t err2 = gpg_error_from_syserror ();
373       log_error ("error closing '%s': %s\n",
374                  ctrl->server_local->devicename, gpg_strerror (err2));
375       if (!err)
376         err = err2;
377     }
378   fp = NULL;
379
380  leave:
381   es_fclose (fp);
382   return leave_cmd (ctx, err);
383 }
384
385
386 static const char hlp_getkeyblob[] =
387   "GETKEYBLOB\n"
388   "\n"
389   "Return the encrypted keyblob of the current device.";
390 static gpg_error_t
391 cmd_getkeyblob (assuan_context_t ctx, char *line)
392 {
393   ctrl_t ctrl = assuan_get_pointer (ctx);
394   gpg_error_t err;
395   void *enckeyblob = NULL;
396   size_t enckeybloblen;
397
398   line = skip_options (line);
399
400   if (!ctrl->server_local->devicename
401       || !ctrl->server_local->devicefp
402       || !ctrl->devti)
403     {
404       err = set_error (GPG_ERR_ENOENT, "No device has been set");
405       goto leave;
406     }
407
408   err = sh_is_empty_partition (ctrl->server_local->devicename);
409   if (!err)
410     {
411       err = gpg_error (GPG_ERR_ENODEV);
412       assuan_set_error (ctx, err, "Partition is empty");
413       goto leave;
414     }
415   err = 0;
416
417   err = g13_keyblob_read (ctrl->server_local->devicename,
418                           &enckeyblob, &enckeybloblen);
419   if (err)
420     goto leave;
421
422   err = assuan_send_data (ctx, enckeyblob, enckeybloblen);
423   if (!err)
424     err = assuan_send_data (ctx, NULL, 0); /* Flush  */
425
426  leave:
427   xfree (enckeyblob);
428   return leave_cmd (ctx, err);
429 }
430
431
432 static const char hlp_mount[] =
433   "MOUNT <type>\n"
434   "\n"
435   "Mount an encrypted partition on the current device.\n"
436   "<type> must be \"dm-crypt\" for now.";
437 static gpg_error_t
438 cmd_mount (assuan_context_t ctx, char *line)
439 {
440   ctrl_t ctrl = assuan_get_pointer (ctx);
441   gpg_error_t err = 0;
442   unsigned char *keyblob = NULL;
443   size_t keybloblen;
444   tupledesc_t tuples = NULL;
445
446   line = skip_options (line);
447
448   if (strcmp (line, "dm-crypt"))
449     {
450       err = set_error (GPG_ERR_INV_ARG, "Type must be \"dm-crypt\"");
451       goto leave;
452     }
453
454   if (!ctrl->server_local->devicename
455       || !ctrl->server_local->devicefp
456       || !ctrl->devti)
457     {
458       err = set_error (GPG_ERR_ENOENT, "No device has been set");
459       goto leave;
460     }
461
462   err = sh_is_empty_partition (ctrl->server_local->devicename);
463   if (!err)
464     {
465       err = gpg_error (GPG_ERR_ENODEV);
466       assuan_set_error (ctx, err, "Partition is empty");
467       goto leave;
468     }
469   err = 0;
470
471   /* We expect that the client already decrypted the keyblob.
472    * Eventually we should move reading of the keyblob to here and ask
473    * the client to decrypt it.  */
474   assuan_begin_confidential (ctx);
475   err = assuan_inquire (ctx, "KEYBLOB",
476                         &keyblob, &keybloblen, 4 * 1024);
477   assuan_end_confidential (ctx);
478   if (err)
479     {
480       log_error (_("assuan_inquire failed: %s\n"), gpg_strerror (err));
481       goto leave;
482     }
483   err = create_tupledesc (&tuples, keyblob, keybloblen);
484   if (!err)
485     keyblob = NULL;
486   else
487     {
488       if (gpg_err_code (err) == GPG_ERR_NOT_SUPPORTED)
489         log_error ("unknown keyblob version received\n");
490       goto leave;
491     }
492
493   err = sh_dmcrypt_mount_container (ctrl,
494                                     ctrl->server_local->devicename,
495                                     tuples);
496
497  leave:
498   destroy_tupledesc (tuples);
499   return leave_cmd (ctx, err);
500 }
501
502
503 static const char hlp_umount[] =
504   "UMOUNT <type>\n"
505   "\n"
506   "Unmount an encrypted partition and wipe the key.\n"
507   "<type> must be \"dm-crypt\" for now.";
508 static gpg_error_t
509 cmd_umount (assuan_context_t ctx, char *line)
510 {
511   ctrl_t ctrl = assuan_get_pointer (ctx);
512   gpg_error_t err = 0;
513
514   line = skip_options (line);
515
516   if (strcmp (line, "dm-crypt"))
517     {
518       err = set_error (GPG_ERR_INV_ARG, "Type must be \"dm-crypt\"");
519       goto leave;
520     }
521
522   if (!ctrl->server_local->devicename
523       || !ctrl->server_local->devicefp
524       || !ctrl->devti)
525     {
526       err = set_error (GPG_ERR_ENOENT, "No device has been set");
527       goto leave;
528     }
529
530   err = sh_dmcrypt_umount_container (ctrl, ctrl->server_local->devicename);
531
532  leave:
533   return leave_cmd (ctx, err);
534 }
535
536
537 static const char hlp_suspend[] =
538   "SUSPEND <type>\n"
539   "\n"
540   "Suspend an encrypted partition and wipe the key.\n"
541   "<type> must be \"dm-crypt\" for now.";
542 static gpg_error_t
543 cmd_suspend (assuan_context_t ctx, char *line)
544 {
545   ctrl_t ctrl = assuan_get_pointer (ctx);
546   gpg_error_t err = 0;
547
548   line = skip_options (line);
549
550   if (strcmp (line, "dm-crypt"))
551     {
552       err = set_error (GPG_ERR_INV_ARG, "Type must be \"dm-crypt\"");
553       goto leave;
554     }
555
556   if (!ctrl->server_local->devicename
557       || !ctrl->server_local->devicefp
558       || !ctrl->devti)
559     {
560       err = set_error (GPG_ERR_ENOENT, "No device has been set");
561       goto leave;
562     }
563
564   err = sh_is_empty_partition (ctrl->server_local->devicename);
565   if (!err)
566     {
567       err = gpg_error (GPG_ERR_ENODEV);
568       assuan_set_error (ctx, err, "Partition is empty");
569       goto leave;
570     }
571   err = 0;
572
573   err = sh_dmcrypt_suspend_container (ctrl, ctrl->server_local->devicename);
574
575  leave:
576   return leave_cmd (ctx, err);
577 }
578
579
580 static const char hlp_resume[] =
581   "RESUME <type>\n"
582   "\n"
583   "Resume an encrypted partition and set the key.\n"
584   "<type> must be \"dm-crypt\" for now.";
585 static gpg_error_t
586 cmd_resume (assuan_context_t ctx, char *line)
587 {
588   ctrl_t ctrl = assuan_get_pointer (ctx);
589   gpg_error_t err = 0;
590   unsigned char *keyblob = NULL;
591   size_t keybloblen;
592   tupledesc_t tuples = NULL;
593
594   line = skip_options (line);
595
596   if (strcmp (line, "dm-crypt"))
597     {
598       err = set_error (GPG_ERR_INV_ARG, "Type must be \"dm-crypt\"");
599       goto leave;
600     }
601
602   if (!ctrl->server_local->devicename
603       || !ctrl->server_local->devicefp
604       || !ctrl->devti)
605     {
606       err = set_error (GPG_ERR_ENOENT, "No device has been set");
607       goto leave;
608     }
609
610   err = sh_is_empty_partition (ctrl->server_local->devicename);
611   if (!err)
612     {
613       err = gpg_error (GPG_ERR_ENODEV);
614       assuan_set_error (ctx, err, "Partition is empty");
615       goto leave;
616     }
617   err = 0;
618
619   /* We expect that the client already decrypted the keyblob.
620    * Eventually we should move reading of the keyblob to here and ask
621    * the client to decrypt it.  */
622   assuan_begin_confidential (ctx);
623   err = assuan_inquire (ctx, "KEYBLOB",
624                         &keyblob, &keybloblen, 4 * 1024);
625   assuan_end_confidential (ctx);
626   if (err)
627     {
628       log_error (_("assuan_inquire failed: %s\n"), gpg_strerror (err));
629       goto leave;
630     }
631   err = create_tupledesc (&tuples, keyblob, keybloblen);
632   if (!err)
633     keyblob = NULL;
634   else
635     {
636       if (gpg_err_code (err) == GPG_ERR_NOT_SUPPORTED)
637         log_error ("unknown keyblob version received\n");
638       goto leave;
639     }
640
641   err = sh_dmcrypt_resume_container (ctrl,
642                                      ctrl->server_local->devicename,
643                                      tuples);
644
645  leave:
646   destroy_tupledesc (tuples);
647   return leave_cmd (ctx, err);
648 }
649
650
651 static const char hlp_getinfo[] =
652   "GETINFO <what>\n"
653   "\n"
654   "Multipurpose function to return a variety of information.\n"
655   "Supported values for WHAT are:\n"
656   "\n"
657   "  version     - Return the version of the program.\n"
658   "  pid         - Return the process id of the server.\n"
659   "  showtab     - Show the table for the user.";
660 static gpg_error_t
661 cmd_getinfo (assuan_context_t ctx, char *line)
662 {
663   ctrl_t ctrl = assuan_get_pointer (ctx);
664   gpg_error_t err = 0;
665   char *buf;
666
667   if (!strcmp (line, "version"))
668     {
669       const char *s = PACKAGE_VERSION;
670       err = assuan_send_data (ctx, s, strlen (s));
671     }
672   else if (!strcmp (line, "pid"))
673     {
674       char numbuf[50];
675
676       snprintf (numbuf, sizeof numbuf, "%lu", (unsigned long)getpid ());
677       err = assuan_send_data (ctx, numbuf, strlen (numbuf));
678     }
679   else if (!strncmp (line, "getsz", 5))
680     {
681       unsigned long long nblocks;
682       err = sh_blockdev_getsz (line+6, &nblocks);
683       if (!err)
684         log_debug ("getsz=%llu\n", nblocks);
685     }
686   else if (!strcmp (line, "showtab"))
687     {
688       tab_item_t ti;
689
690       for (ti=ctrl->client.tab; !err && ti; ti = ti->next)
691         {
692           buf = es_bsprintf ("%s %s%s %s %s%s\n",
693                              ctrl->client.uname,
694                              *ti->blockdev=='/'? "":"partuuid=",
695                              ti->blockdev,
696                              ti->label? ti->label : "-",
697                              ti->mountpoint? " ":"",
698                              ti->mountpoint? ti->mountpoint:"");
699           if (!buf)
700             err = gpg_error_from_syserror ();
701           else
702             {
703               err = assuan_send_data (ctx, buf, strlen (buf));
704               if (!err)
705                 err = assuan_send_data (ctx, NULL, 0); /* Flush  */
706             }
707           xfree (buf);
708         }
709     }
710   else
711     err = set_error (GPG_ERR_ASS_PARAMETER, "unknown value for WHAT");
712
713   return leave_cmd (ctx, err);
714 }
715
716
717 /* This command handler is used for all commands if this process has
718    not been started as expected.  */
719 static gpg_error_t
720 fail_command (assuan_context_t ctx, char *line)
721 {
722   gpg_error_t err;
723   const char *name = assuan_get_command_name (ctx);
724
725   (void)line;
726
727   if (!name)
728     name = "?";
729
730   err = set_error_fail_cmd ();
731   log_error ("command '%s' failed: %s\n", name, gpg_strerror (err));
732   return err;
733 }
734
735
736 /* Tell the Assuan library about our commands.  */
737 static int
738 register_commands (assuan_context_t ctx, int fail_all)
739 {
740   static struct {
741     const char *name;
742     assuan_handler_t handler;
743     const char * const help;
744   } table[] =  {
745     { "FINDDEVICE",    cmd_finddevice, hlp_finddevice },
746     { "DEVICE",        cmd_device, hlp_device },
747     { "CREATE",        cmd_create, hlp_create },
748     { "GETKEYBLOB",    cmd_getkeyblob,  hlp_getkeyblob },
749     { "MOUNT",         cmd_mount,  hlp_mount  },
750     { "UMOUNT",        cmd_umount, hlp_umount  },
751     { "SUSPEND",       cmd_suspend,hlp_suspend},
752     { "RESUME",        cmd_resume, hlp_resume },
753     { "INPUT",         NULL },
754     { "OUTPUT",        NULL },
755     { "GETINFO",       cmd_getinfo, hlp_getinfo },
756     { NULL }
757   };
758   gpg_error_t err;
759   int i;
760
761   for (i=0; table[i].name; i++)
762     {
763       err = assuan_register_command (ctx, table[i].name,
764                                      fail_all ? fail_command : table[i].handler,
765                                      table[i].help);
766       if (err)
767         return err;
768     }
769   return 0;
770 }
771
772
773 /* Startup the server.  */
774 gpg_error_t
775 syshelp_server (ctrl_t ctrl)
776 {
777   gpg_error_t err;
778   assuan_fd_t filedes[2];
779   assuan_context_t ctx = NULL;
780
781   /* We use a pipe based server so that we can work from scripts.
782      assuan_init_pipe_server will automagically detect when we are
783      called with a socketpair and ignore FILEDES in this case. */
784   filedes[0] = assuan_fdopen (0);
785   filedes[1] = assuan_fdopen (1);
786   err = assuan_new (&ctx);
787   if (err)
788     {
789       log_error ("failed to allocate an Assuan context: %s\n",
790                  gpg_strerror (err));
791       goto leave;
792     }
793
794   err = assuan_init_pipe_server (ctx, filedes);
795   if (err)
796     {
797       log_error ("failed to initialize the server: %s\n", gpg_strerror (err));
798       goto leave;
799     }
800
801   err = register_commands (ctx, 0 /*FIXME:ctrl->fail_all_cmds*/);
802   if (err)
803     {
804       log_error ("failed to the register commands with Assuan: %s\n",
805                  gpg_strerror (err));
806       goto leave;
807     }
808
809   assuan_set_pointer (ctx, ctrl);
810
811   {
812     char *tmp = xtryasprintf ("G13-syshelp %s ready to serve requests "
813                               "from %lu(%s)",
814                               PACKAGE_VERSION,
815                               (unsigned long)ctrl->client.uid,
816                               ctrl->client.uname);
817     if (tmp)
818       {
819         assuan_set_hello_line (ctx, tmp);
820         xfree (tmp);
821       }
822   }
823
824   assuan_register_reset_notify (ctx, reset_notify);
825   assuan_register_option_handler (ctx, option_handler);
826
827   ctrl->server_local = xtrycalloc (1, sizeof *ctrl->server_local);
828   if (!ctrl->server_local)
829     {
830       err = gpg_error_from_syserror ();
831       goto leave;
832     }
833   ctrl->server_local->assuan_ctx = ctx;
834
835   while ( !(err = assuan_accept (ctx)) )
836     {
837       err = assuan_process (ctx);
838       if (err)
839         log_info ("Assuan processing failed: %s\n", gpg_strerror (err));
840     }
841   if (err == -1)
842     err = 0;
843   else
844     log_info ("Assuan accept problem: %s\n", gpg_strerror (err));
845
846  leave:
847   reset_notify (ctx, NULL);  /* Release all items hold by SERVER_LOCAL.  */
848   if (ctrl->server_local)
849     {
850       xfree (ctrl->server_local);
851       ctrl->server_local = NULL;
852     }
853
854   assuan_release (ctx);
855   return err;
856 }
857
858
859 gpg_error_t
860 sh_encrypt_keyblob (ctrl_t ctrl, const void *keyblob, size_t keybloblen,
861                     char **r_enckeyblob, size_t *r_enckeybloblen)
862 {
863   assuan_context_t ctx = ctrl->server_local->assuan_ctx;
864   gpg_error_t err;
865   unsigned char *enckeyblob;
866   size_t enckeybloblen;
867
868   *r_enckeyblob = NULL;
869
870   /* Send the plaintext.  */
871   err = g13_status (ctrl, STATUS_PLAINTEXT_FOLLOWS, NULL);
872   if (err)
873     return err;
874   assuan_begin_confidential (ctx);
875   err = assuan_send_data (ctx, keyblob, keybloblen);
876   if (!err)
877     err = assuan_send_data (ctx, NULL, 0);
878   assuan_end_confidential (ctx);
879   if (!err)
880     err = assuan_write_line (ctx, "END");
881   if (err)
882     {
883       log_error (_("error sending data: %s\n"), gpg_strerror (err));
884       return err;
885     }
886
887   /* Inquire the ciphertext.  */
888   err = assuan_inquire (ctx, "ENCKEYBLOB",
889                         &enckeyblob, &enckeybloblen, 16 * 1024);
890   if (err)
891     {
892       log_error (_("assuan_inquire failed: %s\n"), gpg_strerror (err));
893       return err;
894     }
895
896   *r_enckeyblob = enckeyblob;
897   *r_enckeybloblen = enckeybloblen;
898   return 0;
899 }
900
901
902 /* Send a status line with status ID NO.  The arguments are a list of
903    strings terminated by a NULL argument.  */
904 gpg_error_t
905 g13_status (ctrl_t ctrl, int no, ...)
906 {
907   gpg_error_t err = 0;
908   va_list arg_ptr;
909   const char *text;
910
911   va_start (arg_ptr, no);
912
913   if (1)
914     {
915       assuan_context_t ctx = ctrl->server_local->assuan_ctx;
916       char buf[950], *p;
917       size_t n;
918
919       p = buf;
920       n = 0;
921       while ( (text = va_arg (arg_ptr, const char *)) )
922         {
923           if (n)
924             {
925               *p++ = ' ';
926               n++;
927             }
928           for ( ; *text && n < DIM (buf)-2; n++)
929             *p++ = *text++;
930         }
931       *p = 0;
932       err = assuan_write_status (ctx, get_status_string (no), buf);
933     }
934
935   va_end (arg_ptr);
936   return err;
937 }