Expand a ~ in the ca-cert-file argument.
[gnupg.git] / sm / server.c
1 /* server.c - Server mode and main entry point 
2  * Copyright (C) 2001, 2002, 2003, 2004, 2005, 2006,
3  *               2007, 2008, 2009 Free Software Foundation, Inc.
4  *
5  * This file is part of GnuPG.
6  *
7  * GnuPG is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 3 of the License, or
10  * (at your option) any later version.
11  *
12  * GnuPG is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, see <http://www.gnu.org/licenses/>.
19  */
20
21 #include <config.h>
22 #include <errno.h>
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <stdarg.h>
27 #include <ctype.h>
28 #include <unistd.h>
29
30 #include <assuan.h>
31
32 #include "gpgsm.h"
33 #include "sysutils.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   int enable_audit_log;         /* Use an audit log.  */
49   certlist_t recplist;
50   certlist_t signerlist;
51   certlist_t default_recplist; /* As set by main() - don't release. */
52   int allow_pinentry_notify;   /* Set if pinentry notifications should
53                                   be passed back to the client. */
54   int no_encrypt_to;           /* Local version of option.  */
55 };
56
57
58 /* Cookie definition for assuan data line output.  */
59 static ssize_t data_line_cookie_write (void *cookie,
60                                        const void *buffer, size_t size);
61 static int data_line_cookie_close (void *cookie);
62 static es_cookie_io_functions_t data_line_cookie_functions =
63   {
64     NULL,
65     data_line_cookie_write,
66     NULL,
67     data_line_cookie_close
68   };
69
70
71 \f
72 static int command_has_option (const char *cmd, const char *cmdopt);
73
74
75
76 \f
77 /* Note that it is sufficient to allocate the target string D as
78    long as the source string S, i.e.: strlen(s)+1; */
79 static void
80 strcpy_escaped_plus (char *d, const char *s)
81 {
82   while (*s)
83     {
84       if (*s == '%' && s[1] && s[2])
85         { 
86           s++;
87           *d++ = xtoi_2 (s);
88           s += 2;
89         }
90       else if (*s == '+')
91         *d++ = ' ', s++;
92       else
93         *d++ = *s++;
94     }
95   *d = 0; 
96 }
97
98
99 /* Skip over options.  
100    Blanks after the options are also removed. */
101 static char *
102 skip_options (const char *line)
103 {
104   while (spacep (line))
105     line++;
106   while ( *line == '-' && line[1] == '-' )
107     {
108       while (*line && !spacep (line))
109         line++;
110       while (spacep (line))
111         line++;
112     }
113   return (char*)line;
114 }
115
116
117 /* Check whether the option NAME appears in LINE */
118 static int
119 has_option (const char *line, const char *name)
120 {
121   const char *s;
122   int n = strlen (name);
123
124   s = strstr (line, name);
125   if (s && s >= skip_options (line))
126     return 0;
127   return (s && (s == line || spacep (s-1)) && (!s[n] || spacep (s+n)));
128 }
129
130
131 /* A write handler used by es_fopencookie to write assuan data
132    lines.  */
133 static ssize_t
134 data_line_cookie_write (void *cookie, const void *buffer, size_t size)
135 {
136   assuan_context_t ctx = cookie;
137
138   if (assuan_send_data (ctx, buffer, size))
139     {
140       errno = EIO;
141       return -1;
142     }
143
144   return size;
145 }
146
147 static int
148 data_line_cookie_close (void *cookie)
149 {
150   assuan_context_t ctx = cookie;
151
152   if (assuan_send_data (ctx, NULL, 0))
153     {
154       errno = EIO;
155       return -1;
156     }
157
158   return 0;
159 }
160
161
162 static void 
163 close_message_fd (ctrl_t ctrl)
164 {
165   if (ctrl->server_local->message_fd != -1)
166     {
167       close (ctrl->server_local->message_fd);
168       ctrl->server_local->message_fd = -1;
169     }
170 }
171
172
173 /* Start a new audit session if this has been enabled.  */
174 static gpg_error_t
175 start_audit_session (ctrl_t ctrl)
176 {
177   audit_release (ctrl->audit);
178   ctrl->audit = NULL;
179   if (ctrl->server_local->enable_audit_log && !(ctrl->audit = audit_new ()) )
180     return gpg_error_from_syserror ();
181   
182   return 0;
183 }
184
185
186 static int
187 option_handler (assuan_context_t ctx, const char *key, const char *value)
188 {
189   ctrl_t ctrl = assuan_get_pointer (ctx);
190   gpg_error_t err = 0;
191
192   if (!strcmp (key, "putenv"))
193     {
194       /* Change the session's environment to be used for the
195          Pinentry.  Valid values are:
196           <NAME>            Delete envvar NAME
197           <KEY>=            Set envvar NAME to the empty string
198           <KEY>=<VALUE>     Set envvar NAME to VALUE
199       */
200       err = session_env_putenv (opt.session_env, value);
201     }
202   else if (!strcmp (key, "display"))
203     {
204       err = session_env_setenv (opt.session_env, "DISPLAY", value);
205     }
206   else if (!strcmp (key, "ttyname"))
207     {
208       err = session_env_setenv (opt.session_env, "GPG_TTY", value);
209     }
210   else if (!strcmp (key, "ttytype"))
211     {
212       err = session_env_setenv (opt.session_env, "TERM", value);
213     }
214   else if (!strcmp (key, "lc-ctype"))
215     {
216       xfree (opt.lc_ctype);
217       opt.lc_ctype = xtrystrdup (value);
218       if (!opt.lc_ctype)
219         err = gpg_error_from_syserror ();
220     }
221   else if (!strcmp (key, "lc-messages"))
222     {
223       xfree (opt.lc_messages);
224       opt.lc_messages = xtrystrdup (value);
225       if (!opt.lc_messages)
226         err = gpg_error_from_syserror ();
227     }
228   else if (!strcmp (key, "xauthority"))
229     {
230       err = session_env_setenv (opt.session_env, "XAUTHORITY", value);
231     }
232   else if (!strcmp (key, "pinentry-user-data"))
233     {
234       err = session_env_setenv (opt.session_env, "PINENTRY_USER_DATA", value);
235     }
236   else if (!strcmp (key, "include-certs"))
237     {
238       int i = *value? atoi (value) : -1;
239       if (ctrl->include_certs < -2)
240         err = gpg_error (GPG_ERR_ASS_PARAMETER);
241       else
242         ctrl->include_certs = i;
243     }
244   else if (!strcmp (key, "list-mode"))
245     {
246       int i = *value? atoi (value) : 0;
247       if (!i || i == 1) /* default and mode 1 */
248         {
249           ctrl->server_local->list_internal = 1;
250           ctrl->server_local->list_external = 0;
251         }
252       else if (i == 2)
253         {
254           ctrl->server_local->list_internal = 0;
255           ctrl->server_local->list_external = 1;
256         }
257       else if (i == 3)
258         {
259           ctrl->server_local->list_internal = 1;
260           ctrl->server_local->list_external = 1;
261         }
262       else
263         err = gpg_error (GPG_ERR_ASS_PARAMETER);
264     }
265   else if (!strcmp (key, "list-to-output"))
266     {
267       int i = *value? atoi (value) : 0;
268       ctrl->server_local->list_to_output = i;
269     }
270   else if (!strcmp (key, "with-validation"))
271     {
272       int i = *value? atoi (value) : 0;
273       ctrl->with_validation = i;
274     }
275   else if (!strcmp (key, "validation-model"))
276     {
277       int i = gpgsm_parse_validation_model (value);
278       if ( i >= 0 && i <= 1 )
279         ctrl->validation_model = i;
280       else
281         err = gpg_error (GPG_ERR_ASS_PARAMETER);
282     }
283   else if (!strcmp (key, "with-key-data"))
284     {
285       opt.with_key_data = 1;
286     }
287   else if (!strcmp (key, "enable-audit-log"))
288     {
289       int i = *value? atoi (value) : 0;
290       ctrl->server_local->enable_audit_log = i;
291     }
292   else if (!strcmp (key, "allow-pinentry-notify"))
293     {
294       ctrl->server_local->allow_pinentry_notify = 1;
295     }
296   else if (!strcmp (key, "with-ephemeral-keys"))
297     {
298       int i = *value? atoi (value) : 0;
299       ctrl->with_ephemeral_keys = i;
300     }
301   else if (!strcmp (key, "no-encrypt-to"))
302     {
303       ctrl->server_local->no_encrypt_to = 1;
304     }
305   else
306     err = gpg_error (GPG_ERR_UNKNOWN_OPTION);
307
308   return err;
309 }
310
311
312 static void
313 reset_notify (assuan_context_t ctx)
314 {
315   ctrl_t ctrl = assuan_get_pointer (ctx);
316
317   gpgsm_release_certlist (ctrl->server_local->recplist);
318   gpgsm_release_certlist (ctrl->server_local->signerlist);
319   ctrl->server_local->recplist = NULL;
320   ctrl->server_local->signerlist = NULL;
321   close_message_fd (ctrl);
322   assuan_close_input_fd (ctx);
323   assuan_close_output_fd (ctx);
324 }
325
326
327 static void
328 input_notify (assuan_context_t ctx, const char *line)
329 {
330   ctrl_t ctrl = assuan_get_pointer (ctx);
331
332   ctrl->autodetect_encoding = 0;
333   ctrl->is_pem = 0;
334   ctrl->is_base64 = 0;
335   if (strstr (line, "--armor"))
336     ctrl->is_pem = 1;  
337   else if (strstr (line, "--base64"))
338     ctrl->is_base64 = 1; 
339   else if (strstr (line, "--binary"))
340     ;
341   else
342     ctrl->autodetect_encoding = 1;
343 }
344
345 static void
346 output_notify (assuan_context_t ctx, const char *line)
347 {
348   ctrl_t ctrl = assuan_get_pointer (ctx);
349
350   ctrl->create_pem = 0;
351   ctrl->create_base64 = 0;
352   if (strstr (line, "--armor"))
353     ctrl->create_pem = 1;  
354   else if (strstr (line, "--base64"))
355     ctrl->create_base64 = 1; /* just the raw output */
356 }
357
358
359
360 /*  RECIPIENT <userID>
361
362   Set the recipient for the encryption.  <userID> should be the
363   internal representation of the key; the server may accept any other
364   way of specification [we will support this].  If this is a valid and
365   trusted recipient the server does respond with OK, otherwise the
366   return is an ERR with the reason why the recipient can't be used,
367   the encryption will then not be done for this recipient.  If the
368   policy is not to encrypt at all if not all recipients are valid, the
369   client has to take care of this.  All RECIPIENT commands are
370   cumulative until a RESET or an successful ENCRYPT command.  */
371 static int 
372 cmd_recipient (assuan_context_t ctx, char *line)
373 {
374   ctrl_t ctrl = assuan_get_pointer (ctx);
375   int rc;
376
377   if (!ctrl->audit)
378     rc = start_audit_session (ctrl);
379   else
380     rc = 0;
381
382   if (!rc)
383     rc = gpgsm_add_to_certlist (ctrl, line, 0,
384                                 &ctrl->server_local->recplist, 0);
385   if (rc)
386     {
387       gpgsm_status2 (ctrl, STATUS_INV_RECP,
388                      get_inv_recpsgnr_code (rc), line, NULL);
389     }
390
391   return rc;
392 }
393
394 /*  SIGNER <userID>
395
396   Set the signer's keys for the signature creation.  <userID> should
397   be the internal representation of the key; the server may accept any
398   other way of specification [we will support this].  If this is a
399   valid and usable signing key the server does respond with OK,
400   otherwise it returns an ERR with the reason why the key can't be
401   used, the signing will then not be done for this key.  If the policy
402   is not to sign at all if not all signer keys are valid, the client
403   has to take care of this.  All SIGNER commands are cumulative until
404   a RESET but they are *not* reset by an SIGN command becuase it can
405   be expected that set of signers are used for more than one sign
406   operation.  */
407 static int 
408 cmd_signer (assuan_context_t ctx, char *line)
409 {
410   ctrl_t ctrl = assuan_get_pointer (ctx);
411   int rc;
412
413   rc = gpgsm_add_to_certlist (ctrl, line, 1,
414                               &ctrl->server_local->signerlist, 0);
415   if (rc)
416     {
417       gpgsm_status2 (ctrl, STATUS_INV_SGNR, 
418                      get_inv_recpsgnr_code (rc), line, NULL);
419       /* For compatibiliy reasons we also issue the old code after the
420          new one.  */
421       gpgsm_status2 (ctrl, STATUS_INV_RECP, 
422                      get_inv_recpsgnr_code (rc), line, NULL);
423     }
424   return rc;
425 }
426
427
428 /* ENCRYPT 
429
430   Do the actual encryption process. Takes the plaintext from the INPUT
431   command, writes to the ciphertext to the file descriptor set with
432   the OUTPUT command, take the recipients form all the recipients set
433   so far.  If this command fails the clients should try to delete all
434   output currently done or otherwise mark it as invalid.  GPGSM does
435   ensure that there won't be any security problem with leftover data
436   on the output in this case.
437
438   This command should in general not fail, as all necessary checks
439   have been done while setting the recipients.  The input and output
440   pipes are closed. */
441 static int 
442 cmd_encrypt (assuan_context_t ctx, char *line)
443 {
444   ctrl_t ctrl = assuan_get_pointer (ctx);
445   certlist_t cl;
446   int inp_fd, out_fd;
447   FILE *out_fp;
448   int rc;
449
450   (void)line;
451
452   inp_fd = translate_sys2libc_fd (assuan_get_input_fd (ctx), 0);
453   if (inp_fd == -1)
454     return set_error (GPG_ERR_ASS_NO_INPUT, NULL);
455   out_fd = translate_sys2libc_fd (assuan_get_output_fd (ctx), 1);
456   if (out_fd == -1)
457     return set_error (GPG_ERR_ASS_NO_OUTPUT, NULL);
458
459   out_fp = fdopen (dup (out_fd), "w");
460   if (!out_fp)
461     return set_error (GPG_ERR_ASS_GENERAL, "fdopen() failed");
462   
463   /* Now add all encrypt-to marked recipients from the default
464      list. */
465   rc = 0;
466   if (!opt.no_encrypt_to && !ctrl->server_local->no_encrypt_to)
467     {
468       for (cl=ctrl->server_local->default_recplist; !rc && cl; cl = cl->next)
469         if (cl->is_encrypt_to)
470           rc = gpgsm_add_cert_to_certlist (ctrl, cl->cert,
471                                            &ctrl->server_local->recplist, 1);
472     }
473   if (!rc)
474     rc = ctrl->audit? 0 : start_audit_session (ctrl);
475   if (!rc)
476     rc = gpgsm_encrypt (assuan_get_pointer (ctx),
477                         ctrl->server_local->recplist,
478                         inp_fd, out_fp);
479   fclose (out_fp);
480
481   gpgsm_release_certlist (ctrl->server_local->recplist);
482   ctrl->server_local->recplist = NULL;
483   /* Close and reset the fd */
484   close_message_fd (ctrl);
485   assuan_close_input_fd (ctx);
486   assuan_close_output_fd (ctx);
487   return rc;
488 }
489
490
491 /* DECRYPT
492
493   This performs the decrypt operation after doing some check on the
494   internal state. (e.g. that only needed data has been set).  Because
495   it utilizes the GPG-Agent for the session key decryption, there is
496   no need to ask the client for a protecting passphrase - GpgAgent
497   does take care of this by requesting this from the user. */
498 static int 
499 cmd_decrypt (assuan_context_t ctx, char *line)
500 {
501   ctrl_t ctrl = assuan_get_pointer (ctx);
502   int inp_fd, out_fd;
503   FILE *out_fp;
504   int rc;
505
506   (void)line;
507
508   inp_fd = translate_sys2libc_fd (assuan_get_input_fd (ctx), 0);
509   if (inp_fd == -1)
510     return set_error (GPG_ERR_ASS_NO_INPUT, NULL);
511   out_fd = translate_sys2libc_fd (assuan_get_output_fd (ctx), 1);
512   if (out_fd == -1)
513     return set_error (GPG_ERR_ASS_NO_OUTPUT, NULL);
514
515   out_fp = fdopen (dup(out_fd), "w");
516   if (!out_fp)
517     return set_error (GPG_ERR_ASS_GENERAL, "fdopen() failed");
518
519   rc = start_audit_session (ctrl);
520   if (!rc)
521     rc = gpgsm_decrypt (ctrl, inp_fd, out_fp); 
522   fclose (out_fp);
523
524   /* close and reset the fd */
525   close_message_fd (ctrl);
526   assuan_close_input_fd (ctx);
527   assuan_close_output_fd (ctx);
528
529   return rc;
530 }
531
532
533 /* VERIFY
534
535   This does a verify operation on the message send to the input-FD.
536   The result is written out using status lines.  If an output FD was
537   given, the signed text will be written to that.
538   
539   If the signature is a detached one, the server will inquire about
540   the signed material and the client must provide it.
541   */
542 static int 
543 cmd_verify (assuan_context_t ctx, char *line)
544 {
545   int rc;
546   ctrl_t ctrl = assuan_get_pointer (ctx);
547   int fd = translate_sys2libc_fd (assuan_get_input_fd (ctx), 0);
548   int out_fd = translate_sys2libc_fd (assuan_get_output_fd (ctx), 1);
549   FILE *out_fp = NULL;
550
551   (void)line;
552
553   if (fd == -1)
554     return set_error (GPG_ERR_ASS_NO_INPUT, NULL);
555
556   if (out_fd != -1)
557     {
558       out_fp = fdopen ( dup(out_fd), "w");
559       if (!out_fp)
560         return set_error (GPG_ERR_ASS_GENERAL, "fdopen() failed");
561     }
562
563   rc = start_audit_session (ctrl);
564   if (!rc)
565     rc = gpgsm_verify (assuan_get_pointer (ctx), fd,
566                        ctrl->server_local->message_fd, out_fp);
567   if (out_fp)
568     fclose (out_fp);
569
570   /* close and reset the fd */
571   close_message_fd (ctrl);
572   assuan_close_input_fd (ctx);
573   assuan_close_output_fd (ctx);
574
575   return rc;
576 }
577
578
579 /* SIGN [--detached]
580
581   Sign the data set with the INPUT command and write it to the sink
582   set by OUTPUT.  With "--detached" specified, a detached signature is
583   created (surprise).  */
584 static int 
585 cmd_sign (assuan_context_t ctx, char *line)
586 {
587   ctrl_t ctrl = assuan_get_pointer (ctx);
588   int inp_fd, out_fd;
589   FILE *out_fp;
590   int detached;
591   int rc;
592
593   inp_fd = translate_sys2libc_fd (assuan_get_input_fd (ctx), 0);
594   if (inp_fd == -1)
595     return set_error (GPG_ERR_ASS_NO_INPUT, NULL);
596   out_fd = translate_sys2libc_fd (assuan_get_output_fd (ctx), 1);
597   if (out_fd == -1)
598     return set_error (GPG_ERR_ASS_NO_OUTPUT, NULL);
599
600   detached = has_option (line, "--detached"); 
601
602   out_fp = fdopen ( dup(out_fd), "w");
603   if (!out_fp)
604     return set_error (GPG_ERR_ASS_GENERAL, "fdopen() failed");
605
606   rc = start_audit_session (ctrl);
607   if (!rc)
608     rc = gpgsm_sign (assuan_get_pointer (ctx), ctrl->server_local->signerlist,
609                      inp_fd, detached, out_fp);
610   fclose (out_fp);
611
612   /* close and reset the fd */
613   close_message_fd (ctrl);
614   assuan_close_input_fd (ctx);
615   assuan_close_output_fd (ctx);
616
617   return rc;
618 }
619
620
621 /* IMPORT [--re-import]
622
623    Import the certificates read form the input-fd, return status
624    message for each imported one.  The import checks the validity of
625    the certificate but not of the entire chain.  It is possible to
626    import expired certificates.
627
628    With the option --re-import the input data is expected to a be a LF
629    separated list of fingerprints.  The command will re-import these
630    certificates, meaning that they are made permanent by removing
631    their ephemeral flag.   */
632 static int 
633 cmd_import (assuan_context_t ctx, char *line)
634 {
635   ctrl_t ctrl = assuan_get_pointer (ctx);
636   int rc;
637   int fd = translate_sys2libc_fd (assuan_get_input_fd (ctx), 0);
638   int reimport = has_option (line, "--re-import"); 
639
640   (void)line;
641
642   if (fd == -1)
643     return set_error (GPG_ERR_ASS_NO_INPUT, NULL);
644
645   rc = gpgsm_import (assuan_get_pointer (ctx), fd, reimport);
646
647   /* close and reset the fd */
648   close_message_fd (ctrl);
649   assuan_close_input_fd (ctx);
650   assuan_close_output_fd (ctx);
651
652   return rc;
653 }
654
655
656 /* EXPORT [--data [--armor|--base64]] [--] pattern
657
658  */
659
660 static int 
661 cmd_export (assuan_context_t ctx, char *line)
662 {
663   ctrl_t ctrl = assuan_get_pointer (ctx);
664   char *p;
665   strlist_t list, sl;
666   int use_data;
667   
668   use_data = has_option (line, "--data");
669
670   if (use_data)
671     {
672       /* We need to override any possible setting done by an OUTPUT command. */
673       ctrl->create_pem = has_option (line, "--armor");
674       ctrl->create_base64 = has_option (line, "--base64");
675     }
676
677   line = skip_options (line);
678
679   /* Break the line down into an strlist_t. */
680   list = NULL;
681   for (p=line; *p; line = p)
682     {
683       while (*p && *p != ' ')
684         p++;
685       if (*p)
686         *p++ = 0;
687       if (*line)
688         {
689           sl = xtrymalloc (sizeof *sl + strlen (line));
690           if (!sl)
691             {
692               free_strlist (list);
693               return out_of_core ();
694             }
695           sl->flags = 0;
696           strcpy_escaped_plus (sl->d, line);
697           sl->next = list;
698           list = sl;
699         }
700     }
701
702   if (use_data)
703     {
704       estream_t stream;
705
706       stream = es_fopencookie (ctx, "w", data_line_cookie_functions);
707       if (!stream)
708         {
709           free_strlist (list);
710           return set_error (GPG_ERR_ASS_GENERAL, 
711                             "error setting up a data stream");
712         }
713       gpgsm_export (ctrl, list, NULL, stream);
714       es_fclose (stream);
715     }
716   else
717     {
718       int fd = translate_sys2libc_fd (assuan_get_output_fd (ctx), 1);
719       FILE *out_fp;
720
721       if (fd == -1)
722         {
723           free_strlist (list);
724           return set_error (GPG_ERR_ASS_NO_OUTPUT, NULL);
725         }
726       out_fp = fdopen ( dup(fd), "w");
727       if (!out_fp)
728         {
729           free_strlist (list);
730           return set_error (GPG_ERR_ASS_GENERAL, "fdopen() failed");
731         }
732       
733       gpgsm_export (ctrl, list, out_fp, NULL);
734       fclose (out_fp);
735     }
736
737   free_strlist (list);
738   /* Close and reset the fds. */
739   close_message_fd (ctrl);
740   assuan_close_input_fd (ctx);
741   assuan_close_output_fd (ctx);
742   return 0;
743 }
744
745
746 static int 
747 cmd_delkeys (assuan_context_t ctx, char *line)
748 {
749   ctrl_t ctrl = assuan_get_pointer (ctx);
750   char *p;
751   strlist_t list, sl;
752   int rc;
753
754   /* break the line down into an strlist_t */
755   list = NULL;
756   for (p=line; *p; line = p)
757     {
758       while (*p && *p != ' ')
759         p++;
760       if (*p)
761         *p++ = 0;
762       if (*line)
763         {
764           sl = xtrymalloc (sizeof *sl + strlen (line));
765           if (!sl)
766             {
767               free_strlist (list);
768               return out_of_core ();
769             }
770           sl->flags = 0;
771           strcpy_escaped_plus (sl->d, line);
772           sl->next = list;
773           list = sl;
774         }
775     }
776
777   rc = gpgsm_delete (ctrl, list);
778   free_strlist (list);
779
780   /* close and reset the fd */
781   close_message_fd (ctrl);
782   assuan_close_input_fd (ctx);
783   assuan_close_output_fd (ctx);
784
785   return rc;
786 }
787
788
789
790 /* MESSAGE FD=<n>
791
792    Set the file descriptor to read a message which is used with
793    detached signatures */
794 static int 
795 cmd_message (assuan_context_t ctx, char *line)
796 {
797   int rc;
798   gnupg_fd_t sysfd;
799   int fd;
800   ctrl_t ctrl = assuan_get_pointer (ctx);
801
802   rc = assuan_command_parse_fd (ctx, line, &sysfd);
803   if (rc)
804     return rc;
805   fd = translate_sys2libc_fd (sysfd, 0);
806   if (fd == -1)
807     return set_error (GPG_ERR_ASS_NO_INPUT, NULL);
808   ctrl->server_local->message_fd = fd;
809   return 0;
810 }
811
812 /* LISTKEYS [<patterns>]
813    DUMPKEYS [<patterns>]
814    LISTSECRETKEYS [<patterns>]
815    DUMPSECRETKEYS [<patterns>]
816 */
817 static int 
818 do_listkeys (assuan_context_t ctx, char *line, int mode)
819 {
820   ctrl_t ctrl = assuan_get_pointer (ctx);
821   estream_t fp;
822   char *p;
823   strlist_t list, sl;
824   unsigned int listmode;
825   gpg_error_t err;
826
827   /* Break the line down into an strlist. */
828   list = NULL;
829   for (p=line; *p; line = p)
830     {
831       while (*p && *p != ' ')
832         p++;
833       if (*p)
834         *p++ = 0;
835       if (*line)
836         {
837           sl = xtrymalloc (sizeof *sl + strlen (line));
838           if (!sl)
839             {
840               free_strlist (list);
841               return out_of_core ();
842             }
843           sl->flags = 0;
844           strcpy_escaped_plus (sl->d, line);
845           sl->next = list;
846           list = sl;
847         }
848     }
849
850   if (ctrl->server_local->list_to_output)
851     {
852       int outfd = translate_sys2libc_fd (assuan_get_output_fd (ctx), 1);
853
854       if ( outfd == -1 )
855         return set_error (GPG_ERR_ASS_NO_OUTPUT, NULL);
856       fp = es_fdopen ( dup (outfd), "w");
857       if (!fp)
858         return set_error (GPG_ERR_ASS_GENERAL, "es_fdopen() failed");
859     }
860   else
861     {
862       fp = es_fopencookie (ctx, "w", data_line_cookie_functions);
863       if (!fp)
864         return set_error (GPG_ERR_ASS_GENERAL, 
865                           "error setting up a data stream");
866     }
867   
868   ctrl->with_colons = 1;
869   listmode = mode; 
870   if (ctrl->server_local->list_internal)
871     listmode |= (1<<6);
872   if (ctrl->server_local->list_external)
873     listmode |= (1<<7);
874   err = gpgsm_list_keys (assuan_get_pointer (ctx), list, fp, listmode);
875   free_strlist (list);
876   es_fclose (fp);
877   if (ctrl->server_local->list_to_output)
878     assuan_close_output_fd (ctx);
879   return err;
880 }
881
882 static int 
883 cmd_listkeys (assuan_context_t ctx, char *line)
884 {
885   return do_listkeys (ctx, line, 3);
886 }
887
888 static int 
889 cmd_dumpkeys (assuan_context_t ctx, char *line)
890 {
891   return do_listkeys (ctx, line, 259);
892 }
893
894 static int 
895 cmd_listsecretkeys (assuan_context_t ctx, char *line)
896 {
897   return do_listkeys (ctx, line, 2);
898 }
899
900 static int 
901 cmd_dumpsecretkeys (assuan_context_t ctx, char *line)
902 {
903   return do_listkeys (ctx, line, 258);
904 }
905
906 \f
907 /* GENKEY
908
909    Read the parameters in native format from the input fd and write a
910    certificate request to the output.
911  */
912 static int 
913 cmd_genkey (assuan_context_t ctx, char *line)
914 {
915   ctrl_t ctrl = assuan_get_pointer (ctx);
916   int inp_fd, out_fd;
917   FILE *out_fp;
918   int rc;
919   estream_t in_stream;
920
921   (void)line;
922
923   inp_fd = translate_sys2libc_fd (assuan_get_input_fd (ctx), 0);
924   if (inp_fd == -1)
925     return set_error (GPG_ERR_ASS_NO_INPUT, NULL);
926   out_fd = translate_sys2libc_fd (assuan_get_output_fd (ctx), 1);
927   if (out_fd == -1)
928     return set_error (GPG_ERR_ASS_NO_OUTPUT, NULL);
929
930   in_stream = es_fdopen_nc (inp_fd, "r");
931   if (!in_stream)
932     return set_error (GPG_ERR_ASS_GENERAL, "es_fdopen failed");
933
934   out_fp = fdopen ( dup(out_fd), "w");
935   if (!out_fp)
936     {
937       es_fclose (in_stream);
938       return set_error (GPG_ERR_ASS_GENERAL, "fdopen() failed");
939     }
940   rc = gpgsm_genkey (ctrl, in_stream, out_fp);
941   fclose (out_fp);
942
943   /* close and reset the fds */
944   assuan_close_input_fd (ctx);
945   assuan_close_output_fd (ctx);
946
947   return rc;
948 }
949
950
951 \f
952 /* GETAUDITLOG [--data] [--html]
953
954    !!!WORK in PROGRESS!!!
955
956    If --data is used, the output is send using D-lines and not to the
957    source given by an OUTPUT command.
958
959    If --html is used the output is formated as an XHTML block. This is
960    designed to be incorporated into a HTML document.
961  */
962 static int 
963 cmd_getauditlog (assuan_context_t ctx, char *line)
964 {
965   ctrl_t ctrl = assuan_get_pointer (ctx);
966   int  out_fd;
967   estream_t out_stream;
968   int opt_data, opt_html;
969   int rc;
970
971   opt_data = has_option (line, "--data"); 
972   opt_html = has_option (line, "--html"); 
973   line = skip_options (line);
974
975   if (!ctrl->audit)
976     return gpg_error (GPG_ERR_NO_DATA);
977
978   if (opt_data)
979     {
980       out_stream = es_fopencookie (ctx, "w", data_line_cookie_functions);
981       if (!out_stream)
982         return set_error (GPG_ERR_ASS_GENERAL, 
983                           "error setting up a data stream");
984     }
985   else
986     {
987       out_fd = translate_sys2libc_fd (assuan_get_output_fd (ctx), 1);
988       if (out_fd == -1)
989         return set_error (GPG_ERR_ASS_NO_OUTPUT, NULL);
990       
991       out_stream = es_fdopen_nc ( dup (out_fd), "w");
992       if (!out_stream)
993         {
994           return set_error (GPG_ERR_ASS_GENERAL, "es_fdopen() failed");
995         }
996     }
997
998   audit_print_result (ctrl->audit, out_stream, opt_html);
999   rc = 0;
1000
1001   es_fclose (out_stream);
1002
1003   /* Close and reset the fd. */
1004   if (!opt_data)
1005     assuan_close_output_fd (ctx);
1006   return rc;
1007 }
1008
1009
1010 /* GETINFO <what>
1011
1012    Multipurpose function to return a variety of information.
1013    Supported values for WHAT are:
1014
1015      version     - Return the version of the program.
1016      pid         - Return the process id of the server.
1017      agent-check - Return success if the agent is running.
1018      cmd_has_option CMD OPT
1019                  - Returns OK if the command CMD implements the option OPT.
1020
1021  */
1022 static int
1023 cmd_getinfo (assuan_context_t ctx, char *line)
1024 {
1025   int rc = 0;
1026
1027   if (!strcmp (line, "version"))
1028     {
1029       const char *s = VERSION;
1030       rc = assuan_send_data (ctx, s, strlen (s));
1031     }
1032   else if (!strcmp (line, "pid"))
1033     {
1034       char numbuf[50];
1035
1036       snprintf (numbuf, sizeof numbuf, "%lu", (unsigned long)getpid ());
1037       rc = assuan_send_data (ctx, numbuf, strlen (numbuf));
1038     }
1039   else if (!strcmp (line, "agent-check"))
1040     {
1041       ctrl_t ctrl = assuan_get_pointer (ctx);
1042       rc = gpgsm_agent_send_nop (ctrl);
1043     }
1044   else if (!strncmp (line, "cmd_has_option", 14)
1045            && (line[14] == ' ' || line[14] == '\t' || !line[14]))
1046     {
1047       char *cmd, *cmdopt;
1048       line += 14;
1049       while (*line == ' ' || *line == '\t')
1050         line++;
1051       if (!*line)
1052         rc = gpg_error (GPG_ERR_MISSING_VALUE);
1053       else
1054         {
1055           cmd = line;
1056           while (*line && (*line != ' ' && *line != '\t'))
1057             line++;
1058           if (!*line)
1059             rc = gpg_error (GPG_ERR_MISSING_VALUE);
1060           else
1061             {
1062               *line++ = 0;
1063               while (*line == ' ' || *line == '\t')
1064                 line++;
1065               if (!*line)
1066                 rc = gpg_error (GPG_ERR_MISSING_VALUE);
1067               else
1068                 {
1069                   cmdopt = line;
1070                   if (!command_has_option (cmd, cmdopt))
1071                     rc = gpg_error (GPG_ERR_GENERAL);
1072                 }
1073             }
1074         }
1075     }
1076   else
1077     rc = set_error (GPG_ERR_ASS_PARAMETER, "unknown value for WHAT");
1078
1079   return rc;
1080 }
1081
1082
1083 \f
1084 /* Return true if the command CMD implements the option OPT.  */
1085 static int
1086 command_has_option (const char *cmd, const char *cmdopt)
1087 {
1088   if (!strcmp (cmd, "IMPORT"))
1089     {
1090       if (!strcmp (cmdopt, "re-import"))
1091         return 1;
1092     }
1093       
1094   return 0;
1095 }
1096
1097
1098 /* Tell the assuan library about our commands */
1099 static int
1100 register_commands (assuan_context_t ctx)
1101 {
1102   static struct {
1103     const char *name;
1104     int (*handler)(assuan_context_t, char *line);
1105   } table[] = {
1106     { "RECIPIENT",     cmd_recipient },
1107     { "SIGNER",        cmd_signer },
1108     { "ENCRYPT",       cmd_encrypt },
1109     { "DECRYPT",       cmd_decrypt },
1110     { "VERIFY",        cmd_verify },
1111     { "SIGN",          cmd_sign },
1112     { "IMPORT",        cmd_import },
1113     { "EXPORT",        cmd_export },
1114     { "INPUT",         NULL }, 
1115     { "OUTPUT",        NULL }, 
1116     { "MESSAGE",       cmd_message },
1117     { "LISTKEYS",      cmd_listkeys },
1118     { "DUMPKEYS",      cmd_dumpkeys },
1119     { "LISTSECRETKEYS",cmd_listsecretkeys },
1120     { "DUMPSECRETKEYS",cmd_dumpsecretkeys },
1121     { "GENKEY",        cmd_genkey },
1122     { "DELKEYS",       cmd_delkeys },
1123     { "GETAUDITLOG",   cmd_getauditlog },
1124     { "GETINFO",       cmd_getinfo },
1125     { NULL }
1126   };
1127   int i, rc;
1128
1129   for (i=0; table[i].name; i++)
1130     {
1131       rc = assuan_register_command (ctx, table[i].name, table[i].handler);
1132       if (rc)
1133         return rc;
1134     } 
1135   return 0;
1136 }
1137
1138 /* Startup the server. DEFAULT_RECPLIST is the list of recipients as
1139    set from the command line or config file.  We only require those
1140    marked as encrypt-to. */
1141 void
1142 gpgsm_server (certlist_t default_recplist)
1143 {
1144   int rc;
1145   int filedes[2];
1146   assuan_context_t ctx;
1147   struct server_control_s ctrl;
1148   static const char hello[] = ("GNU Privacy Guard's S/M server "
1149                                VERSION " ready");
1150
1151   memset (&ctrl, 0, sizeof ctrl);
1152   gpgsm_init_default_ctrl (&ctrl);
1153
1154   /* We use a pipe based server so that we can work from scripts.
1155      assuan_init_pipe_server will automagically detect when we are
1156      called with a socketpair and ignore FIELDES in this case. */
1157   filedes[0] = 0;
1158   filedes[1] = 1;
1159   rc = assuan_init_pipe_server (&ctx, filedes);
1160   if (rc)
1161     {
1162       log_error ("failed to initialize the server: %s\n",
1163                  gpg_strerror (rc));
1164       gpgsm_exit (2);
1165     }
1166   rc = register_commands (ctx);
1167   if (rc)
1168     {
1169       log_error ("failed to the register commands with Assuan: %s\n",
1170                  gpg_strerror(rc));
1171       gpgsm_exit (2);
1172     }
1173   if (opt.verbose || opt.debug)
1174     {
1175       char *tmp = NULL;
1176       const char *s1 = getenv ("GPG_AGENT_INFO");
1177       const char *s2 = getenv ("DIRMNGR_INFO");
1178
1179       if (asprintf (&tmp,
1180                     "Home: %s\n"
1181                     "Config: %s\n"
1182                     "AgentInfo: %s\n"
1183                     "DirmngrInfo: %s\n"
1184                     "%s",
1185                     opt.homedir,
1186                     opt.config_filename,
1187                     s1?s1:"[not set]",
1188                     s2?s2:"[not set]",
1189                     hello) > 0)
1190         {
1191           assuan_set_hello_line (ctx, tmp);
1192           free (tmp);
1193         }
1194     }
1195   else
1196     assuan_set_hello_line (ctx, hello);
1197
1198   assuan_register_reset_notify (ctx, reset_notify);
1199   assuan_register_input_notify (ctx, input_notify);
1200   assuan_register_output_notify (ctx, output_notify);
1201   assuan_register_option_handler (ctx, option_handler);
1202
1203   assuan_set_pointer (ctx, &ctrl);
1204   ctrl.server_local = xcalloc (1, sizeof *ctrl.server_local);
1205   ctrl.server_local->assuan_ctx = ctx;
1206   ctrl.server_local->message_fd = -1;
1207   ctrl.server_local->list_internal = 1;
1208   ctrl.server_local->list_external = 0;
1209   ctrl.server_local->default_recplist = default_recplist;
1210
1211   if (DBG_ASSUAN)
1212     assuan_set_log_stream (ctx, log_get_stream ());
1213
1214   for (;;)
1215     {
1216       rc = assuan_accept (ctx);
1217       if (rc == -1)
1218         {
1219           break;
1220         }
1221       else if (rc)
1222         {
1223           log_info ("Assuan accept problem: %s\n", gpg_strerror (rc));
1224           break;
1225         }
1226       
1227       rc = assuan_process (ctx);
1228       if (rc)
1229         {
1230           log_info ("Assuan processing failed: %s\n", gpg_strerror (rc));
1231           continue;
1232         }
1233     }
1234
1235   gpgsm_release_certlist (ctrl.server_local->recplist);
1236   ctrl.server_local->recplist = NULL;
1237   gpgsm_release_certlist (ctrl.server_local->signerlist);
1238   ctrl.server_local->signerlist = NULL;
1239   xfree (ctrl.server_local);
1240
1241   audit_release (ctrl.audit);
1242   ctrl.audit = NULL;
1243
1244   assuan_deinit_server (ctx);
1245 }
1246
1247
1248
1249 gpg_error_t
1250 gpgsm_status2 (ctrl_t ctrl, int no, ...)
1251 {
1252   gpg_error_t err = 0;
1253   va_list arg_ptr;
1254   const char *text;
1255
1256   va_start (arg_ptr, no);
1257
1258   if (ctrl->no_server && ctrl->status_fd == -1)
1259     ; /* No status wanted. */
1260   else if (ctrl->no_server)
1261     {
1262       if (!statusfp)
1263         {
1264           if (ctrl->status_fd == 1)
1265             statusfp = stdout;
1266           else if (ctrl->status_fd == 2)
1267             statusfp = stderr;
1268           else
1269             statusfp = fdopen (ctrl->status_fd, "w");
1270       
1271           if (!statusfp)
1272             {
1273               log_fatal ("can't open fd %d for status output: %s\n",
1274                          ctrl->status_fd, strerror(errno));
1275             }
1276         }
1277       
1278       fputs ("[GNUPG:] ", statusfp);
1279       fputs (get_status_string (no), statusfp);
1280     
1281       while ( (text = va_arg (arg_ptr, const char*) ))
1282         {
1283           putc ( ' ', statusfp );
1284           for (; *text; text++) 
1285             {
1286               if (*text == '\n')
1287                 fputs ( "\\n", statusfp );
1288               else if (*text == '\r')
1289                 fputs ( "\\r", statusfp );
1290               else 
1291                 putc ( *(const byte *)text,  statusfp );
1292             }
1293         }
1294       putc ('\n', statusfp);
1295       fflush (statusfp);
1296     }
1297   else 
1298     {
1299       assuan_context_t ctx = ctrl->server_local->assuan_ctx;
1300       char buf[950], *p;
1301       size_t n;
1302
1303       p = buf; 
1304       n = 0;
1305       while ( (text = va_arg (arg_ptr, const char *)) )
1306         {
1307           if (n)
1308             {
1309               *p++ = ' ';
1310               n++;
1311             }
1312           for ( ; *text && n < DIM (buf)-2; n++)
1313             *p++ = *text++;
1314         }
1315       *p = 0;
1316       err = assuan_write_status (ctx, get_status_string (no), buf);
1317     }
1318
1319   va_end (arg_ptr);
1320   return err;
1321 }
1322
1323 gpg_error_t
1324 gpgsm_status (ctrl_t ctrl, int no, const char *text)
1325 {
1326   return gpgsm_status2 (ctrl, no, text, NULL);
1327 }
1328
1329 gpg_error_t
1330 gpgsm_status_with_err_code (ctrl_t ctrl, int no, const char *text,
1331                             gpg_err_code_t ec)
1332 {
1333   char buf[30];
1334
1335   sprintf (buf, "%u", (unsigned int)ec);
1336   if (text)
1337     return gpgsm_status2 (ctrl, no, text, buf, NULL);
1338   else
1339     return gpgsm_status2 (ctrl, no, buf, NULL);
1340 }
1341
1342
1343 /* Helper to notify the client about Pinentry events.  Because that
1344    might disturb some older clients, this is only done when enabled
1345    via an option.  Returns an gpg error code. */
1346 gpg_error_t
1347 gpgsm_proxy_pinentry_notify (ctrl_t ctrl, const unsigned char *line)
1348 {
1349   if (!ctrl || !ctrl->server_local 
1350       || !ctrl->server_local->allow_pinentry_notify)
1351     return 0;
1352   return assuan_inquire (ctrl->server_local->assuan_ctx, line, NULL, NULL, 0);
1353 }
1354
1355
1356