2001-11-23 Marcus Brinkmann <marcus@g10code.de>
[gpgme.git] / gpgme / engine-gpgsm.c
1 /* engine-gpgsm.c -  GpgSM engine
2  *      Copyright (C) 2000 Werner Koch (dd9jn)
3  *      Copyright (C) 2001 g10 Code GmbH
4  *
5  * This file is part of GPGME.
6  *
7  * GPGME is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  *
12  * GPGME is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
20  */
21
22 #if HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25
26 /* FIXME: Correct check?  */
27 #ifdef GPGSM_PATH
28 #define ENABLE_GPGSM 1
29 #endif
30
31 #ifdef ENABLE_GPGSM
32
33 #include <stdlib.h>
34 #include <string.h>
35 #include <sys/types.h>
36 #include <assert.h>
37 #include <fcntl.h> /* FIXME */
38
39 /* FIXME */
40 #include "../assuan/assuan-defs.h"
41 #undef xtrymalloc
42 #undef xtrycalloc
43 #undef xtryrealloc
44 #undef xfree
45
46 #include "rungpg.h"
47 #include "status-table.h"
48
49 #include "gpgme.h"
50 #include "util.h"
51 #include "types.h"
52 #include "ops.h"
53 #include "wait.h"
54 #include "io.h"
55 #include "key.h"
56
57 #include "engine-gpgsm.h"
58
59 #include "assuan.h"
60
61 struct gpgsm_object_s
62 {
63   ASSUAN_CONTEXT assuan_ctx;
64
65   /* Input, output etc are from the servers perspective.  */
66   int input_fd;
67   int input_fd_server;
68   GpgmeData input_data;
69   int output_fd;
70   int output_fd_server;
71   GpgmeData output_data;
72   int message_fd;
73   int message_fd_server;
74   GpgmeData message_data;
75
76   char *command;
77
78   struct
79   {
80     GpgStatusHandler fnc;
81     void *fnc_value;
82   } status;
83   
84 };
85
86 const char *
87 _gpgme_gpgsm_get_version (void)
88 {
89   static const char *gpgsm_version;
90
91   /* FIXME: Locking.  */
92   if (!gpgsm_version)
93     gpgsm_version = _gpgme_get_program_version (_gpgme_get_gpgsm_path ());
94
95   return gpgsm_version;
96 }
97
98 GpgmeError
99 _gpgme_gpgsm_check_version (void)
100 {
101   return _gpgme_compare_versions (_gpgme_gpgsm_get_version (),
102                                   NEED_GPGSM_VERSION)
103     ? 0 : mk_error (Invalid_Engine);
104 }
105
106 GpgmeError
107 _gpgme_gpgsm_new (GpgsmObject *r_gpgsm)
108 {
109   GpgmeError err = 0;
110   GpgsmObject gpgsm;
111   char *argv[] = { "gpgsm", "--server", NULL };
112   int ip[2] = { -1, -1 };
113   int op[2] = { -1, -1 };
114   int mp[2] = { -1, -1 };
115
116   *r_gpgsm = NULL;
117   gpgsm = xtrycalloc (1, sizeof *gpgsm);
118   if (!gpgsm)
119     {
120       err = mk_error (Out_Of_Core);
121       goto leave;
122     }
123
124   if (_gpgme_io_pipe (ip, 0) < 0)
125     {
126       err = mk_error (General_Error);
127       goto leave;
128     }
129   gpgsm->input_fd = ip[1];
130   fcntl (ip[1], F_SETFD, FD_CLOEXEC); /* FIXME */
131   gpgsm->input_fd_server = ip[0];
132   if (_gpgme_io_pipe (op, 1) < 0)
133     {
134       err = mk_error (General_Error);
135       goto leave;
136     }
137   gpgsm->output_fd = op[0];
138   fcntl (op[0], F_SETFD, FD_CLOEXEC); /* FIXME */
139   gpgsm->output_fd_server = op[1];
140   if (_gpgme_io_pipe (mp, 0) < 0)
141     {
142       err = mk_error (General_Error);
143       goto leave;
144     }
145   gpgsm->message_fd = mp[1];
146   fcntl (mp[1], F_SETFD, FD_CLOEXEC); /* FIXME */
147   gpgsm->message_fd_server = mp[0];
148
149   err = assuan_pipe_connect (&gpgsm->assuan_ctx,
150                              _gpgme_get_gpgsm_path (), argv);
151
152  leave:
153   if (ip[0] != -1)
154     _gpgme_io_close (ip[0]);
155   if (op[1] != -1)
156     _gpgme_io_close (op[1]);
157   if (mp[0] != -1)
158     _gpgme_io_close (mp[0]);
159
160   if (err)
161     _gpgme_gpgsm_release (gpgsm);
162   else
163     *r_gpgsm = gpgsm;
164
165   return err;
166 }
167
168 void
169 _gpgme_gpgsm_release (GpgsmObject gpgsm)
170 {
171   pid_t pid;
172
173   if (!gpgsm)
174     return;
175
176   pid = assuan_get_pid (gpgsm->assuan_ctx);
177   if (pid != -1)
178     _gpgme_remove_proc_from_wait_queue (pid);
179
180   if (gpgsm->input_fd != -1)
181     _gpgme_io_close (gpgsm->input_fd);
182   if (gpgsm->output_fd != -1)
183     _gpgme_io_close (gpgsm->output_fd);
184   if (gpgsm->message_fd != -1)
185     _gpgme_io_close (gpgsm->message_fd);
186
187   assuan_pipe_disconnect (gpgsm->assuan_ctx);
188
189   xfree (gpgsm->command);
190   xfree (gpgsm);
191 }
192
193 static AssuanError
194 gpgsm_assuan_simple_command (ASSUAN_CONTEXT ctx, char *line)
195 {
196   AssuanError err;
197
198   err = _assuan_write_line (ctx, line);
199   if (err)
200     return err;
201
202   do
203     {
204       err = _assuan_read_line (ctx);
205       if (err)
206         return err;
207     }
208   while (*ctx->inbound.line == '#' || !ctx->inbound.linelen);
209   
210   if (ctx->inbound.linelen >= 2
211       && ctx->inbound.line[0] == 'O' && ctx->inbound.line[1] == 'K'
212       && (ctx->inbound.line[2] == '\0' || ctx->inbound.line[2] == ' '))
213     return 0;
214   else
215     return ASSUAN_General_Error;
216 }
217
218 #define COMMANDLINELEN 40
219 static AssuanError
220 gpgsm_set_fd (ASSUAN_CONTEXT ctx, const char *which, int fd)
221 {
222   char line[COMMANDLINELEN];
223
224   snprintf (line, COMMANDLINELEN, "%s FD=%i", which, fd);
225   return gpgsm_assuan_simple_command (ctx, line);
226 }
227
228 GpgmeError
229 _gpgme_gpgsm_op_decrypt (GpgsmObject gpgsm, GpgmeData ciph, GpgmeData plain)
230 {
231   AssuanError err;
232
233   if (!gpgsm)
234     return mk_error (Invalid_Value);
235
236   gpgsm->command = xtrystrdup ("DECRYPT");
237   if (!gpgsm->command)
238     return mk_error (Out_Of_Core);
239
240   gpgsm->input_data = ciph;
241   err = gpgsm_set_fd (gpgsm->assuan_ctx, "INPUT", gpgsm->input_fd_server);
242   if (err)
243     return mk_error (General_Error);    /* FIXME */
244   gpgsm->output_data = plain;
245   err = gpgsm_set_fd (gpgsm->assuan_ctx, "OUTPUT", gpgsm->output_fd_server);
246   if (err)
247     return mk_error (General_Error);    /* FIXME */
248   _gpgme_io_close (gpgsm->message_fd);
249   gpgsm->message_fd = -1;
250
251   return 0;
252 }
253
254 GpgmeError
255 _gpgme_gpgsm_op_delete (GpgsmObject gpgsm, GpgmeKey key, int allow_secret)
256 {
257   /* FIXME */
258   return mk_error (Not_Implemented);
259 }
260
261 static AssuanError
262 gpgsm_set_recipients (ASSUAN_CONTEXT ctx, GpgmeRecipients recp)
263 {
264   AssuanError err;
265   char *line;
266   int linelen;
267   struct user_id_s *r;
268
269   linelen = 10 + 40 + 1;        /* "RECIPIENT " + guess + '\0'.  */
270   line = xtrymalloc (10 + 40 + 1);
271   if (!line)
272     return ASSUAN_Out_Of_Core;
273   strcpy (line, "RECIPIENT ");
274   for (r = recp->list; r; r = r->next)
275     {
276       int newlen = 11 + strlen (r->name);
277       if (linelen < newlen)
278         {
279           char *newline = xtryrealloc (line, newlen);
280           if (! newline)
281             {
282               xfree (line);
283               return ASSUAN_Out_Of_Core;
284             }
285           line = newline;
286           linelen = newlen;
287         }
288       strcpy (&line[10], r->name);
289       
290       err = gpgsm_assuan_simple_command (ctx, line);
291       if (err)
292         {
293           xfree (line);
294           return err;
295         }
296     }
297   return 0;
298 }
299
300 GpgmeError
301 _gpgme_gpgsm_op_encrypt (GpgsmObject gpgsm, GpgmeRecipients recp,
302                          GpgmeData plain, GpgmeData ciph, int use_armor)
303 {
304   AssuanError err;
305
306   if (!gpgsm)
307     return mk_error (Invalid_Value);
308
309   gpgsm->command = xtrystrdup (use_armor ? "ENCRYPT armor" : "ENCRYPT");
310   if (!gpgsm->command)
311     return mk_error (Out_Of_Core);
312
313   gpgsm->input_data = plain;
314   err = gpgsm_set_fd (gpgsm->assuan_ctx, "INPUT", gpgsm->input_fd_server);
315   if (err)
316     return mk_error (General_Error);    /* FIXME */
317   gpgsm->output_data = ciph;
318   err = gpgsm_set_fd (gpgsm->assuan_ctx, "OUTPUT", gpgsm->output_fd_server);
319   if (err)
320     return mk_error (General_Error);    /* FIXME */
321   _gpgme_io_close (gpgsm->message_fd);
322   gpgsm->message_fd = -1;
323
324   err = gpgsm_set_recipients (gpgsm->assuan_ctx, recp);
325   if (err)
326     return mk_error (General_Error);
327
328   return 0;
329 }
330
331 GpgmeError
332 _gpgme_gpgsm_op_export (GpgsmObject gpgsm, GpgmeRecipients recp,
333                         GpgmeData keydata, int use_armor)
334 {
335   /* FIXME */
336   return mk_error (Not_Implemented);
337 }
338
339 GpgmeError
340 _gpgme_gpgsm_op_genkey (GpgsmObject gpgsm, GpgmeData help_data, int use_armor)
341 {
342   /* FIXME */
343   return mk_error (Not_Implemented);
344 }
345
346 GpgmeError
347 _gpgme_gpgsm_op_import (GpgsmObject gpgsm, GpgmeData keydata)
348 {
349   AssuanError err;
350
351   if (!gpgsm)
352     return mk_error (Invalid_Value);
353
354   gpgsm->command = xtrystrdup ("IMPORT");
355   if (!gpgsm->command)
356     return mk_error (Out_Of_Core);
357
358   gpgsm->input_data = keydata;
359   err = gpgsm_set_fd (gpgsm->assuan_ctx, "INPUT", gpgsm->input_fd_server);
360   if (err)
361     return mk_error (General_Error);    /* FIXME */
362   _gpgme_io_close (gpgsm->output_fd);
363   gpgsm->output_fd = -1;
364   _gpgme_io_close (gpgsm->message_fd);
365   gpgsm->message_fd = -1;
366
367   return 0;
368 }
369
370 GpgmeError
371 _gpgme_gpgsm_op_keylist (GpgsmObject gpgsm, const char *pattern,
372                          int secret_only, int keylist_mode)
373 {
374   char *line;
375
376   line = xtrymalloc (9 + strlen (pattern) + 1); /* "LISTKEYS " + p + '\0'.  */
377   if (!line)
378     return mk_error (Out_Of_Core);
379   strcpy (line, "LISTKEYS ");
380   strcpy (&line[9], pattern);
381
382   _gpgme_io_close (gpgsm->input_fd);
383   gpgsm->input_fd = -1;
384   _gpgme_io_close (gpgsm->output_fd);
385   gpgsm->output_fd = -1;
386   _gpgme_io_close (gpgsm->message_fd);
387   gpgsm->message_fd = -1;
388
389   gpgsm->command = line;
390   return 0;
391 }
392
393 GpgmeError
394 _gpgme_gpgsm_op_sign (GpgsmObject gpgsm, GpgmeData in, GpgmeData out,
395                       GpgmeSigMode mode, int use_armor,
396                       int use_textmode, GpgmeCtx ctx /* FIXME */)
397 {
398   AssuanError err;
399
400   if (!gpgsm)
401     return mk_error (Invalid_Value);
402
403   gpgsm->command = xtrystrdup (mode == GPGME_SIG_MODE_DETACH
404                                ? "VERIFY --detach" : "VERIFY");
405   if (!gpgsm->command)
406     return mk_error (Out_Of_Core);
407
408   gpgsm->input_data = in;
409   err = gpgsm_set_fd (gpgsm->assuan_ctx, "INPUT", gpgsm->input_fd_server);
410   if (err)
411     return mk_error (General_Error);    /* FIXME */
412   gpgsm->output_data = out;
413   err = gpgsm_set_fd (gpgsm->assuan_ctx, "OUTPUT", gpgsm->output_fd_server);
414   if (err)
415     return mk_error (General_Error);    /* FIXME */
416   _gpgme_io_close (gpgsm->message_fd);
417   gpgsm->message_fd = -1;
418
419   return 0;
420 }
421
422 GpgmeError
423 _gpgme_gpgsm_op_trustlist (GpgsmObject gpgsm, const char *pattern)
424 {
425   /* FIXME */
426   return mk_error (Not_Implemented);
427 }
428
429 GpgmeError
430 _gpgme_gpgsm_op_verify (GpgsmObject gpgsm, GpgmeData sig, GpgmeData text)
431 {
432   AssuanError err;
433
434   if (!gpgsm)
435     return mk_error (Invalid_Value);
436
437   gpgsm->command = xtrystrdup ("VERIFY");
438   if (!gpgsm->command)
439     return mk_error (Out_Of_Core);
440
441   gpgsm->input_data = sig;
442   err = gpgsm_set_fd (gpgsm->assuan_ctx, "INPUT", gpgsm->input_fd_server);
443   if (err)
444     return mk_error (General_Error);    /* FIXME */
445   gpgsm->message_data = sig;
446   err = gpgsm_set_fd (gpgsm->assuan_ctx, "MESSAGE", gpgsm->message_fd_server);
447   if (err)
448     return mk_error (General_Error);    /* FIXME */
449   _gpgme_io_close (gpgsm->output_fd);
450   gpgsm->output_fd = -1;
451
452   return 0;
453 }
454
455 static int
456 status_cmp (const void *ap, const void *bp)
457 {
458   const struct status_table_s *a = ap;
459   const struct status_table_s *b = bp;
460
461   return strcmp (a->name, b->name);
462 }
463
464 static int
465 gpgsm_status_handler (void *opaque, int pid, int fd)
466 {
467   int err;
468   GpgsmObject gpgsm = opaque;
469   ASSUAN_CONTEXT actx = gpgsm->assuan_ctx;
470
471   assert (fd == gpgsm->assuan_ctx->inbound.fd);
472
473   err = _assuan_read_line (gpgsm->assuan_ctx);
474
475   if (actx->inbound.line[0] == '#' || !actx->inbound.linelen)
476     return 0;  /* FIXME */
477
478   if ((actx->inbound.linelen >= 2
479        && actx->inbound.line[0] == 'O' && actx->inbound.line[1] == 'K'
480        && (actx->inbound.line[2] == '\0' || actx->inbound.line[2] == ' '))
481       || (actx->inbound.linelen >= 3
482           && actx->inbound.line[0] == 'E' && actx->inbound.line[1] == 'R'
483           && actx->inbound.line[2] == 'R'
484           && (actx->inbound.line[3] == '\0' || actx->inbound.line[3] == ' ')))
485     {
486       /* FIXME Save error somewhere.  */
487       if (gpgsm->status.fnc)
488         gpgsm->status.fnc (gpgsm->status.fnc_value, STATUS_EOF, "");
489       return 1;
490     }
491   /* FIXME: Parse the status and call the handler.  */
492
493   if (actx->inbound.linelen > 2
494       && actx->inbound.line[0] == 'S' && actx->inbound.line[1] == ' ')
495     {
496       struct status_table_s t, *r;
497       char *rest;
498
499       rest = strchr (actx->inbound.line + 2, ' ');
500       if (!rest)
501         rest = actx->inbound.line + actx->inbound.linelen; /* set to an empty string */
502       else
503         *rest++ = 0;
504
505       t.name = actx->inbound.line + 2;
506       r = bsearch (&t, status_table, DIM(status_table) - 1,
507                    sizeof t, status_cmp);
508
509       if (gpgsm->status.fnc)
510         gpgsm->status.fnc (gpgsm->status.fnc_value, r->code, rest);
511     }
512   else
513     fprintf (stderr, "[UNCAUGHT STATUS]%s", actx->inbound.line);
514   return 0;
515 }
516
517 void
518 _gpgme_gpgsm_set_status_handler (GpgsmObject gpgsm,
519                                  GpgStatusHandler fnc, void *fnc_value) 
520 {
521   assert (gpgsm);
522
523   gpgsm->status.fnc = fnc;
524   gpgsm->status.fnc_value = fnc_value;
525 }
526
527 GpgmeError
528 _gpgme_gpgsm_start (GpgsmObject gpgsm, void *opaque)
529 {
530   GpgmeError err = 0;
531   pid_t pid;
532
533   if (!gpgsm)
534     return mk_error (Invalid_Value);
535
536   pid = assuan_get_pid (gpgsm->assuan_ctx);
537
538   err = _gpgme_register_pipe_handler (opaque, gpgsm_status_handler, gpgsm, pid,
539                                       gpgsm->assuan_ctx->inbound.fd, 1);
540
541   if (gpgsm->input_fd != -1)
542     {
543       err = _gpgme_register_pipe_handler (opaque, _gpgme_data_outbound_handler,
544                                           gpgsm->input_data, pid,
545                                           gpgsm->input_fd, 0);
546       if (!err) /* FIXME Kludge around poll() problem.  */
547         err = _gpgme_io_set_nonblocking (gpgsm->input_fd);
548     }
549   if (!err && gpgsm->output_fd != -1)
550     err = _gpgme_register_pipe_handler (opaque, _gpgme_data_inbound_handler,
551                                         gpgsm->output_data, pid,
552                                         gpgsm->output_fd, 1);
553   if (!err && gpgsm->message_fd != -1)
554     {
555       err = _gpgme_register_pipe_handler (opaque, _gpgme_data_outbound_handler,
556                                           gpgsm->message_data, pid,
557                                           gpgsm->message_fd, 0);
558       if (!err) /* FIXME Kludge around poll() problem.  */
559         err = _gpgme_io_set_nonblocking (gpgsm->message_fd);
560     }
561
562   if (!err)
563     err = _assuan_write_line (gpgsm->assuan_ctx, gpgsm->command);
564
565   return err;
566 }
567
568 #else   /* ENABLE_GPGSM */
569
570 #include <stddef.h>
571 #include "util.h"
572
573 #include "engine-gpgsm.h"
574
575 const char *
576 _gpgme_gpgsm_get_version (void)
577 {
578   return NULL;
579 }
580
581 GpgmeError
582 _gpgme_gpgsm_check_version (void)
583 {
584   return mk_error (Invalid_Engine);
585 }
586
587 GpgmeError
588 _gpgme_gpgsm_new (GpgsmObject *r_gpgsm)
589 {
590   return mk_error (Invalid_Engine);
591 }
592
593 void
594 _gpgme_gpgsm_release (GpgsmObject gpgsm)
595 {
596   return;
597 }
598
599 void
600 _gpgme_gpgsm_set_status_handler (GpgsmObject gpgsm,
601                                  GpgStatusHandler fnc, void *fnc_value) 
602 {
603   return;
604 }
605
606 GpgmeError
607 _gpgme_gpgsm_op_decrypt (GpgsmObject gpgsm, GpgmeData ciph, GpgmeData plain)
608 {
609   return mk_error (Invalid_Engine);
610 }
611
612 GpgmeError
613 _gpgme_gpgsm_op_delete (GpgsmObject gpgsm, GpgmeKey key, int allow_secret)
614 {
615   return mk_error (Invalid_Engine);
616 }
617
618 GpgmeError
619 _gpgme_gpgsm_op_encrypt (GpgsmObject gpgsm, GpgmeRecipients recp,
620                          GpgmeData plain, GpgmeData ciph, int use_armor)
621 {
622   return mk_error (Invalid_Engine);
623 }
624
625 GpgmeError
626 _gpgme_gpgsm_op_export (GpgsmObject gpgsm, GpgmeRecipients recp,
627                         GpgmeData keydata, int use_armor)
628 {
629   return mk_error (Invalid_Engine);
630 }
631
632 GpgmeError
633 _gpgme_gpgsm_op_genkey (GpgsmObject gpgsm, GpgmeData help_data, int use_armor)
634 {
635   return mk_error (Invalid_Engine);
636 }
637   
638 GpgmeError
639 _gpgme_gpgsm_op_import (GpgsmObject gpgsm, GpgmeData keydata)
640 {
641   return mk_error (Invalid_Engine);
642 }
643
644 GpgmeError
645 _gpgme_gpgsm_op_keylist (GpgsmObject gpgsm, const char *pattern,
646                          int secret_only, int keylist_mode)
647 {
648   return mk_error (Invalid_Engine);
649 }
650
651 GpgmeError
652 _gpgme_gpgsm_op_sign (GpgsmObject gpgsm, GpgmeData in, GpgmeData out,
653                       GpgmeSigMode mode, int use_armor,
654                       int use_textmode, GpgmeCtx ctx /* FIXME */)
655 {
656   return mk_error (Invalid_Engine);
657 }
658
659 GpgmeError
660 _gpgme_gpgsm_op_trustlist (GpgsmObject gpgsm, const char *pattern)
661 {
662   return mk_error (Invalid_Engine);
663 }
664
665 GpgmeError
666 _gpgme_gpgsm_op_verify (GpgsmObject gpgsm, GpgmeData sig, GpgmeData text)
667 {
668   return mk_error (Invalid_Engine);
669 }
670
671 GpgmeError
672 _gpgme_gpgsm_start (GpgsmObject gpgsm, void *opaque)
673 {
674   return mk_error (Invalid_Engine);
675 }
676
677 #endif  /* ! ENABLE_GPGSM */