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   /* FIXME */
399   return mk_error (Not_Implemented);
400 }
401
402 GpgmeError
403 _gpgme_gpgsm_op_trustlist (GpgsmObject gpgsm, const char *pattern)
404 {
405   /* FIXME */
406   return mk_error (Not_Implemented);
407 }
408
409 GpgmeError
410 _gpgme_gpgsm_op_verify (GpgsmObject gpgsm, GpgmeData sig, GpgmeData text)
411 {
412   AssuanError err;
413
414   if (!gpgsm)
415     return mk_error (Invalid_Value);
416
417   gpgsm->command = xtrystrdup ("VERIFY");
418   if (!gpgsm->command)
419     return mk_error (Out_Of_Core);
420
421   gpgsm->input_data = sig;
422   err = gpgsm_set_fd (gpgsm->assuan_ctx, "INPUT", gpgsm->input_fd_server);
423   if (err)
424     return mk_error (General_Error);    /* FIXME */
425   gpgsm->message_data = sig;
426   err = gpgsm_set_fd (gpgsm->assuan_ctx, "MESSAGE", gpgsm->message_fd_server);
427   if (err)
428     return mk_error (General_Error);    /* FIXME */
429   _gpgme_io_close (gpgsm->output_fd);
430   gpgsm->output_fd = -1;
431
432   return 0;
433 }
434
435 static int
436 status_cmp (const void *ap, const void *bp)
437 {
438   const struct status_table_s *a = ap;
439   const struct status_table_s *b = bp;
440
441   return strcmp (a->name, b->name);
442 }
443
444 static int
445 gpgsm_status_handler (void *opaque, int pid, int fd)
446 {
447   int err;
448   GpgsmObject gpgsm = opaque;
449   ASSUAN_CONTEXT actx = gpgsm->assuan_ctx;
450
451   assert (fd == gpgsm->assuan_ctx->inbound.fd);
452
453   err = _assuan_read_line (gpgsm->assuan_ctx);
454
455   if (actx->inbound.line[0] == '#' || !actx->inbound.linelen)
456     return 0;  /* FIXME */
457
458   if ((actx->inbound.linelen >= 2
459        && actx->inbound.line[0] == 'O' && actx->inbound.line[1] == 'K'
460        && (actx->inbound.line[2] == '\0' || actx->inbound.line[2] == ' '))
461       || (actx->inbound.linelen >= 3
462           && actx->inbound.line[0] == 'E' && actx->inbound.line[1] == 'R'
463           && actx->inbound.line[2] == 'R'
464           && (actx->inbound.line[3] == '\0' || actx->inbound.line[3] == ' ')))
465     {
466       /* FIXME Save error somewhere.  */
467       if (gpgsm->status.fnc)
468         gpgsm->status.fnc (gpgsm->status.fnc_value, STATUS_EOF, "");
469       return 1;
470     }
471   /* FIXME: Parse the status and call the handler.  */
472
473   if (actx->inbound.linelen > 2
474       && actx->inbound.line[0] == 'S' && actx->inbound.line[1] == ' ')
475     {
476       struct status_table_s t, *r;
477       char *rest;
478
479       rest = strchr (actx->inbound.line + 2, ' ');
480       if (!rest)
481         rest = actx->inbound.line + actx->inbound.linelen; /* set to an empty string */
482       else
483         *rest++ = 0;
484
485       t.name = actx->inbound.line + 2;
486       r = bsearch (&t, status_table, DIM(status_table) - 1,
487                    sizeof t, status_cmp);
488
489       if (gpgsm->status.fnc)
490         gpgsm->status.fnc (gpgsm->status.fnc_value, r->code, rest);
491     }
492   else
493     fprintf (stderr, "[UNCAUGHT STATUS]%s", actx->inbound.line);
494   return 0;
495 }
496
497 void
498 _gpgme_gpgsm_set_status_handler (GpgsmObject gpgsm,
499                                  GpgStatusHandler fnc, void *fnc_value) 
500 {
501   assert (gpgsm);
502
503   gpgsm->status.fnc = fnc;
504   gpgsm->status.fnc_value = fnc_value;
505 }
506
507 GpgmeError
508 _gpgme_gpgsm_start (GpgsmObject gpgsm, void *opaque)
509 {
510   GpgmeError err = 0;
511   pid_t pid;
512
513   if (!gpgsm)
514     return mk_error (Invalid_Value);
515
516   pid = assuan_get_pid (gpgsm->assuan_ctx);
517
518   err = _gpgme_register_pipe_handler (opaque, gpgsm_status_handler, gpgsm, pid,
519                                       gpgsm->assuan_ctx->inbound.fd, 1);
520
521   if (gpgsm->input_fd != -1)
522     {
523       err = _gpgme_register_pipe_handler (opaque, _gpgme_data_outbound_handler,
524                                           gpgsm->input_data, pid,
525                                           gpgsm->input_fd, 0);
526       if (!err) /* FIXME Kludge around poll() problem.  */
527         err = _gpgme_io_set_nonblocking (gpgsm->input_fd);
528     }
529   if (!err && gpgsm->output_fd != -1)
530     err = _gpgme_register_pipe_handler (opaque, _gpgme_data_inbound_handler,
531                                         gpgsm->output_data, pid,
532                                         gpgsm->output_fd, 1);
533   if (!err && gpgsm->message_fd != -1)
534     {
535       err = _gpgme_register_pipe_handler (opaque, _gpgme_data_outbound_handler,
536                                           gpgsm->message_data, pid,
537                                           gpgsm->message_fd, 0);
538       if (!err) /* FIXME Kludge around poll() problem.  */
539         err = _gpgme_io_set_nonblocking (gpgsm->message_fd);
540     }
541
542   if (!err)
543     err = _assuan_write_line (gpgsm->assuan_ctx, gpgsm->command);
544
545   return err;
546 }
547
548 #else   /* ENABLE_GPGSM */
549
550 #include <stddef.h>
551 #include "util.h"
552
553 #include "engine-gpgsm.h"
554
555 const char *
556 _gpgme_gpgsm_get_version (void)
557 {
558   return NULL;
559 }
560
561 GpgmeError
562 _gpgme_gpgsm_check_version (void)
563 {
564   return mk_error (Invalid_Engine);
565 }
566
567 GpgmeError
568 _gpgme_gpgsm_new (GpgsmObject *r_gpgsm)
569 {
570   return mk_error (Invalid_Engine);
571 }
572
573 void
574 _gpgme_gpgsm_release (GpgsmObject gpgsm)
575 {
576   return;
577 }
578
579 void
580 _gpgme_gpgsm_set_status_handler (GpgsmObject gpgsm,
581                                  GpgStatusHandler fnc, void *fnc_value) 
582 {
583   return;
584 }
585
586 GpgmeError
587 _gpgme_gpgsm_op_decrypt (GpgsmObject gpgsm, GpgmeData ciph, GpgmeData plain)
588 {
589   return mk_error (Invalid_Engine);
590 }
591
592 GpgmeError
593 _gpgme_gpgsm_op_delete (GpgsmObject gpgsm, GpgmeKey key, int allow_secret)
594 {
595   return mk_error (Invalid_Engine);
596 }
597
598 GpgmeError
599 _gpgme_gpgsm_op_encrypt (GpgsmObject gpgsm, GpgmeRecipients recp,
600                          GpgmeData plain, GpgmeData ciph, int use_armor)
601 {
602   return mk_error (Invalid_Engine);
603 }
604
605 GpgmeError
606 _gpgme_gpgsm_op_export (GpgsmObject gpgsm, GpgmeRecipients recp,
607                         GpgmeData keydata, int use_armor)
608 {
609   return mk_error (Invalid_Engine);
610 }
611
612 GpgmeError
613 _gpgme_gpgsm_op_genkey (GpgsmObject gpgsm, GpgmeData help_data, int use_armor)
614 {
615   return mk_error (Invalid_Engine);
616 }
617   
618 GpgmeError
619 _gpgme_gpgsm_op_import (GpgsmObject gpgsm, GpgmeData keydata)
620 {
621   return mk_error (Invalid_Engine);
622 }
623
624 GpgmeError
625 _gpgme_gpgsm_op_keylist (GpgsmObject gpgsm, const char *pattern,
626                          int secret_only, int keylist_mode)
627 {
628   return mk_error (Invalid_Engine);
629 }
630
631 GpgmeError
632 _gpgme_gpgsm_op_sign (GpgsmObject gpgsm, GpgmeData in, GpgmeData out,
633                       GpgmeSigMode mode, int use_armor,
634                       int use_textmode, GpgmeCtx ctx /* FIXME */)
635 {
636   return mk_error (Invalid_Engine);
637 }
638
639 GpgmeError
640 _gpgme_gpgsm_op_trustlist (GpgsmObject gpgsm, const char *pattern)
641 {
642   return mk_error (Invalid_Engine);
643 }
644
645 GpgmeError
646 _gpgme_gpgsm_op_verify (GpgsmObject gpgsm, GpgmeData sig, GpgmeData text)
647 {
648   return mk_error (Invalid_Engine);
649 }
650
651 GpgmeError
652 _gpgme_gpgsm_start (GpgsmObject gpgsm, void *opaque)
653 {
654   return mk_error (Invalid_Engine);
655 }
656
657 #endif  /* ! ENABLE_GPGSM */