Implemented encryption in server mode.
[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   
207
208   return set_error (Not_Implemented, "fixme");
209 }
210
211
212 /* VERIFY
213
214   This does a verify operation on the message send to the input-FD.
215   The result is written out using status lines.  If an output FD was
216   given, the signed text will be written to that.
217   
218   If the signature is a detached one, the server will inquire about
219   the signed material and the client must provide it.
220   */
221 static int 
222 cmd_verify (ASSUAN_CONTEXT ctx, char *line)
223 {
224   int rc;
225   CTRL ctrl = assuan_get_pointer (ctx);
226   int fd = assuan_get_input_fd (ctx);
227
228   if (fd == -1)
229     return set_error (No_Input, NULL);
230
231   rc = gpgsm_verify (assuan_get_pointer (ctx), fd,
232                      ctrl->server_local->message_fd);
233
234   return rc_to_assuan_status (rc);
235 }
236
237
238 /* SIGN [--detached]
239
240   Sign the data set with the INPUT command and write it to the sink
241   set by OUTPUT.  With "--detached" specified, a detached signature is
242   created (surprise).  */
243 static int 
244 cmd_sign (ASSUAN_CONTEXT ctx, char *line)
245 {
246   int inp_fd, out_fd;
247   FILE *out_fp;
248   int detached;
249   int rc;
250
251   inp_fd = assuan_get_input_fd (ctx);
252   if (inp_fd == -1)
253     return set_error (No_Input, NULL);
254   out_fd = assuan_get_output_fd (ctx);
255   if (out_fd == -1)
256     return set_error (No_Output, NULL);
257
258   detached = !!strstr (line, "--detached");  /* fixme: this is ambiguous */
259
260   out_fp = fdopen ( dup(out_fd), "w");
261   if (!out_fp)
262     return set_error (General_Error, "fdopen() failed");
263   rc = gpgsm_sign (assuan_get_pointer (ctx), inp_fd, detached, out_fp);
264   fclose (out_fp);
265
266   return rc_to_assuan_status (rc);
267 }
268
269
270 /* IMPORT
271
272   Import the certificates read form the input-fd, return status
273   message for each imported one.  The import checks the validity of
274   the certificate but not of the path.  It is possible to import
275   expired certificates.  */
276 static int 
277 cmd_import (ASSUAN_CONTEXT ctx, char *line)
278 {
279   int rc;
280   int fd = assuan_get_input_fd (ctx);
281
282   if (fd == -1)
283     return set_error (No_Input, NULL);
284
285   rc = gpgsm_import (assuan_get_pointer (ctx), fd);
286
287   return rc_to_assuan_status (rc);
288 }
289
290 /* MESSAGE FD=<n>
291
292    Set the file descriptor to read a message which is used with
293    detached signatures */
294 static int 
295 cmd_message (ASSUAN_CONTEXT ctx, char *line)
296 {
297   char *endp;
298   int fd;
299   CTRL ctrl = assuan_get_pointer (ctx);
300
301   if (strncmp (line, "FD=", 3))
302     return set_error (Syntax_Error, "FD=<n> expected");
303   line += 3;
304   if (!digitp (*line))
305     return set_error (Syntax_Error, "number required");
306   fd = strtoul (line, &endp, 10);
307   if (*endp)
308     return set_error (Syntax_Error, "garbage found");
309   if (fd == -1)
310     return set_error (No_Input, NULL);
311
312   ctrl->server_local->message_fd = fd;
313   return 0;
314 }
315
316 static int 
317 cmd_listkeys (ASSUAN_CONTEXT ctx, char *line)
318 {
319   CTRL ctrl = assuan_get_pointer (ctx);
320
321   ctrl->with_colons = 1;
322   /* fixme: check that the returned data_fp is not NULL */
323   gpgsm_list_keys (assuan_get_pointer (ctx), NULL,
324                         assuan_get_data_fp (ctx));
325
326   return 0;
327 }
328
329
330
331
332 \f
333 /* Tell the assuan library about our commands */
334 static int
335 register_commands (ASSUAN_CONTEXT ctx)
336 {
337   static struct {
338     const char *name;
339     int cmd_id;
340     int (*handler)(ASSUAN_CONTEXT, char *line);
341   } table[] = {
342     { "RECIPIENT",  0,  cmd_recipient },
343     { "ENCRYPT",    0,  cmd_encrypt },
344     { "DECRYPT",    0,  cmd_decrypt },
345     { "VERIFY",     0,  cmd_verify },
346     { "SIGN",       0,  cmd_sign },
347     { "IMPORT",     0,  cmd_import },
348     { "",     ASSUAN_CMD_INPUT, NULL }, 
349     { "",     ASSUAN_CMD_OUTPUT, NULL }, 
350     { "MESSAGE",    0,  cmd_message },
351     { "LISTKEYS",   0,  cmd_listkeys },
352     { NULL }
353   };
354   int i, j, rc;
355
356   for (i=j=0; table[i].name; i++)
357     {
358       rc = assuan_register_command (ctx,
359                                     table[i].cmd_id? table[i].cmd_id
360                                                    : (ASSUAN_CMD_USER + j++),
361                                     table[i].name, table[i].handler);
362       if (rc)
363         return rc;
364     } 
365   return 0;
366 }
367
368 /* Startup the server */
369 void
370 gpgsm_server (void)
371 {
372   int rc;
373   int filedes[2];
374   ASSUAN_CONTEXT ctx;
375   struct server_control_s ctrl;
376
377   memset (&ctrl, 0, sizeof ctrl);
378
379   /* For now we use a simple pipe based server so that we can work
380      from scripts.  We will later add options to run as a daemon and
381      wait for requests on a Unix domain socket */
382   filedes[0] = 0;
383   filedes[1] = 1;
384   rc = assuan_init_pipe_server (&ctx, filedes);
385   if (rc)
386     {
387       log_error ("failed to initialize the server: %s\n",
388                  assuan_strerror(rc));
389       gpgsm_exit (2);
390     }
391   rc = register_commands (ctx);
392   if (rc)
393     {
394       log_error ("failed to the register commands with Assuan: %s\n",
395                  assuan_strerror(rc));
396       gpgsm_exit (2);
397     }
398
399   assuan_register_reset_notify (ctx, reset_notify);
400   assuan_register_input_notify (ctx, input_notify);
401   assuan_register_output_notify (ctx, output_notify);
402
403   assuan_set_pointer (ctx, &ctrl);
404   ctrl.server_local = xcalloc (1, sizeof *ctrl.server_local);
405   ctrl.server_local->assuan_ctx = ctx;
406   ctrl.server_local->message_fd = -1;
407
408   log_info ("Assuan started\n");
409   for (;;)
410     {
411       rc = assuan_accept (ctx);
412       if (rc == -1)
413         {
414           log_info ("Assuan terminated\n");
415           break;
416         }
417       else if (rc)
418         {
419           log_info ("Assuan accept problem: %s\n", assuan_strerror (rc));
420           break;
421         }
422       
423       rc = assuan_process (ctx);
424       if (rc)
425         {
426           log_info ("Assuan processing failed: %s\n", assuan_strerror (rc));
427           continue;
428         }
429     }
430
431   gpgsm_release_certlist (ctrl.server_local->recplist);
432   ctrl.server_local->recplist = NULL;
433
434   assuan_deinit_pipe_server (ctx);
435 }
436
437
438 static const char *
439 get_status_string ( int no ) 
440 {
441   const char *s;
442
443   switch (no)
444     {
445     case STATUS_ENTER  : s = "ENTER"; break;
446     case STATUS_LEAVE  : s = "LEAVE"; break;
447     case STATUS_ABORT  : s = "ABORT"; break;
448     case STATUS_GOODSIG: s = "GOODSIG"; break;
449     case STATUS_SIGEXPIRED: s = "SIGEXPIRED"; break;
450     case STATUS_KEYREVOKED: s = "KEYREVOKED"; break;
451     case STATUS_BADSIG : s = "BADSIG"; break;
452     case STATUS_ERRSIG : s = "ERRSIG"; break;
453     case STATUS_BADARMOR : s = "BADARMOR"; break;
454     case STATUS_RSA_OR_IDEA : s= "RSA_OR_IDEA"; break;
455     case STATUS_TRUST_UNDEFINED: s = "TRUST_UNDEFINED"; break;
456     case STATUS_TRUST_NEVER      : s = "TRUST_NEVER"; break;
457     case STATUS_TRUST_MARGINAL : s = "TRUST_MARGINAL"; break;
458     case STATUS_TRUST_FULLY      : s = "TRUST_FULLY"; break;
459     case STATUS_TRUST_ULTIMATE : s = "TRUST_ULTIMATE"; break;
460     case STATUS_GET_BOOL         : s = "GET_BOOL"; break;
461     case STATUS_GET_LINE         : s = "GET_LINE"; break;
462     case STATUS_GET_HIDDEN       : s = "GET_HIDDEN"; break;
463     case STATUS_GOT_IT   : s = "GOT_IT"; break;
464     case STATUS_SHM_INFO         : s = "SHM_INFO"; break;
465     case STATUS_SHM_GET  : s = "SHM_GET"; break;
466     case STATUS_SHM_GET_BOOL     : s = "SHM_GET_BOOL"; break;
467     case STATUS_SHM_GET_HIDDEN : s = "SHM_GET_HIDDEN"; break;
468     case STATUS_NEED_PASSPHRASE: s = "NEED_PASSPHRASE"; break;
469     case STATUS_VALIDSIG         : s = "VALIDSIG"; break;
470     case STATUS_SIG_ID   : s = "SIG_ID"; break;
471     case STATUS_ENC_TO   : s = "ENC_TO"; break;
472     case STATUS_NODATA   : s = "NODATA"; break;
473     case STATUS_BAD_PASSPHRASE : s = "BAD_PASSPHRASE"; break;
474     case STATUS_NO_PUBKEY        : s = "NO_PUBKEY"; break;
475     case STATUS_NO_SECKEY        : s = "NO_SECKEY"; break;
476     case STATUS_NEED_PASSPHRASE_SYM: s = "NEED_PASSPHRASE_SYM"; break;
477     case STATUS_DECRYPTION_FAILED: s = "DECRYPTION_FAILED"; break;
478     case STATUS_DECRYPTION_OKAY: s = "DECRYPTION_OKAY"; break;
479     case STATUS_MISSING_PASSPHRASE: s = "MISSING_PASSPHRASE"; break;
480     case STATUS_GOOD_PASSPHRASE : s = "GOOD_PASSPHRASE"; break;
481     case STATUS_GOODMDC  : s = "GOODMDC"; break;
482     case STATUS_BADMDC   : s = "BADMDC"; break;
483     case STATUS_ERRMDC   : s = "ERRMDC"; break;
484     case STATUS_IMPORTED         : s = "IMPORTED"; break;
485     case STATUS_IMPORT_RES       : s = "IMPORT_RES"; break;
486     case STATUS_FILE_START       : s = "FILE_START"; break;
487     case STATUS_FILE_DONE        : s = "FILE_DONE"; break;
488     case STATUS_FILE_ERROR       : s = "FILE_ERROR"; break;
489     case STATUS_BEGIN_DECRYPTION:s = "BEGIN_DECRYPTION"; break;
490     case STATUS_END_DECRYPTION : s = "END_DECRYPTION"; break;
491     case STATUS_BEGIN_ENCRYPTION:s = "BEGIN_ENCRYPTION"; break;
492     case STATUS_END_ENCRYPTION : s = "END_ENCRYPTION"; break;
493     case STATUS_DELETE_PROBLEM : s = "DELETE_PROBLEM"; break;
494     case STATUS_PROGRESS         : s = "PROGRESS"; break;
495     case STATUS_SIG_CREATED      : s = "SIG_CREATED"; break;
496     case STATUS_SESSION_KEY      : s = "SESSION_KEY"; break;
497     case STATUS_NOTATION_NAME  : s = "NOTATION_NAME" ; break;
498     case STATUS_NOTATION_DATA  : s = "NOTATION_DATA" ; break;
499     case STATUS_POLICY_URL     : s = "POLICY_URL" ; break;
500     case STATUS_BEGIN_STREAM   : s = "BEGIN_STREAM"; break;
501     case STATUS_END_STREAM     : s = "END_STREAM"; break;
502     case STATUS_KEY_CREATED    : s = "KEY_CREATED"; break;
503     case STATUS_UNEXPECTED     : s = "UNEXPECTED"; break;
504     case STATUS_INV_RECP       : s = "INV_RECP"; break;
505     case STATUS_NO_RECP        : s = "NO_RECP"; break;
506     case STATUS_ALREADY_SIGNED : s = "ALREADY_SIGNED"; break;
507     default: s = "?"; break;
508     }
509   return s;
510 }
511
512
513
514 void
515 gpgsm_status (CTRL ctrl, int no, const char *text)
516 {
517   if (ctrl->no_server)
518     {
519       if (ctrl->status_fd == -1)
520         return; /* no status wanted */
521       if (!statusfp)
522         {
523           if (ctrl->status_fd == 1)
524             statusfp = stdout;
525           else if (ctrl->status_fd == 2)
526             statusfp = stderr;
527           else
528             statusfp = fdopen (ctrl->status_fd, "w");
529       
530           if (!statusfp)
531             {
532               log_fatal ("can't open fd %d for status output: %s\n",
533                          ctrl->status_fd, strerror(errno));
534             }
535         }
536       
537       fputs ("[GNUPG:] ", statusfp);
538       fputs (get_status_string (no), statusfp);
539     
540       if (text)
541         {
542           putc ( ' ', statusfp );
543           for (; *text; text++) 
544             {
545               if (*text == '\n')
546                 fputs ( "\\n", statusfp );
547               else if (*text == '\r')
548                 fputs ( "\\r", statusfp );
549               else 
550                 putc ( *(const byte *)text,  statusfp );
551             }
552         }
553       putc ('\n', statusfp);
554       fflush (statusfp);
555     }
556   else 
557     {
558       ASSUAN_CONTEXT ctx = ctrl->server_local->assuan_ctx;
559
560       assuan_write_status (ctx, get_status_string (no), text);
561     }
562 }
563
564
565 #if 0
566 /*
567  * Write a status line with a buffer using %XX escapes.  If WRAP is >
568  * 0 wrap the line after this length.  If STRING is not NULL it will
569  * be prepended to the buffer, no escaping is done for string.
570  * A wrap of -1 forces spaces not to be encoded as %20.
571  */
572 void
573 write_status_text_and_buffer ( int no, const char *string,
574                                const char *buffer, size_t len, int wrap )
575 {
576     const char *s, *text;
577     int esc, first;
578     int lower_limit = ' ';
579     size_t n, count, dowrap;
580
581     if( !statusfp )
582         return;  /* not enabled */
583     
584     if (wrap == -1) {
585         lower_limit--;
586         wrap = 0;
587     }
588
589     text = get_status_string (no);
590     count = dowrap = first = 1;
591     do {
592         if (dowrap) {
593             fprintf (statusfp, "[GNUPG:] %s ", text );
594             count = dowrap = 0;
595             if (first && string) {
596                 fputs (string, statusfp);
597                 count += strlen (string);
598             }
599             first = 0;
600         }
601         for (esc=0, s=buffer, n=len; n && !esc; s++, n-- ) {
602             if ( *s == '%' || *(const byte*)s <= lower_limit 
603                            || *(const byte*)s == 127 ) 
604                 esc = 1;
605             if ( wrap && ++count > wrap ) {
606                 dowrap=1;
607                 break;
608             }
609         }
610         if (esc) {
611             s--; n++;
612         }
613         if (s != buffer) 
614             fwrite (buffer, s-buffer, 1, statusfp );
615         if ( esc ) {
616             fprintf (statusfp, "%%%02X", *(const byte*)s );
617             s++; n--;
618         }
619         buffer = s;
620         len = n;
621         if ( dowrap && len )
622             putc ( '\n', statusfp );
623     } while ( len );
624
625     putc ('\n',statusfp);
626     fflush (statusfp);
627 }
628 #endif
629
630
631
632
633
634
635