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