Changed to GPLv3.
[gnupg.git] / sm / server.c
1 /* server.c - Server mode and main entry point 
2  * Copyright (C) 2001, 2002, 2003, 2004 Free Software Foundation, Inc.
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 <errno.h>
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <stdarg.h>
26 #include <ctype.h>
27 #include <unistd.h>
28
29 #include <assuan.h>
30
31 #include "gpgsm.h"
32
33 #define set_error(e,t) assuan_set_error (ctx, gpg_error (e), (t))
34
35
36 /* The filepointer for status message used in non-server mode */
37 static FILE *statusfp;
38
39 /* Data used to assuciate an Assuan context with local server data */
40 struct server_local_s {
41   assuan_context_t assuan_ctx;
42   int message_fd;
43   int list_internal;
44   int list_external;
45   int list_to_output;           /* Write keylistings to the output fd. */
46   certlist_t recplist;
47   certlist_t signerlist;
48   certlist_t default_recplist; /* As set by main() - don't release. */
49 };
50
51
52 /* Cookie definition for assuan data line output.  */
53 static ssize_t data_line_cookie_write (void *cookie,
54                                        const void *buffer, size_t size);
55 static int data_line_cookie_close (void *cookie);
56 static es_cookie_io_functions_t data_line_cookie_functions =
57   {
58     NULL,
59     data_line_cookie_write,
60     NULL,
61     data_line_cookie_close
62   };
63
64
65
66 \f
67 /* Note that it is sufficient to allocate the target string D as
68    long as the source string S, i.e.: strlen(s)+1; */
69 static void
70 strcpy_escaped_plus (char *d, const char *s)
71 {
72   while (*s)
73     {
74       if (*s == '%' && s[1] && s[2])
75         { 
76           s++;
77           *d++ = xtoi_2 (s);
78           s += 2;
79         }
80       else if (*s == '+')
81         *d++ = ' ', s++;
82       else
83         *d++ = *s++;
84     }
85   *d = 0; 
86 }
87
88
89 /* Skip over options.  
90    Blanks after the options are also removed. */
91 static char *
92 skip_options (const char *line)
93 {
94   while (spacep (line))
95     line++;
96   while ( *line == '-' && line[1] == '-' )
97     {
98       while (*line && !spacep (line))
99         line++;
100       while (spacep (line))
101         line++;
102     }
103   return (char*)line;
104 }
105
106
107 /* Check whether the option NAME appears in LINE */
108 static int
109 has_option (const char *line, const char *name)
110 {
111   const char *s;
112   int n = strlen (name);
113
114   s = strstr (line, name);
115   if (s && s >= skip_options (line))
116     return 0;
117   return (s && (s == line || spacep (s-1)) && (!s[n] || spacep (s+n)));
118 }
119
120
121 /* A write handler used by es_fopencookie to write assuan data
122    lines.  */
123 static ssize_t
124 data_line_cookie_write (void *cookie, const void *buffer, size_t size)
125 {
126   assuan_context_t ctx = cookie;
127
128   if (assuan_send_data (ctx, buffer, size))
129     {
130       errno = EIO;
131       return -1;
132     }
133
134   return size;
135 }
136
137 static int
138 data_line_cookie_close (void *cookie)
139 {
140   assuan_context_t ctx = cookie;
141
142   if (assuan_send_data (ctx, NULL, 0))
143     {
144       errno = EIO;
145       return -1;
146     }
147
148   return 0;
149 }
150
151
152 static void 
153 close_message_fd (ctrl_t ctrl)
154 {
155   if (ctrl->server_local->message_fd != -1)
156     {
157       close (ctrl->server_local->message_fd);
158       ctrl->server_local->message_fd = -1;
159     }
160 }
161
162
163 static int
164 option_handler (assuan_context_t ctx, const char *key, const char *value)
165 {
166   ctrl_t ctrl = assuan_get_pointer (ctx);
167
168   if (!strcmp (key, "include-certs"))
169     {
170       int i = *value? atoi (value) : -1;
171       if (ctrl->include_certs < -2)
172         return gpg_error (GPG_ERR_ASS_PARAMETER);
173       ctrl->include_certs = i;
174     }
175   else if (!strcmp (key, "display"))
176     {
177       if (opt.display)
178         free (opt.display);
179       opt.display = strdup (value);
180       if (!opt.display)
181         return out_of_core ();
182     }
183   else if (!strcmp (key, "ttyname"))
184     {
185       if (opt.ttyname)
186         free (opt.ttyname);
187       opt.ttyname = strdup (value);
188       if (!opt.ttyname)
189         return out_of_core ();
190     }
191   else if (!strcmp (key, "ttytype"))
192     {
193       if (opt.ttytype)
194         free (opt.ttytype);
195       opt.ttytype = strdup (value);
196       if (!opt.ttytype)
197         return out_of_core ();
198     }
199   else if (!strcmp (key, "lc-ctype"))
200     {
201       if (opt.lc_ctype)
202         free (opt.lc_ctype);
203       opt.lc_ctype = strdup (value);
204       if (!opt.lc_ctype)
205         return out_of_core ();
206     }
207   else if (!strcmp (key, "lc-messages"))
208     {
209       if (opt.lc_messages)
210         free (opt.lc_messages);
211       opt.lc_messages = strdup (value);
212       if (!opt.lc_messages)
213         return out_of_core ();
214     }
215   else if (!strcmp (key, "list-mode"))
216     {
217       int i = *value? atoi (value) : 0;
218       if (!i || i == 1) /* default and mode 1 */
219         {
220           ctrl->server_local->list_internal = 1;
221           ctrl->server_local->list_external = 0;
222         }
223       else if (i == 2)
224         {
225           ctrl->server_local->list_internal = 0;
226           ctrl->server_local->list_external = 1;
227         }
228       else if (i == 3)
229         {
230           ctrl->server_local->list_internal = 1;
231           ctrl->server_local->list_external = 1;
232         }
233       else
234         return gpg_error (GPG_ERR_ASS_PARAMETER);
235     }
236   else if (!strcmp (key, "list-to-output"))
237     {
238       int i = *value? atoi (value) : 0;
239       ctrl->server_local->list_to_output = i;
240     }
241   else if (!strcmp (key, "with-validation"))
242     {
243       int i = *value? atoi (value) : 0;
244       ctrl->with_validation = i;
245     }
246   else if (!strcmp (key, "with-key-data"))
247     {
248       opt.with_key_data = 1;
249     }
250   else
251     return gpg_error (GPG_ERR_UNKNOWN_OPTION);
252
253   return 0;
254 }
255
256
257
258
259 static void
260 reset_notify (assuan_context_t ctx)
261 {
262   ctrl_t ctrl = assuan_get_pointer (ctx);
263
264   gpgsm_release_certlist (ctrl->server_local->recplist);
265   gpgsm_release_certlist (ctrl->server_local->signerlist);
266   ctrl->server_local->recplist = NULL;
267   ctrl->server_local->signerlist = NULL;
268   close_message_fd (ctrl);
269   assuan_close_input_fd (ctx);
270   assuan_close_output_fd (ctx);
271 }
272
273
274 static void
275 input_notify (assuan_context_t ctx, const char *line)
276 {
277   ctrl_t ctrl = assuan_get_pointer (ctx);
278
279   ctrl->autodetect_encoding = 0;
280   ctrl->is_pem = 0;
281   ctrl->is_base64 = 0;
282   if (strstr (line, "--armor"))
283     ctrl->is_pem = 1;  
284   else if (strstr (line, "--base64"))
285     ctrl->is_base64 = 1; 
286   else if (strstr (line, "--binary"))
287     ;
288   else
289     ctrl->autodetect_encoding = 1;
290 }
291
292 static void
293 output_notify (assuan_context_t ctx, const char *line)
294 {
295   ctrl_t ctrl = assuan_get_pointer (ctx);
296
297   ctrl->create_pem = 0;
298   ctrl->create_base64 = 0;
299   if (strstr (line, "--armor"))
300     ctrl->create_pem = 1;  
301   else if (strstr (line, "--base64"))
302     ctrl->create_base64 = 1; /* just the raw output */
303 }
304
305
306
307 /*  RECIPIENT <userID>
308
309   Set the recipient for the encryption.  <userID> should be the
310   internal representation of the key; the server may accept any other
311   way of specification [we will support this].  If this is a valid and
312   trusted recipient the server does respond with OK, otherwise the
313   return is an ERR with the reason why the recipient can't be used,
314   the encryption will then not be done for this recipient.  If the
315   policy is not to encrypt at all if not all recipients are valid, the
316   client has to take care of this.  All RECIPIENT commands are
317   cumulative until a RESET or an successful ENCRYPT command.  */
318 static int 
319 cmd_recipient (assuan_context_t ctx, char *line)
320 {
321   ctrl_t ctrl = assuan_get_pointer (ctx);
322   int rc;
323
324   rc = gpgsm_add_to_certlist (ctrl, line, 0, &ctrl->server_local->recplist, 0);
325   if (rc)
326     {
327       gpg_err_code_t r = gpg_err_code (rc);
328       gpgsm_status2 (ctrl, STATUS_INV_RECP,
329                    r == -1? "1":
330                    r == GPG_ERR_NO_PUBKEY?       "1":
331                    r == GPG_ERR_AMBIGUOUS_NAME?  "2":
332                    r == GPG_ERR_WRONG_KEY_USAGE? "3":
333                    r == GPG_ERR_CERT_REVOKED?    "4":
334                    r == GPG_ERR_CERT_EXPIRED?    "5":
335                    r == GPG_ERR_NO_CRL_KNOWN?    "6":
336                    r == GPG_ERR_CRL_TOO_OLD?     "7":
337                    r == GPG_ERR_NO_POLICY_MATCH? "8":
338                    "0",
339                    line, NULL);
340     }
341
342   return rc;
343 }
344
345 /*  SIGNER <userID>
346
347   Set the signer's keys for the signature creation.  <userID> should
348   be the internal representation of the key; the server may accept any
349   other way of specification [we will support this].  If this is a
350   valid and usable signing key the server does respond with OK,
351   otherwise it returns an ERR with the reason why the key can't be
352   used, the signing will then not be done for this key.  If the policy
353   is not to sign at all if not all signer keys are valid, the client
354   has to take care of this.  All SIGNER commands are cumulative until
355   a RESET but they are *not* reset by an SIGN command becuase it can
356   be expected that set of signers are used for more than one sign
357   operation.  
358
359   Note that this command returns an INV_RECP status which is a bit
360   strange, but they are very similar.  */
361 static int 
362 cmd_signer (assuan_context_t ctx, char *line)
363 {
364   ctrl_t ctrl = assuan_get_pointer (ctx);
365   int rc;
366
367   rc = gpgsm_add_to_certlist (ctrl, line, 1,
368                               &ctrl->server_local->signerlist, 0);
369   if (rc)
370     {
371       gpg_err_code_t r = gpg_err_code (rc);
372       gpgsm_status2 (ctrl, STATUS_INV_RECP,
373                    r == -1?                          "1":
374                    r == GPG_ERR_NO_PUBKEY?           "1":
375                    r == GPG_ERR_AMBIGUOUS_NAME?      "2":
376                    r == GPG_ERR_WRONG_KEY_USAGE?     "3":
377                    r == GPG_ERR_CERT_REVOKED?        "4":
378                    r == GPG_ERR_CERT_EXPIRED?        "5":
379                    r == GPG_ERR_NO_CRL_KNOWN?        "6":
380                    r == GPG_ERR_CRL_TOO_OLD?         "7":
381                    r == GPG_ERR_NO_POLICY_MATCH?     "8":
382                    r == GPG_ERR_NO_SECKEY?           "9":
383                    "0",
384                   line, NULL);
385     }
386   return rc;
387 }
388
389
390 /* ENCRYPT 
391
392   Do the actual encryption process. Takes the plaintext from the INPUT
393   command, writes to the ciphertext to the file descriptor set with
394   the OUTPUT command, take the recipients form all the recipients set
395   so far.  If this command fails the clients should try to delete all
396   output currently done or otherwise mark it as invalid.  GPGSM does
397   ensure that there won't be any security problem with leftover data
398   on the output in this case.
399
400   This command should in general not fail, as all necessary checks
401   have been done while setting the recipients.  The input and output
402   pipes are closed. */
403 static int 
404 cmd_encrypt (assuan_context_t ctx, char *line)
405 {
406   ctrl_t ctrl = assuan_get_pointer (ctx);
407   certlist_t cl;
408   int inp_fd, out_fd;
409   FILE *out_fp;
410   int rc;
411
412   inp_fd = assuan_get_input_fd (ctx);
413   if (inp_fd == -1)
414     return set_error (GPG_ERR_ASS_NO_INPUT, NULL);
415   out_fd = assuan_get_output_fd (ctx);
416   if (out_fd == -1)
417     return set_error (GPG_ERR_ASS_NO_OUTPUT, NULL);
418
419   out_fp = fdopen ( dup(out_fd), "w");
420   if (!out_fp)
421     return set_error (GPG_ERR_ASS_GENERAL, "fdopen() failed");
422   
423   /* Now add all encrypt-to marked recipients from the default
424      list. */
425   rc = 0;
426   if (!opt.no_encrypt_to)
427     {
428       for (cl=ctrl->server_local->default_recplist; !rc && cl; cl = cl->next)
429         if (cl->is_encrypt_to)
430           rc = gpgsm_add_cert_to_certlist (ctrl, cl->cert,
431                                            &ctrl->server_local->recplist, 1);
432     }
433   if (!rc)
434     rc = gpgsm_encrypt (assuan_get_pointer (ctx),
435                         ctrl->server_local->recplist,
436                         inp_fd, out_fp);
437   fclose (out_fp);
438
439   gpgsm_release_certlist (ctrl->server_local->recplist);
440   ctrl->server_local->recplist = NULL;
441   /* Close and reset the fd */
442   close_message_fd (ctrl);
443   assuan_close_input_fd (ctx);
444   assuan_close_output_fd (ctx);
445   return rc;
446 }
447
448 /* DECRYPT
449
450   This performs the decrypt operation after doing some check on the
451   internal state. (e.g. that only needed data has been set).  Because
452   it utilizes the GPG-Agent for the session key decryption, there is
453   no need to ask the client for a protecting passphrase - GpgAgent
454   does take care of this by requesting this from the user. */
455 static int 
456 cmd_decrypt (assuan_context_t ctx, char *line)
457 {
458   ctrl_t ctrl = assuan_get_pointer (ctx);
459   int inp_fd, out_fd;
460   FILE *out_fp;
461   int rc;
462
463   inp_fd = assuan_get_input_fd (ctx);
464   if (inp_fd == -1)
465     return set_error (GPG_ERR_ASS_NO_INPUT, NULL);
466   out_fd = assuan_get_output_fd (ctx);
467   if (out_fd == -1)
468     return set_error (GPG_ERR_ASS_NO_OUTPUT, NULL);
469
470   out_fp = fdopen ( dup(out_fd), "w");
471   if (!out_fp)
472     return set_error (GPG_ERR_ASS_GENERAL, "fdopen() failed");
473   rc = gpgsm_decrypt (ctrl, inp_fd, out_fp); 
474   fclose (out_fp);
475
476   /* close and reset the fd */
477   close_message_fd (ctrl);
478   assuan_close_input_fd (ctx);
479   assuan_close_output_fd (ctx);
480
481   return rc;
482 }
483
484
485 /* VERIFY
486
487   This does a verify operation on the message send to the input-FD.
488   The result is written out using status lines.  If an output FD was
489   given, the signed text will be written to that.
490   
491   If the signature is a detached one, the server will inquire about
492   the signed material and the client must provide it.
493   */
494 static int 
495 cmd_verify (assuan_context_t ctx, char *line)
496 {
497   int rc;
498   ctrl_t ctrl = assuan_get_pointer (ctx);
499   int fd = assuan_get_input_fd (ctx);
500   int out_fd = assuan_get_output_fd (ctx);
501   FILE *out_fp = NULL;
502
503   if (fd == -1)
504     return set_error (GPG_ERR_ASS_NO_INPUT, NULL);
505
506   if (out_fd != -1)
507     {
508       out_fp = fdopen ( dup(out_fd), "w");
509       if (!out_fp)
510         return set_error (GPG_ERR_ASS_GENERAL, "fdopen() failed");
511     }
512
513   rc = gpgsm_verify (assuan_get_pointer (ctx), fd,
514                      ctrl->server_local->message_fd, out_fp);
515   if (out_fp)
516     fclose (out_fp);
517
518   /* close and reset the fd */
519   close_message_fd (ctrl);
520   assuan_close_input_fd (ctx);
521   assuan_close_output_fd (ctx);
522
523   return rc;
524 }
525
526
527 /* SIGN [--detached]
528
529   Sign the data set with the INPUT command and write it to the sink
530   set by OUTPUT.  With "--detached" specified, a detached signature is
531   created (surprise).  */
532 static int 
533 cmd_sign (assuan_context_t ctx, char *line)
534 {
535   ctrl_t ctrl = assuan_get_pointer (ctx);
536   int inp_fd, out_fd;
537   FILE *out_fp;
538   int detached;
539   int rc;
540
541   inp_fd = assuan_get_input_fd (ctx);
542   if (inp_fd == -1)
543     return set_error (GPG_ERR_ASS_NO_INPUT, NULL);
544   out_fd = assuan_get_output_fd (ctx);
545   if (out_fd == -1)
546     return set_error (GPG_ERR_ASS_NO_OUTPUT, NULL);
547
548   detached = has_option (line, "--detached"); 
549
550   out_fp = fdopen ( dup(out_fd), "w");
551   if (!out_fp)
552     return set_error (GPG_ERR_ASS_GENERAL, "fdopen() failed");
553
554   rc = gpgsm_sign (assuan_get_pointer (ctx), ctrl->server_local->signerlist,
555                    inp_fd, detached, out_fp);
556   fclose (out_fp);
557
558   /* close and reset the fd */
559   close_message_fd (ctrl);
560   assuan_close_input_fd (ctx);
561   assuan_close_output_fd (ctx);
562
563   return rc;
564 }
565
566
567 /* IMPORT
568
569   Import the certificates read form the input-fd, return status
570   message for each imported one.  The import checks the validity of
571   the certificate but not of the entire chain.  It is possible to
572   import expired certificates.  */
573 static int 
574 cmd_import (assuan_context_t ctx, char *line)
575 {
576   ctrl_t ctrl = assuan_get_pointer (ctx);
577   int rc;
578   int fd = assuan_get_input_fd (ctx);
579
580   if (fd == -1)
581     return set_error (GPG_ERR_ASS_NO_INPUT, NULL);
582
583   rc = gpgsm_import (assuan_get_pointer (ctx), fd);
584
585   /* close and reset the fd */
586   close_message_fd (ctrl);
587   assuan_close_input_fd (ctx);
588   assuan_close_output_fd (ctx);
589
590   return rc;
591 }
592
593
594 /* EXPORT [--data [--armor|--base64]] [--] pattern
595
596  */
597
598 static int 
599 cmd_export (assuan_context_t ctx, char *line)
600 {
601   ctrl_t ctrl = assuan_get_pointer (ctx);
602   char *p;
603   strlist_t list, sl;
604   int use_data;
605   
606   use_data = has_option (line, "--data");
607
608   if (use_data)
609     {
610       /* We need to override any possible setting done by an OUTPUT command. */
611       ctrl->create_pem = has_option (line, "--armor");
612       ctrl->create_base64 = has_option (line, "--base64");
613     }
614
615   line = skip_options (line);
616
617   /* Break the line down into an strlist_t. */
618   list = NULL;
619   for (p=line; *p; line = p)
620     {
621       while (*p && *p != ' ')
622         p++;
623       if (*p)
624         *p++ = 0;
625       if (*line)
626         {
627           sl = xtrymalloc (sizeof *sl + strlen (line));
628           if (!sl)
629             {
630               free_strlist (list);
631               return out_of_core ();
632             }
633           sl->flags = 0;
634           strcpy_escaped_plus (sl->d, line);
635           sl->next = list;
636           list = sl;
637         }
638     }
639
640   if (use_data)
641     {
642       estream_t stream;
643
644       stream = es_fopencookie (ctx, "w", data_line_cookie_functions);
645       if (!stream)
646         {
647           free_strlist (list);
648           return set_error (GPG_ERR_ASS_GENERAL, 
649                             "error setting up a data stream");
650         }
651       gpgsm_export (ctrl, list, NULL, stream);
652       es_fclose (stream);
653     }
654   else
655     {
656       int fd = assuan_get_output_fd (ctx);
657       FILE *out_fp;
658
659       if (fd == -1)
660         {
661           free_strlist (list);
662           return set_error (GPG_ERR_ASS_NO_OUTPUT, NULL);
663         }
664       out_fp = fdopen ( dup(fd), "w");
665       if (!out_fp)
666         {
667           free_strlist (list);
668           return set_error (GPG_ERR_ASS_GENERAL, "fdopen() failed");
669         }
670       
671       gpgsm_export (ctrl, list, out_fp, NULL);
672       fclose (out_fp);
673     }
674
675   free_strlist (list);
676   /* Close and reset the fds. */
677   close_message_fd (ctrl);
678   assuan_close_input_fd (ctx);
679   assuan_close_output_fd (ctx);
680   return 0;
681 }
682
683
684 static int 
685 cmd_delkeys (assuan_context_t ctx, char *line)
686 {
687   ctrl_t ctrl = assuan_get_pointer (ctx);
688   char *p;
689   strlist_t list, sl;
690   int rc;
691
692   /* break the line down into an strlist_t */
693   list = NULL;
694   for (p=line; *p; line = p)
695     {
696       while (*p && *p != ' ')
697         p++;
698       if (*p)
699         *p++ = 0;
700       if (*line)
701         {
702           sl = xtrymalloc (sizeof *sl + strlen (line));
703           if (!sl)
704             {
705               free_strlist (list);
706               return out_of_core ();
707             }
708           sl->flags = 0;
709           strcpy_escaped_plus (sl->d, line);
710           sl->next = list;
711           list = sl;
712         }
713     }
714
715   rc = gpgsm_delete (ctrl, list);
716   free_strlist (list);
717
718   /* close and reset the fd */
719   close_message_fd (ctrl);
720   assuan_close_input_fd (ctx);
721   assuan_close_output_fd (ctx);
722
723   return rc;
724 }
725
726
727
728 /* MESSAGE FD=<n>
729
730    Set the file descriptor to read a message which is used with
731    detached signatures */
732 static int 
733 cmd_message (assuan_context_t ctx, char *line)
734 {
735   int rc;
736   int fd;
737   ctrl_t ctrl = assuan_get_pointer (ctx);
738
739   rc = assuan_command_parse_fd (ctx, line, &fd);
740   if (rc)
741     return rc;
742   if (fd == -1)
743     return set_error (GPG_ERR_ASS_NO_INPUT, NULL);
744   ctrl->server_local->message_fd = fd;
745   return 0;
746 }
747
748 /* LISTKEYS [<patterns>]
749    DUMPKEYS [<patterns>]
750    LISTSECRETKEYS [<patterns>]
751    DUMPSECRETKEYS [<patterns>]
752 */
753 static int 
754 do_listkeys (assuan_context_t ctx, char *line, int mode)
755 {
756   ctrl_t ctrl = assuan_get_pointer (ctx);
757   estream_t fp;
758   char *p;
759   strlist_t list, sl;
760   unsigned int listmode;
761   gpg_error_t err;
762
763   /* Break the line down into an strlist. */
764   list = NULL;
765   for (p=line; *p; line = p)
766     {
767       while (*p && *p != ' ')
768         p++;
769       if (*p)
770         *p++ = 0;
771       if (*line)
772         {
773           sl = xtrymalloc (sizeof *sl + strlen (line));
774           if (!sl)
775             {
776               free_strlist (list);
777               return out_of_core ();
778             }
779           sl->flags = 0;
780           strcpy_escaped_plus (sl->d, line);
781           sl->next = list;
782           list = sl;
783         }
784     }
785
786   if (ctrl->server_local->list_to_output)
787     {
788       int outfd = assuan_get_output_fd (ctx);
789
790       if ( outfd == -1 )
791         return set_error (GPG_ERR_ASS_NO_OUTPUT, NULL);
792       fp = es_fdopen ( dup (outfd), "w");
793       if (!fp)
794         return set_error (GPG_ERR_ASS_GENERAL, "es_fdopen() failed");
795     }
796   else
797     {
798       fp = es_fopencookie (ctx, "w", data_line_cookie_functions);
799       if (!fp)
800         return set_error (GPG_ERR_ASS_GENERAL, 
801                           "error setting up a data stream");
802     }
803   
804   ctrl->with_colons = 1;
805   listmode = mode; 
806   if (ctrl->server_local->list_internal)
807     listmode |= (1<<6);
808   if (ctrl->server_local->list_external)
809     listmode |= (1<<7);
810   err = gpgsm_list_keys (assuan_get_pointer (ctx), list, fp, listmode);
811   free_strlist (list);
812   es_fclose (fp);
813   if (ctrl->server_local->list_to_output)
814     assuan_close_output_fd (ctx);
815   return err;
816 }
817
818 static int 
819 cmd_listkeys (assuan_context_t ctx, char *line)
820 {
821   return do_listkeys (ctx, line, 3);
822 }
823
824 static int 
825 cmd_dumpkeys (assuan_context_t ctx, char *line)
826 {
827   return do_listkeys (ctx, line, 259);
828 }
829
830 static int 
831 cmd_listsecretkeys (assuan_context_t ctx, char *line)
832 {
833   return do_listkeys (ctx, line, 2);
834 }
835
836 static int 
837 cmd_dumpsecretkeys (assuan_context_t ctx, char *line)
838 {
839   return do_listkeys (ctx, line, 258);
840 }
841
842 \f
843 /* GENKEY
844
845    Read the parameters in native format from the input fd and write a
846    certificate request to the output.
847  */
848 static int 
849 cmd_genkey (assuan_context_t ctx, char *line)
850 {
851   ctrl_t ctrl = assuan_get_pointer (ctx);
852   int inp_fd, out_fd;
853   FILE *out_fp;
854   int rc;
855
856   inp_fd = assuan_get_input_fd (ctx);
857   if (inp_fd == -1)
858     return set_error (GPG_ERR_ASS_NO_INPUT, NULL);
859   out_fd = assuan_get_output_fd (ctx);
860   if (out_fd == -1)
861     return set_error (GPG_ERR_ASS_NO_OUTPUT, NULL);
862
863   out_fp = fdopen ( dup(out_fd), "w");
864   if (!out_fp)
865     return set_error (GPG_ERR_ASS_GENERAL, "fdopen() failed");
866   rc = gpgsm_genkey (ctrl, inp_fd, NULL, out_fp);
867   fclose (out_fp);
868
869   /* close and reset the fds */
870   assuan_close_input_fd (ctx);
871   assuan_close_output_fd (ctx);
872
873   return rc;
874 }
875
876
877
878
879 \f
880 /* Tell the assuan library about our commands */
881 static int
882 register_commands (assuan_context_t ctx)
883 {
884   static struct {
885     const char *name;
886     int (*handler)(assuan_context_t, char *line);
887   } table[] = {
888     { "RECIPIENT",     cmd_recipient },
889     { "SIGNER",        cmd_signer },
890     { "ENCRYPT",       cmd_encrypt },
891     { "DECRYPT",       cmd_decrypt },
892     { "VERIFY",        cmd_verify },
893     { "SIGN",          cmd_sign },
894     { "IMPORT",        cmd_import },
895     { "EXPORT",        cmd_export },
896     { "INPUT",         NULL }, 
897     { "OUTPUT",        NULL }, 
898     { "MESSAGE",       cmd_message },
899     { "LISTKEYS",      cmd_listkeys },
900     { "DUMPKEYS",      cmd_dumpkeys },
901     { "LISTSECRETKEYS",cmd_listsecretkeys },
902     { "DUMPSECRETKEYS",cmd_dumpsecretkeys },
903     { "GENKEY",        cmd_genkey },
904     { "DELKEYS",       cmd_delkeys },
905     { NULL }
906   };
907   int i, rc;
908
909   for (i=0; table[i].name; i++)
910     {
911       rc = assuan_register_command (ctx, table[i].name, table[i].handler);
912       if (rc)
913         return rc;
914     } 
915   return 0;
916 }
917
918 /* Startup the server. DEFAULT_RECPLIST is the list of recipients as
919    set from the command line or config file.  We only require those
920    marked as encrypt-to. */
921 void
922 gpgsm_server (certlist_t default_recplist)
923 {
924   int rc;
925   int filedes[2];
926   assuan_context_t ctx;
927   struct server_control_s ctrl;
928   static const char hello[] = ("GNU Privacy Guard's S/M server "
929                                VERSION " ready");
930
931   memset (&ctrl, 0, sizeof ctrl);
932   gpgsm_init_default_ctrl (&ctrl);
933
934   /* We use a pipe based server so that we can work from scripts.
935      assuan_init_pipe_server will automagically detect when we are
936      called with a socketpair and ignore FIELDES in this case. */
937   filedes[0] = 0;
938   filedes[1] = 1;
939   rc = assuan_init_pipe_server (&ctx, filedes);
940   if (rc)
941     {
942       log_error ("failed to initialize the server: %s\n",
943                  gpg_strerror (rc));
944       gpgsm_exit (2);
945     }
946   rc = register_commands (ctx);
947   if (rc)
948     {
949       log_error ("failed to the register commands with Assuan: %s\n",
950                  gpg_strerror(rc));
951       gpgsm_exit (2);
952     }
953   if (opt.verbose || opt.debug)
954     {
955       char *tmp = NULL;
956       const char *s1 = getenv ("GPG_AGENT_INFO");
957       const char *s2 = getenv ("DIRMNGR_INFO");
958
959       if (asprintf (&tmp,
960                     "Home: %s\n"
961                     "Config: %s\n"
962                     "AgentInfo: %s\n"
963                     "DirmngrInfo: %s\n"
964                     "%s",
965                     opt.homedir,
966                     opt.config_filename,
967                     s1?s1:"[not set]",
968                     s2?s2:"[not set]",
969                     hello) > 0)
970         {
971           assuan_set_hello_line (ctx, tmp);
972           free (tmp);
973         }
974     }
975   else
976     assuan_set_hello_line (ctx, hello);
977
978   assuan_register_reset_notify (ctx, reset_notify);
979   assuan_register_input_notify (ctx, input_notify);
980   assuan_register_output_notify (ctx, output_notify);
981   assuan_register_option_handler (ctx, option_handler);
982
983   assuan_set_pointer (ctx, &ctrl);
984   ctrl.server_local = xcalloc (1, sizeof *ctrl.server_local);
985   ctrl.server_local->assuan_ctx = ctx;
986   ctrl.server_local->message_fd = -1;
987   ctrl.server_local->list_internal = 1;
988   ctrl.server_local->list_external = 0;
989   ctrl.server_local->default_recplist = default_recplist;
990
991   if (DBG_ASSUAN)
992     assuan_set_log_stream (ctx, log_get_stream ());
993
994   for (;;)
995     {
996       rc = assuan_accept (ctx);
997       if (rc == -1)
998         {
999           break;
1000         }
1001       else if (rc)
1002         {
1003           log_info ("Assuan accept problem: %s\n", gpg_strerror (rc));
1004           break;
1005         }
1006       
1007       rc = assuan_process (ctx);
1008       if (rc)
1009         {
1010           log_info ("Assuan processing failed: %s\n", gpg_strerror (rc));
1011           continue;
1012         }
1013     }
1014
1015   gpgsm_release_certlist (ctrl.server_local->recplist);
1016   ctrl.server_local->recplist = NULL;
1017   gpgsm_release_certlist (ctrl.server_local->signerlist);
1018   ctrl.server_local->signerlist = NULL;
1019   xfree (ctrl.server_local);
1020
1021   assuan_deinit_server (ctx);
1022 }
1023
1024
1025 static const char *
1026 get_status_string ( int no ) 
1027 {
1028   const char *s;
1029
1030   switch (no)
1031     {
1032     case STATUS_ENTER  : s = "ENTER"; break;
1033     case STATUS_LEAVE  : s = "LEAVE"; break;
1034     case STATUS_ABORT  : s = "ABORT"; break;
1035     case STATUS_NEWSIG : s = "NEWSIG"; break;
1036     case STATUS_GOODSIG: s = "GOODSIG"; break;
1037     case STATUS_SIGEXPIRED: s = "SIGEXPIRED"; break;
1038     case STATUS_KEYREVOKED: s = "KEYREVOKED"; break;
1039     case STATUS_BADSIG : s = "BADSIG"; break;
1040     case STATUS_ERRSIG : s = "ERRSIG"; break;
1041     case STATUS_BADARMOR : s = "BADARMOR"; break;
1042     case STATUS_RSA_OR_IDEA : s= "RSA_OR_IDEA"; break;
1043     case STATUS_TRUST_UNDEFINED: s = "TRUST_UNDEFINED"; break;
1044     case STATUS_TRUST_NEVER      : s = "TRUST_NEVER"; break;
1045     case STATUS_TRUST_MARGINAL : s = "TRUST_MARGINAL"; break;
1046     case STATUS_TRUST_FULLY      : s = "TRUST_FULLY"; break;
1047     case STATUS_TRUST_ULTIMATE : s = "TRUST_ULTIMATE"; break;
1048     case STATUS_GET_BOOL         : s = "GET_BOOL"; break;
1049     case STATUS_GET_LINE         : s = "GET_LINE"; break;
1050     case STATUS_GET_HIDDEN       : s = "GET_HIDDEN"; break;
1051     case STATUS_GOT_IT   : s = "GOT_IT"; break;
1052     case STATUS_SHM_INFO         : s = "SHM_INFO"; break;
1053     case STATUS_SHM_GET  : s = "SHM_GET"; break;
1054     case STATUS_SHM_GET_BOOL     : s = "SHM_GET_BOOL"; break;
1055     case STATUS_SHM_GET_HIDDEN : s = "SHM_GET_HIDDEN"; break;
1056     case STATUS_NEED_PASSPHRASE: s = "NEED_PASSPHRASE"; break;
1057     case STATUS_VALIDSIG         : s = "VALIDSIG"; break;
1058     case STATUS_SIG_ID   : s = "SIG_ID"; break;
1059     case STATUS_ENC_TO   : s = "ENC_TO"; break;
1060     case STATUS_NODATA   : s = "NODATA"; break;
1061     case STATUS_BAD_PASSPHRASE : s = "BAD_PASSPHRASE"; break;
1062     case STATUS_NO_PUBKEY        : s = "NO_PUBKEY"; break;
1063     case STATUS_NO_SECKEY        : s = "NO_SECKEY"; break;
1064     case STATUS_NEED_PASSPHRASE_SYM: s = "NEED_PASSPHRASE_SYM"; break;
1065     case STATUS_DECRYPTION_FAILED: s = "DECRYPTION_FAILED"; break;
1066     case STATUS_DECRYPTION_OKAY: s = "DECRYPTION_OKAY"; break;
1067     case STATUS_MISSING_PASSPHRASE: s = "MISSING_PASSPHRASE"; break;
1068     case STATUS_GOOD_PASSPHRASE : s = "GOOD_PASSPHRASE"; break;
1069     case STATUS_GOODMDC  : s = "GOODMDC"; break;
1070     case STATUS_BADMDC   : s = "BADMDC"; break;
1071     case STATUS_ERRMDC   : s = "ERRMDC"; break;
1072     case STATUS_IMPORTED         : s = "IMPORTED"; break;
1073     case STATUS_IMPORT_OK        : s = "IMPORT_OK"; break;
1074     case STATUS_IMPORT_RES       : s = "IMPORT_RES"; break;
1075     case STATUS_FILE_START       : s = "FILE_START"; break;
1076     case STATUS_FILE_DONE        : s = "FILE_DONE"; break;
1077     case STATUS_FILE_ERROR       : s = "FILE_ERROR"; break;
1078     case STATUS_BEGIN_DECRYPTION:s = "BEGIN_DECRYPTION"; break;
1079     case STATUS_END_DECRYPTION : s = "END_DECRYPTION"; break;
1080     case STATUS_BEGIN_ENCRYPTION:s = "BEGIN_ENCRYPTION"; break;
1081     case STATUS_END_ENCRYPTION : s = "END_ENCRYPTION"; break;
1082     case STATUS_DELETE_PROBLEM : s = "DELETE_PROBLEM"; break;
1083     case STATUS_PROGRESS         : s = "PROGRESS"; break;
1084     case STATUS_SIG_CREATED      : s = "SIG_CREATED"; break;
1085     case STATUS_SESSION_KEY      : s = "SESSION_KEY"; break;
1086     case STATUS_NOTATION_NAME  : s = "NOTATION_NAME" ; break;
1087     case STATUS_NOTATION_DATA  : s = "NOTATION_DATA" ; break;
1088     case STATUS_POLICY_URL     : s = "POLICY_URL" ; break;
1089     case STATUS_BEGIN_STREAM   : s = "BEGIN_STREAM"; break;
1090     case STATUS_END_STREAM     : s = "END_STREAM"; break;
1091     case STATUS_KEY_CREATED    : s = "KEY_CREATED"; break;
1092     case STATUS_UNEXPECTED     : s = "UNEXPECTED"; break;
1093     case STATUS_INV_RECP       : s = "INV_RECP"; break;
1094     case STATUS_NO_RECP        : s = "NO_RECP"; break;
1095     case STATUS_ALREADY_SIGNED : s = "ALREADY_SIGNED"; break;
1096     case STATUS_EXPSIG         : s = "EXPSIG"; break;
1097     case STATUS_EXPKEYSIG      : s = "EXPKEYSIG"; break;
1098     case STATUS_TRUNCATED      : s = "TRUNCATED"; break;
1099     case STATUS_ERROR          : s = "ERROR"; break;
1100     case STATUS_IMPORT_PROBLEM : s = "IMPORT_PROBLEM"; break;
1101     default: s = "?"; break;
1102     }
1103   return s;
1104 }
1105
1106
1107 gpg_error_t
1108 gpgsm_status2 (ctrl_t ctrl, int no, ...)
1109 {
1110   gpg_error_t err = 0;
1111   va_list arg_ptr;
1112   const char *text;
1113
1114   va_start (arg_ptr, no);
1115
1116   if (ctrl->no_server && ctrl->status_fd == -1)
1117     ; /* No status wanted. */
1118   else if (ctrl->no_server)
1119     {
1120       if (!statusfp)
1121         {
1122           if (ctrl->status_fd == 1)
1123             statusfp = stdout;
1124           else if (ctrl->status_fd == 2)
1125             statusfp = stderr;
1126           else
1127             statusfp = fdopen (ctrl->status_fd, "w");
1128       
1129           if (!statusfp)
1130             {
1131               log_fatal ("can't open fd %d for status output: %s\n",
1132                          ctrl->status_fd, strerror(errno));
1133             }
1134         }
1135       
1136       fputs ("[GNUPG:] ", statusfp);
1137       fputs (get_status_string (no), statusfp);
1138     
1139       while ( (text = va_arg (arg_ptr, const char*) ))
1140         {
1141           putc ( ' ', statusfp );
1142           for (; *text; text++) 
1143             {
1144               if (*text == '\n')
1145                 fputs ( "\\n", statusfp );
1146               else if (*text == '\r')
1147                 fputs ( "\\r", statusfp );
1148               else 
1149                 putc ( *(const byte *)text,  statusfp );
1150             }
1151         }
1152       putc ('\n', statusfp);
1153       fflush (statusfp);
1154     }
1155   else 
1156     {
1157       assuan_context_t ctx = ctrl->server_local->assuan_ctx;
1158       char buf[950], *p;
1159       size_t n;
1160
1161       p = buf; 
1162       n = 0;
1163       while ( (text = va_arg (arg_ptr, const char *)) )
1164         {
1165           if (n)
1166             {
1167               *p++ = ' ';
1168               n++;
1169             }
1170           for ( ; *text && n < DIM (buf)-2; n++)
1171             *p++ = *text++;
1172         }
1173       *p = 0;
1174       err = assuan_write_status (ctx, get_status_string (no), buf);
1175     }
1176
1177   va_end (arg_ptr);
1178   return err;
1179 }
1180
1181 gpg_error_t
1182 gpgsm_status (ctrl_t ctrl, int no, const char *text)
1183 {
1184   return gpgsm_status2 (ctrl, no, text, NULL);
1185 }
1186
1187 gpg_error_t
1188 gpgsm_status_with_err_code (ctrl_t ctrl, int no, const char *text,
1189                             gpg_err_code_t ec)
1190 {
1191   char buf[30];
1192
1193   sprintf (buf, "%u", (unsigned int)ec);
1194   if (text)
1195     return gpgsm_status2 (ctrl, no, text, buf, NULL);
1196   else
1197     return gpgsm_status2 (ctrl, no, buf, NULL);
1198 }
1199