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