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