Signing does now work. There is no secret key management yet, so you
[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 [--detached]
132
133   Sign the data set with the INPUT command and write it to the sink
134   set by OUTPUT.  with "--detached" specified, a detached signature is
135   created (surprise).  */
136 static int 
137 cmd_sign (ASSUAN_CONTEXT ctx, char *line)
138 {
139   int inp_fd, out_fd;
140   FILE *out_fp;
141   int detached;
142
143   inp_fd = assuan_get_input_fd (ctx);
144   if (inp_fd == -1)
145     return set_error (No_Input, NULL);
146   out_fd = assuan_get_output_fd (ctx);
147   if (out_fd == -1)
148     return set_error (No_Output, NULL);
149
150   detached = !!strstr (line, "--detached");  /* fixme: this is ambiguous */
151
152   out_fp = fdopen ( dup(out_fd), "w");
153   if (!out_fp)
154     return set_error (General_Error, "fdopen() failed");
155   gpgsm_sign (assuan_get_pointer (ctx), inp_fd, detached, out_fp);
156   fclose (out_fp);
157
158   return 0;
159 }
160
161
162 /* IMPORT
163
164   Import the certificates read form the input-fd, return status
165   message for each imported one.  The import checks the validity of
166   the certificate but not of the path.  It is possible to import
167   expired certificates.  */
168 static int 
169 cmd_import (ASSUAN_CONTEXT ctx, char *line)
170 {
171   int fd = assuan_get_input_fd (ctx);
172
173   if (fd == -1)
174     return set_error (No_Input, NULL);
175
176   gpgsm_import (assuan_get_pointer (ctx), fd);
177
178   return 0;
179 }
180
181 /* MESSAGE FD=<n>
182
183    Set the file descriptor to read a message which is used with
184    detached signatures */
185 static int 
186 cmd_message (ASSUAN_CONTEXT ctx, char *line)
187 {
188   char *endp;
189   int fd;
190   CTRL ctrl = assuan_get_pointer (ctx);
191
192   if (strncmp (line, "FD=", 3))
193     return set_error (Syntax_Error, "FD=<n> expected");
194   line += 3;
195   if (!digitp (*line))
196     return set_error (Syntax_Error, "number required");
197   fd = strtoul (line, &endp, 10);
198   if (*endp)
199     return set_error (Syntax_Error, "garbage found");
200   if (fd == -1)
201     return set_error (No_Input, NULL);
202
203   ctrl->server_local->message_fd = fd;
204   return 0;
205 }
206
207 static int 
208 cmd_listkeys (ASSUAN_CONTEXT ctx, char *line)
209 {
210   CTRL ctrl = assuan_get_pointer (ctx);
211
212   ctrl->with_colons = 1;
213   /* fixme: check that the returned data_fp is not NULL */
214   gpgsm_list_keys (assuan_get_pointer (ctx), NULL, assuan_get_data_fp (ctx));
215
216   return 0;
217 }
218
219
220
221
222 \f
223 /* Tell the assuan library about our commands */
224 static int
225 register_commands (ASSUAN_CONTEXT ctx)
226 {
227   static struct {
228     const char *name;
229     int cmd_id;
230     int (*handler)(ASSUAN_CONTEXT, char *line);
231   } table[] = {
232     { "RECIPIENT",  0,  cmd_recipient },
233     { "ENCRYPT",    0,  cmd_encrypt },
234     { "DECRYPT",    0,  cmd_decrypt },
235     { "VERIFY",     0,  cmd_verify },
236     { "SIGN",       0,  cmd_sign },
237     { "IMPORT",     0,  cmd_import },
238     { "",     ASSUAN_CMD_INPUT, NULL }, 
239     { "",     ASSUAN_CMD_OUTPUT, NULL }, 
240     { "MESSAGE",    0,  cmd_message },
241     { "LISTKEYS",   0,  cmd_listkeys },
242     { NULL }
243   };
244   int i, j, rc;
245
246   for (i=j=0; table[i].name; i++)
247     {
248       rc = assuan_register_command (ctx,
249                                     table[i].cmd_id? table[i].cmd_id
250                                                    : (ASSUAN_CMD_USER + j++),
251                                     table[i].name, table[i].handler);
252       if (rc)
253         return rc;
254     } 
255   return 0;
256 }
257
258 /* Startup the server */
259 void
260 gpgsm_server (void)
261 {
262   int rc;
263   int filedes[2];
264   ASSUAN_CONTEXT ctx;
265   struct server_control_s ctrl;
266
267   memset (&ctrl, 0, sizeof ctrl);
268
269   /* For now we use a simple pipe based server so that we can work
270      from scripts.  We will later add options to run as a daemon and
271      wait for requests on a Unix domain socket */
272   filedes[0] = 0;
273   filedes[1] = 1;
274   rc = assuan_init_pipe_server (&ctx, filedes);
275   if (rc)
276     {
277       log_error ("failed to initialize the server: %s\n",
278                  assuan_strerror(rc));
279       gpgsm_exit (2);
280     }
281   rc = register_commands (ctx);
282   if (rc)
283     {
284       log_error ("failed to the register commands with Assuan: %s\n",
285                  assuan_strerror(rc));
286       gpgsm_exit (2);
287     }
288
289   assuan_set_pointer (ctx, &ctrl);
290   ctrl.server_local = xcalloc (1, sizeof *ctrl.server_local);
291   ctrl.server_local->assuan_ctx = ctx;
292   ctrl.server_local->message_fd = -1;
293
294   log_info ("Assuan started\n");
295   for (;;)
296     {
297       rc = assuan_accept (ctx);
298       if (rc == -1)
299         {
300           log_info ("Assuan terminated\n");
301           break;
302         }
303       else if (rc)
304         {
305           log_info ("Assuan accept problem: %s\n", assuan_strerror (rc));
306           break;
307         }
308       
309       rc = assuan_process (ctx);
310       if (rc)
311         {
312           log_info ("Assuan processing failed: %s\n", assuan_strerror (rc));
313           continue;
314         }
315     }
316
317
318   assuan_deinit_pipe_server (ctx);
319 }
320
321
322 static const char *
323 get_status_string ( int no ) 
324 {
325   const char *s;
326
327   switch (no)
328     {
329     case STATUS_ENTER  : s = "ENTER"; break;
330     case STATUS_LEAVE  : s = "LEAVE"; break;
331     case STATUS_ABORT  : s = "ABORT"; break;
332     case STATUS_GOODSIG: s = "GOODSIG"; break;
333     case STATUS_SIGEXPIRED: s = "SIGEXPIRED"; break;
334     case STATUS_KEYREVOKED: s = "KEYREVOKED"; break;
335     case STATUS_BADSIG : s = "BADSIG"; break;
336     case STATUS_ERRSIG : s = "ERRSIG"; break;
337     case STATUS_BADARMOR : s = "BADARMOR"; break;
338     case STATUS_RSA_OR_IDEA : s= "RSA_OR_IDEA"; break;
339     case STATUS_TRUST_UNDEFINED: s = "TRUST_UNDEFINED"; break;
340     case STATUS_TRUST_NEVER      : s = "TRUST_NEVER"; break;
341     case STATUS_TRUST_MARGINAL : s = "TRUST_MARGINAL"; break;
342     case STATUS_TRUST_FULLY      : s = "TRUST_FULLY"; break;
343     case STATUS_TRUST_ULTIMATE : s = "TRUST_ULTIMATE"; break;
344     case STATUS_GET_BOOL         : s = "GET_BOOL"; break;
345     case STATUS_GET_LINE         : s = "GET_LINE"; break;
346     case STATUS_GET_HIDDEN       : s = "GET_HIDDEN"; break;
347     case STATUS_GOT_IT   : s = "GOT_IT"; break;
348     case STATUS_SHM_INFO         : s = "SHM_INFO"; break;
349     case STATUS_SHM_GET  : s = "SHM_GET"; break;
350     case STATUS_SHM_GET_BOOL     : s = "SHM_GET_BOOL"; break;
351     case STATUS_SHM_GET_HIDDEN : s = "SHM_GET_HIDDEN"; break;
352     case STATUS_NEED_PASSPHRASE: s = "NEED_PASSPHRASE"; break;
353     case STATUS_VALIDSIG         : s = "VALIDSIG"; break;
354     case STATUS_SIG_ID   : s = "SIG_ID"; break;
355     case STATUS_ENC_TO   : s = "ENC_TO"; break;
356     case STATUS_NODATA   : s = "NODATA"; break;
357     case STATUS_BAD_PASSPHRASE : s = "BAD_PASSPHRASE"; break;
358     case STATUS_NO_PUBKEY        : s = "NO_PUBKEY"; break;
359     case STATUS_NO_SECKEY        : s = "NO_SECKEY"; break;
360     case STATUS_NEED_PASSPHRASE_SYM: s = "NEED_PASSPHRASE_SYM"; break;
361     case STATUS_DECRYPTION_FAILED: s = "DECRYPTION_FAILED"; break;
362     case STATUS_DECRYPTION_OKAY: s = "DECRYPTION_OKAY"; break;
363     case STATUS_MISSING_PASSPHRASE: s = "MISSING_PASSPHRASE"; break;
364     case STATUS_GOOD_PASSPHRASE : s = "GOOD_PASSPHRASE"; break;
365     case STATUS_GOODMDC  : s = "GOODMDC"; break;
366     case STATUS_BADMDC   : s = "BADMDC"; break;
367     case STATUS_ERRMDC   : s = "ERRMDC"; break;
368     case STATUS_IMPORTED         : s = "IMPORTED"; break;
369     case STATUS_IMPORT_RES       : s = "IMPORT_RES"; break;
370     case STATUS_FILE_START       : s = "FILE_START"; break;
371     case STATUS_FILE_DONE        : s = "FILE_DONE"; break;
372     case STATUS_FILE_ERROR       : s = "FILE_ERROR"; break;
373     case STATUS_BEGIN_DECRYPTION:s = "BEGIN_DECRYPTION"; break;
374     case STATUS_END_DECRYPTION : s = "END_DECRYPTION"; break;
375     case STATUS_BEGIN_ENCRYPTION:s = "BEGIN_ENCRYPTION"; break;
376     case STATUS_END_ENCRYPTION : s = "END_ENCRYPTION"; break;
377     case STATUS_DELETE_PROBLEM : s = "DELETE_PROBLEM"; break;
378     case STATUS_PROGRESS         : s = "PROGRESS"; break;
379     case STATUS_SIG_CREATED      : s = "SIG_CREATED"; break;
380     case STATUS_SESSION_KEY      : s = "SESSION_KEY"; break;
381     case STATUS_NOTATION_NAME  : s = "NOTATION_NAME" ; break;
382     case STATUS_NOTATION_DATA  : s = "NOTATION_DATA" ; break;
383     case STATUS_POLICY_URL     : s = "POLICY_URL" ; break;
384     case STATUS_BEGIN_STREAM   : s = "BEGIN_STREAM"; break;
385     case STATUS_END_STREAM     : s = "END_STREAM"; break;
386     case STATUS_KEY_CREATED    : s = "KEY_CREATED"; break;
387     case STATUS_UNEXPECTED     : s = "UNEXPECTED"; break;
388     case STATUS_INV_RECP       : s = "INV_RECP"; break;
389     case STATUS_NO_RECP        : s = "NO_RECP"; break;
390     case STATUS_ALREADY_SIGNED : s = "ALREADY_SIGNED"; break;
391     default: s = "?"; break;
392     }
393   return s;
394 }
395
396
397
398 void
399 gpgsm_status (CTRL ctrl, int no, const char *text)
400 {
401   if (ctrl->no_server)
402     {
403       if (ctrl->status_fd == -1)
404         return; /* no status wanted */
405       if (!statusfp)
406         {
407           if (ctrl->status_fd == 1)
408             statusfp = stdout;
409           else if (ctrl->status_fd == 2)
410             statusfp = stderr;
411           else
412             statusfp = fdopen (ctrl->status_fd, "w");
413       
414           if (!statusfp)
415             {
416               log_fatal ("can't open fd %d for status output: %s\n",
417                          ctrl->status_fd, strerror(errno));
418             }
419         }
420       
421       fputs ("[GNUPG:] ", statusfp);
422       fputs (get_status_string (no), statusfp);
423     
424       if (text)
425         {
426           putc ( ' ', statusfp );
427           for (; *text; text++) 
428             {
429               if (*text == '\n')
430                 fputs ( "\\n", statusfp );
431               else if (*text == '\r')
432                 fputs ( "\\r", statusfp );
433               else 
434                 putc ( *(const byte *)text,  statusfp );
435             }
436         }
437       putc ('\n', statusfp);
438       fflush (statusfp);
439     }
440   else 
441     {
442       ASSUAN_CONTEXT ctx = ctrl->server_local->assuan_ctx;
443
444       assuan_write_status (ctx, get_status_string (no), text);
445     }
446 }
447
448
449 #if 0
450 /*
451  * Write a status line with a buffer using %XX escapes.  If WRAP is >
452  * 0 wrap the line after this length.  If STRING is not NULL it will
453  * be prepended to the buffer, no escaping is done for string.
454  * A wrap of -1 forces spaces not to be encoded as %20.
455  */
456 void
457 write_status_text_and_buffer ( int no, const char *string,
458                                const char *buffer, size_t len, int wrap )
459 {
460     const char *s, *text;
461     int esc, first;
462     int lower_limit = ' ';
463     size_t n, count, dowrap;
464
465     if( !statusfp )
466         return;  /* not enabled */
467     
468     if (wrap == -1) {
469         lower_limit--;
470         wrap = 0;
471     }
472
473     text = get_status_string (no);
474     count = dowrap = first = 1;
475     do {
476         if (dowrap) {
477             fprintf (statusfp, "[GNUPG:] %s ", text );
478             count = dowrap = 0;
479             if (first && string) {
480                 fputs (string, statusfp);
481                 count += strlen (string);
482             }
483             first = 0;
484         }
485         for (esc=0, s=buffer, n=len; n && !esc; s++, n-- ) {
486             if ( *s == '%' || *(const byte*)s <= lower_limit 
487                            || *(const byte*)s == 127 ) 
488                 esc = 1;
489             if ( wrap && ++count > wrap ) {
490                 dowrap=1;
491                 break;
492             }
493         }
494         if (esc) {
495             s--; n++;
496         }
497         if (s != buffer) 
498             fwrite (buffer, s-buffer, 1, statusfp );
499         if ( esc ) {
500             fprintf (statusfp, "%%%02X", *(const byte*)s );
501             s++; n--;
502         }
503         buffer = s;
504         len = n;
505         if ( dowrap && len )
506             putc ( '\n', statusfp );
507     } while ( len );
508
509     putc ('\n',statusfp);
510     fflush (statusfp);
511 }
512 #endif
513
514
515
516
517
518
519