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