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