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