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