Made decryption work in --server mode, allow output of plain base64,
[gnupg.git] / sm / server.c
1 /* server.c - Server mode and main entry point 
2  *      Copyright (C) 2001 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
19  */
20
21 #include <config.h>
22 #include <errno.h>
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <ctype.h>
27 #include <unistd.h>
28
29 #include "gpgsm.h"
30 #include "../assuan/assuan.h"
31
32 #define set_error(e,t) assuan_set_error (ctx, ASSUAN_ ## e, (t))
33 #define digitp(a) ((a) >= '0' && (a) <= '9')
34
35
36 /* The filepointer for status message used in non-server mode */
37 static FILE *statusfp;
38
39 /* Data used to assuciate an Assuan context with local server data */
40 struct server_local_s {
41   ASSUAN_CONTEXT assuan_ctx;
42   int message_fd;
43   CERTLIST recplist;
44 };
45
46 /* Map GNUPG_xxx error codes to Assuan status codes */
47 static int
48 rc_to_assuan_status (int rc)
49 {
50   switch (rc)
51     {
52     case 0: break;
53     case GNUPG_Bad_Certificate:   rc = ASSUAN_Bad_Certificate; break;
54     case GNUPG_Bad_Certificate_Path: rc = ASSUAN_Bad_Certificate_Path; break;
55     case GNUPG_Missing_Certificate: rc = ASSUAN_Missing_Certificate; break;
56     case GNUPG_No_Data:           rc = ASSUAN_No_Data_Available; break;
57     case GNUPG_Bad_Signature:     rc = ASSUAN_Bad_Signature; break;
58     case GNUPG_Not_Implemented:   rc = ASSUAN_Not_Implemented; break;
59     case GNUPG_No_Agent:          rc = ASSUAN_No_Agent; break;
60     case GNUPG_Agent_Error:       rc = ASSUAN_Agent_Error; break;
61     case GNUPG_No_Public_Key:     rc = ASSUAN_No_Public_Key; break;
62     case GNUPG_No_Secret_Key:     rc = ASSUAN_No_Secret_Key; break;
63     case GNUPG_Invalid_Data:      rc = ASSUAN_Invalid_Data; break;
64     case GNUPG_Invalid_Name:      rc = ASSUAN_Invalid_Name; break;
65
66     case GNUPG_Read_Error: 
67     case GNUPG_Write_Error:
68     case GNUPG_IO_Error: 
69       rc = ASSUAN_Server_IO_Error;
70       break;
71     case GNUPG_Out_Of_Core:    
72     case GNUPG_Resource_Limit: 
73       rc = ASSUAN_Server_Resource_Problem;
74       break;
75     case GNUPG_Bug: 
76     case GNUPG_Internal_Error:   
77       rc = ASSUAN_Server_Bug;
78       break;
79     default: 
80       rc = ASSUAN_Server_Fault;
81       break;
82     }
83   return rc;
84 }
85
86 static void
87 reset_notify (ASSUAN_CONTEXT ctx)
88 {
89   CTRL ctrl = assuan_get_pointer (ctx);
90
91   gpgsm_release_certlist (ctrl->server_local->recplist);
92   ctrl->server_local->recplist = NULL;
93 }
94
95
96 static void
97 input_notify (ASSUAN_CONTEXT ctx, const char *line)
98 {
99   CTRL ctrl = assuan_get_pointer (ctx);
100
101   ctrl->autodetect_encoding = 0;
102   ctrl->is_pem = 0;
103   ctrl->is_base64 = 0;
104   if (strstr (line, "--armor"))
105     ctrl->is_pem = 1;  
106   else if (strstr (line, "--base64"))
107     ctrl->is_base64 = 1; 
108   else if (strstr (line, "--binary"))
109     ;
110   else
111     ctrl->autodetect_encoding = 0;
112 }
113
114 static void
115 output_notify (ASSUAN_CONTEXT ctx, const char *line)
116 {
117   CTRL ctrl = assuan_get_pointer (ctx);
118
119   ctrl->create_pem = 0;
120   ctrl->create_base64 = 0;
121   if (strstr (line, "--armor"))
122     ctrl->create_pem = 1;  
123   else if (strstr (line, "--base64"))
124     ctrl->create_base64 = 1; /* just the raw output */
125 }
126
127
128
129 /*  RECIPIENT <userID>
130
131   Set the recipient for the encryption.  <userID> should be the
132   internal representation of the key; the server may accept any other
133   way of specification [we will support this].  If this is a valid and
134   trusted recipient the server does respond with OK, otherwise the
135   return is an ERR with the reason why the recipient can't be used,
136   the encryption will then not be done for this recipient.  IF the
137   policy is not to encrypt at all if not all recipients are valid, the
138   client has to take care of this.  All RECIPIENT commands are
139   cumulative until a RESET or an successful ENCRYPT command.  */
140 static int 
141 cmd_recipient (ASSUAN_CONTEXT ctx, char *line)
142 {
143   CTRL ctrl = assuan_get_pointer (ctx);
144   int rc;
145
146   rc = gpgsm_add_to_certlist (line, &ctrl->server_local->recplist);
147
148   return rc_to_assuan_status (rc);
149 }
150
151
152 /* ENCRYPT 
153
154   Do the actual encryption process. Takes the plaintext from the INPUT
155   command, writes to the ciphertext to the file descriptor set with
156   the OUTPUT command, take the recipients form all the recipients set
157   so far.  If this command fails the clients should try to delete all
158   output currently done or otherwise mark it as invalid.  GPGSM does
159   ensure that there won't be any security problem with leftover data
160   on the output in this case.
161
162   This command should in general not fail, as all necessary checks
163   have been done while setting the recipients.  The input and output
164   pipes are closed. */
165 static int 
166 cmd_encrypt (ASSUAN_CONTEXT ctx, char *line)
167 {
168   CTRL ctrl = assuan_get_pointer (ctx);
169   int inp_fd, out_fd;
170   FILE *out_fp;
171   int rc;
172
173   inp_fd = assuan_get_input_fd (ctx);
174   if (inp_fd == -1)
175     return set_error (No_Input, NULL);
176   out_fd = assuan_get_output_fd (ctx);
177   if (out_fd == -1)
178     return set_error (No_Output, NULL);
179
180   out_fp = fdopen ( dup(out_fd), "w");
181   if (!out_fp)
182     return set_error (General_Error, "fdopen() failed");
183   rc = gpgsm_encrypt (assuan_get_pointer (ctx),
184                       ctrl->server_local->recplist,
185                       inp_fd, out_fp);
186   fclose (out_fp);
187
188   if (!rc)
189     {
190       gpgsm_release_certlist (ctrl->server_local->recplist);
191       ctrl->server_local->recplist = NULL;
192     }
193   return rc_to_assuan_status (rc);
194 }
195
196 /* DECRYPT
197
198   This performs the decrypt operation after doing some check on the
199   internal state. (e.g. that only needed data has been set).  Because
200   it utilizes the GPG-Agent for the session key decryption, there is
201   no need to ask the client for a protecting passphrase - GpgAgent
202   does take care of this by requesting this from the user. */
203 static int 
204 cmd_decrypt (ASSUAN_CONTEXT ctx, char *line)
205 {
206   CTRL ctrl = assuan_get_pointer (ctx);
207   int inp_fd, out_fd;
208   FILE *out_fp;
209   int rc;
210
211   inp_fd = assuan_get_input_fd (ctx);
212   if (inp_fd == -1)
213     return set_error (No_Input, NULL);
214   out_fd = assuan_get_output_fd (ctx);
215   if (out_fd == -1)
216     return set_error (No_Output, NULL);
217
218   out_fp = fdopen ( dup(out_fd), "w");
219   if (!out_fp)
220     return set_error (General_Error, "fdopen() failed");
221   rc = gpgsm_decrypt (ctrl, inp_fd, out_fp); 
222   fclose (out_fp);
223
224   return rc_to_assuan_status (rc);
225 }
226
227
228 /* VERIFY
229
230   This does a verify operation on the message send to the input-FD.
231   The result is written out using status lines.  If an output FD was
232   given, the signed text will be written to that.
233   
234   If the signature is a detached one, the server will inquire about
235   the signed material and the client must provide it.
236   */
237 static int 
238 cmd_verify (ASSUAN_CONTEXT ctx, char *line)
239 {
240   int rc;
241   CTRL ctrl = assuan_get_pointer (ctx);
242   int fd = assuan_get_input_fd (ctx);
243
244   if (fd == -1)
245     return set_error (No_Input, NULL);
246
247   rc = gpgsm_verify (assuan_get_pointer (ctx), fd,
248                      ctrl->server_local->message_fd);
249
250   return rc_to_assuan_status (rc);
251 }
252
253
254 /* SIGN [--detached]
255
256   Sign the data set with the INPUT command and write it to the sink
257   set by OUTPUT.  With "--detached" specified, a detached signature is
258   created (surprise).  */
259 static int 
260 cmd_sign (ASSUAN_CONTEXT ctx, char *line)
261 {
262   int inp_fd, out_fd;
263   FILE *out_fp;
264   int detached;
265   int rc;
266
267   inp_fd = assuan_get_input_fd (ctx);
268   if (inp_fd == -1)
269     return set_error (No_Input, NULL);
270   out_fd = assuan_get_output_fd (ctx);
271   if (out_fd == -1)
272     return set_error (No_Output, NULL);
273
274   detached = !!strstr (line, "--detached");  /* fixme: this is ambiguous */
275
276   out_fp = fdopen ( dup(out_fd), "w");
277   if (!out_fp)
278     return set_error (General_Error, "fdopen() failed");
279   rc = gpgsm_sign (assuan_get_pointer (ctx), inp_fd, detached, out_fp);
280   fclose (out_fp);
281
282   return rc_to_assuan_status (rc);
283 }
284
285
286 /* IMPORT
287
288   Import the certificates read form the input-fd, return status
289   message for each imported one.  The import checks the validity of
290   the certificate but not of the path.  It is possible to import
291   expired certificates.  */
292 static int 
293 cmd_import (ASSUAN_CONTEXT ctx, char *line)
294 {
295   int rc;
296   int fd = assuan_get_input_fd (ctx);
297
298   if (fd == -1)
299     return set_error (No_Input, NULL);
300
301   rc = gpgsm_import (assuan_get_pointer (ctx), fd);
302
303   return rc_to_assuan_status (rc);
304 }
305
306 /* MESSAGE FD=<n>
307
308    Set the file descriptor to read a message which is used with
309    detached signatures */
310 static int 
311 cmd_message (ASSUAN_CONTEXT ctx, char *line)
312 {
313   char *endp;
314   int fd;
315   CTRL ctrl = assuan_get_pointer (ctx);
316
317   if (strncmp (line, "FD=", 3))
318     return set_error (Syntax_Error, "FD=<n> expected");
319   line += 3;
320   if (!digitp (*line))
321     return set_error (Syntax_Error, "number required");
322   fd = strtoul (line, &endp, 10);
323   if (*endp)
324     return set_error (Syntax_Error, "garbage found");
325   if (fd == -1)
326     return set_error (No_Input, NULL);
327
328   ctrl->server_local->message_fd = fd;
329   return 0;
330 }
331
332 static int 
333 cmd_listkeys (ASSUAN_CONTEXT ctx, char *line)
334 {
335   CTRL ctrl = assuan_get_pointer (ctx);
336
337   ctrl->with_colons = 1;
338   /* fixme: check that the returned data_fp is not NULL */
339   gpgsm_list_keys (assuan_get_pointer (ctx), NULL,
340                         assuan_get_data_fp (ctx));
341
342   return 0;
343 }
344
345
346
347
348 \f
349 /* Tell the assuan library about our commands */
350 static int
351 register_commands (ASSUAN_CONTEXT ctx)
352 {
353   static struct {
354     const char *name;
355     int cmd_id;
356     int (*handler)(ASSUAN_CONTEXT, char *line);
357   } table[] = {
358     { "RECIPIENT",  0,  cmd_recipient },
359     { "ENCRYPT",    0,  cmd_encrypt },
360     { "DECRYPT",    0,  cmd_decrypt },
361     { "VERIFY",     0,  cmd_verify },
362     { "SIGN",       0,  cmd_sign },
363     { "IMPORT",     0,  cmd_import },
364     { "",     ASSUAN_CMD_INPUT, NULL }, 
365     { "",     ASSUAN_CMD_OUTPUT, NULL }, 
366     { "MESSAGE",    0,  cmd_message },
367     { "LISTKEYS",   0,  cmd_listkeys },
368     { NULL }
369   };
370   int i, j, rc;
371
372   for (i=j=0; table[i].name; i++)
373     {
374       rc = assuan_register_command (ctx,
375                                     table[i].cmd_id? table[i].cmd_id
376                                                    : (ASSUAN_CMD_USER + j++),
377                                     table[i].name, table[i].handler);
378       if (rc)
379         return rc;
380     } 
381   return 0;
382 }
383
384 /* Startup the server */
385 void
386 gpgsm_server (void)
387 {
388   int rc;
389   int filedes[2];
390   ASSUAN_CONTEXT ctx;
391   struct server_control_s ctrl;
392
393   memset (&ctrl, 0, sizeof ctrl);
394
395   /* For now we use a simple pipe based server so that we can work
396      from scripts.  We will later add options to run as a daemon and
397      wait for requests on a Unix domain socket */
398   filedes[0] = 0;
399   filedes[1] = 1;
400   rc = assuan_init_pipe_server (&ctx, filedes);
401   if (rc)
402     {
403       log_error ("failed to initialize the server: %s\n",
404                  assuan_strerror(rc));
405       gpgsm_exit (2);
406     }
407   rc = register_commands (ctx);
408   if (rc)
409     {
410       log_error ("failed to the register commands with Assuan: %s\n",
411                  assuan_strerror(rc));
412       gpgsm_exit (2);
413     }
414   assuan_set_hello_line (ctx, "GNU Privacy Guard's S/M server ready");
415
416   assuan_register_reset_notify (ctx, reset_notify);
417   assuan_register_input_notify (ctx, input_notify);
418   assuan_register_output_notify (ctx, output_notify);
419
420   assuan_set_pointer (ctx, &ctrl);
421   ctrl.server_local = xcalloc (1, sizeof *ctrl.server_local);
422   ctrl.server_local->assuan_ctx = ctx;
423   ctrl.server_local->message_fd = -1;
424
425   log_info ("Assuan started\n");
426   for (;;)
427     {
428       rc = assuan_accept (ctx);
429       if (rc == -1)
430         {
431           log_info ("Assuan terminated\n");
432           break;
433         }
434       else if (rc)
435         {
436           log_info ("Assuan accept problem: %s\n", assuan_strerror (rc));
437           break;
438         }
439       
440       rc = assuan_process (ctx);
441       if (rc)
442         {
443           log_info ("Assuan processing failed: %s\n", assuan_strerror (rc));
444           continue;
445         }
446     }
447
448   gpgsm_release_certlist (ctrl.server_local->recplist);
449   ctrl.server_local->recplist = NULL;
450
451   assuan_deinit_pipe_server (ctx);
452 }
453
454
455 static const char *
456 get_status_string ( int no ) 
457 {
458   const char *s;
459
460   switch (no)
461     {
462     case STATUS_ENTER  : s = "ENTER"; break;
463     case STATUS_LEAVE  : s = "LEAVE"; break;
464     case STATUS_ABORT  : s = "ABORT"; break;
465     case STATUS_GOODSIG: s = "GOODSIG"; break;
466     case STATUS_SIGEXPIRED: s = "SIGEXPIRED"; break;
467     case STATUS_KEYREVOKED: s = "KEYREVOKED"; break;
468     case STATUS_BADSIG : s = "BADSIG"; break;
469     case STATUS_ERRSIG : s = "ERRSIG"; break;
470     case STATUS_BADARMOR : s = "BADARMOR"; break;
471     case STATUS_RSA_OR_IDEA : s= "RSA_OR_IDEA"; break;
472     case STATUS_TRUST_UNDEFINED: s = "TRUST_UNDEFINED"; break;
473     case STATUS_TRUST_NEVER      : s = "TRUST_NEVER"; break;
474     case STATUS_TRUST_MARGINAL : s = "TRUST_MARGINAL"; break;
475     case STATUS_TRUST_FULLY      : s = "TRUST_FULLY"; break;
476     case STATUS_TRUST_ULTIMATE : s = "TRUST_ULTIMATE"; break;
477     case STATUS_GET_BOOL         : s = "GET_BOOL"; break;
478     case STATUS_GET_LINE         : s = "GET_LINE"; break;
479     case STATUS_GET_HIDDEN       : s = "GET_HIDDEN"; break;
480     case STATUS_GOT_IT   : s = "GOT_IT"; break;
481     case STATUS_SHM_INFO         : s = "SHM_INFO"; break;
482     case STATUS_SHM_GET  : s = "SHM_GET"; break;
483     case STATUS_SHM_GET_BOOL     : s = "SHM_GET_BOOL"; break;
484     case STATUS_SHM_GET_HIDDEN : s = "SHM_GET_HIDDEN"; break;
485     case STATUS_NEED_PASSPHRASE: s = "NEED_PASSPHRASE"; break;
486     case STATUS_VALIDSIG         : s = "VALIDSIG"; break;
487     case STATUS_SIG_ID   : s = "SIG_ID"; break;
488     case STATUS_ENC_TO   : s = "ENC_TO"; break;
489     case STATUS_NODATA   : s = "NODATA"; break;
490     case STATUS_BAD_PASSPHRASE : s = "BAD_PASSPHRASE"; break;
491     case STATUS_NO_PUBKEY        : s = "NO_PUBKEY"; break;
492     case STATUS_NO_SECKEY        : s = "NO_SECKEY"; break;
493     case STATUS_NEED_PASSPHRASE_SYM: s = "NEED_PASSPHRASE_SYM"; break;
494     case STATUS_DECRYPTION_FAILED: s = "DECRYPTION_FAILED"; break;
495     case STATUS_DECRYPTION_OKAY: s = "DECRYPTION_OKAY"; break;
496     case STATUS_MISSING_PASSPHRASE: s = "MISSING_PASSPHRASE"; break;
497     case STATUS_GOOD_PASSPHRASE : s = "GOOD_PASSPHRASE"; break;
498     case STATUS_GOODMDC  : s = "GOODMDC"; break;
499     case STATUS_BADMDC   : s = "BADMDC"; break;
500     case STATUS_ERRMDC   : s = "ERRMDC"; break;
501     case STATUS_IMPORTED         : s = "IMPORTED"; break;
502     case STATUS_IMPORT_RES       : s = "IMPORT_RES"; break;
503     case STATUS_FILE_START       : s = "FILE_START"; break;
504     case STATUS_FILE_DONE        : s = "FILE_DONE"; break;
505     case STATUS_FILE_ERROR       : s = "FILE_ERROR"; break;
506     case STATUS_BEGIN_DECRYPTION:s = "BEGIN_DECRYPTION"; break;
507     case STATUS_END_DECRYPTION : s = "END_DECRYPTION"; break;
508     case STATUS_BEGIN_ENCRYPTION:s = "BEGIN_ENCRYPTION"; break;
509     case STATUS_END_ENCRYPTION : s = "END_ENCRYPTION"; break;
510     case STATUS_DELETE_PROBLEM : s = "DELETE_PROBLEM"; break;
511     case STATUS_PROGRESS         : s = "PROGRESS"; break;
512     case STATUS_SIG_CREATED      : s = "SIG_CREATED"; break;
513     case STATUS_SESSION_KEY      : s = "SESSION_KEY"; break;
514     case STATUS_NOTATION_NAME  : s = "NOTATION_NAME" ; break;
515     case STATUS_NOTATION_DATA  : s = "NOTATION_DATA" ; break;
516     case STATUS_POLICY_URL     : s = "POLICY_URL" ; break;
517     case STATUS_BEGIN_STREAM   : s = "BEGIN_STREAM"; break;
518     case STATUS_END_STREAM     : s = "END_STREAM"; break;
519     case STATUS_KEY_CREATED    : s = "KEY_CREATED"; break;
520     case STATUS_UNEXPECTED     : s = "UNEXPECTED"; break;
521     case STATUS_INV_RECP       : s = "INV_RECP"; break;
522     case STATUS_NO_RECP        : s = "NO_RECP"; break;
523     case STATUS_ALREADY_SIGNED : s = "ALREADY_SIGNED"; break;
524     default: s = "?"; break;
525     }
526   return s;
527 }
528
529
530
531 void
532 gpgsm_status (CTRL ctrl, int no, const char *text)
533 {
534   if (ctrl->no_server)
535     {
536       if (ctrl->status_fd == -1)
537         return; /* no status wanted */
538       if (!statusfp)
539         {
540           if (ctrl->status_fd == 1)
541             statusfp = stdout;
542           else if (ctrl->status_fd == 2)
543             statusfp = stderr;
544           else
545             statusfp = fdopen (ctrl->status_fd, "w");
546       
547           if (!statusfp)
548             {
549               log_fatal ("can't open fd %d for status output: %s\n",
550                          ctrl->status_fd, strerror(errno));
551             }
552         }
553       
554       fputs ("[GNUPG:] ", statusfp);
555       fputs (get_status_string (no), statusfp);
556     
557       if (text)
558         {
559           putc ( ' ', statusfp );
560           for (; *text; text++) 
561             {
562               if (*text == '\n')
563                 fputs ( "\\n", statusfp );
564               else if (*text == '\r')
565                 fputs ( "\\r", statusfp );
566               else 
567                 putc ( *(const byte *)text,  statusfp );
568             }
569         }
570       putc ('\n', statusfp);
571       fflush (statusfp);
572     }
573   else 
574     {
575       ASSUAN_CONTEXT ctx = ctrl->server_local->assuan_ctx;
576
577       assuan_write_status (ctx, get_status_string (no), text);
578     }
579 }
580
581
582 #if 0
583 /*
584  * Write a status line with a buffer using %XX escapes.  If WRAP is >
585  * 0 wrap the line after this length.  If STRING is not NULL it will
586  * be prepended to the buffer, no escaping is done for string.
587  * A wrap of -1 forces spaces not to be encoded as %20.
588  */
589 void
590 write_status_text_and_buffer ( int no, const char *string,
591                                const char *buffer, size_t len, int wrap )
592 {
593     const char *s, *text;
594     int esc, first;
595     int lower_limit = ' ';
596     size_t n, count, dowrap;
597
598     if( !statusfp )
599         return;  /* not enabled */
600     
601     if (wrap == -1) {
602         lower_limit--;
603         wrap = 0;
604     }
605
606     text = get_status_string (no);
607     count = dowrap = first = 1;
608     do {
609         if (dowrap) {
610             fprintf (statusfp, "[GNUPG:] %s ", text );
611             count = dowrap = 0;
612             if (first && string) {
613                 fputs (string, statusfp);
614                 count += strlen (string);
615             }
616             first = 0;
617         }
618         for (esc=0, s=buffer, n=len; n && !esc; s++, n-- ) {
619             if ( *s == '%' || *(const byte*)s <= lower_limit 
620                            || *(const byte*)s == 127 ) 
621                 esc = 1;
622             if ( wrap && ++count > wrap ) {
623                 dowrap=1;
624                 break;
625             }
626         }
627         if (esc) {
628             s--; n++;
629         }
630         if (s != buffer) 
631             fwrite (buffer, s-buffer, 1, statusfp );
632         if ( esc ) {
633             fprintf (statusfp, "%%%02X", *(const byte*)s );
634             s++; n--;
635         }
636         buffer = s;
637         len = n;
638         if ( dowrap && len )
639             putc ( '\n', statusfp );
640     } while ( len );
641
642     putc ('\n',statusfp);
643     fflush (statusfp);
644 }
645 #endif
646
647
648
649
650
651
652