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