Started to code a --server mode.
[gnupg.git] / g10 / server.c
1 /* server.c - server mode for gpg
2  * Copyright (C) 2006  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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
19  * USA.
20  */
21
22 #include <config.h>
23 #include <errno.h>
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <stdarg.h>
28 #include <ctype.h>
29 #include <unistd.h>
30
31 #include <assuan.h>
32
33 #include "gpg.h"
34 #include "util.h"
35 #include "i18n.h"
36 #include "options.h"
37
38
39
40 #define set_error(e,t) assuan_set_error (ctx, gpg_error (e), (t))
41
42
43 /* Data used to associate an Assuan context with local server data.  */
44 struct server_local_s 
45 {
46   /* Our current Assuan context. */
47   assuan_context_t assuan_ctx;  
48   /* File descriptor as set by the MESSAGE command. */
49   int message_fd;               
50 };
51
52
53 \f
54 /* Helper to close the message fd if it is open. */
55 static void 
56 close_message_fd (ctrl_t ctrl)
57 {
58   if (ctrl->server_local->message_fd != -1)
59     {
60       close (ctrl->server_local->message_fd);
61       ctrl->server_local->message_fd = -1;
62     } 
63 }
64
65
66 \f
67 /* Called by libassuan for Assuan options.  See the Assuan manual for
68    details. */
69 static int
70 option_handler (assuan_context_t ctx, const char *key, const char *value)
71 {
72 /*   ctrl_t ctrl = assuan_get_pointer (ctx); */
73
74   /* Fixme: Implement the tty and locale args. */
75   if (!strcmp (key, "display"))
76     {
77     }
78   else if (!strcmp (key, "ttyname"))
79     {
80     }
81   else if (!strcmp (key, "ttytype"))
82     {
83     }
84   else if (!strcmp (key, "lc-ctype"))
85     {
86     }
87   else if (!strcmp (key, "lc-messages"))
88     {
89     }
90   else if (!strcmp (key, "list-mode"))
91     {
92       /* This is for now a dummy option. */
93     }
94   else
95     return gpg_error (GPG_ERR_UNKNOWN_OPTION);
96
97   return 0;
98 }
99
100
101 /* Called by libassuan for RESET commands. */
102 static void
103 reset_notify (assuan_context_t ctx)
104 {
105   ctrl_t ctrl = assuan_get_pointer (ctx);
106
107   close_message_fd (ctrl);
108   assuan_close_input_fd (ctx);
109   assuan_close_output_fd (ctx);
110 }
111
112
113 /* Called by libassuan for INPUT commands. */
114 static void
115 input_notify (assuan_context_t ctx, const char *line)
116 {
117 /*   ctrl_t ctrl = assuan_get_pointer (ctx); */
118
119   if (strstr (line, "--armor"))
120     ; /* FIXME */
121   else if (strstr (line, "--base64"))
122     ; /* FIXME */
123   else if (strstr (line, "--binary"))
124     ;
125   else
126     ; /* FIXME (autodetect encoding) */
127 }
128
129
130 /* Called by libassuan for OUTPUT commands. */
131 static void
132 output_notify (assuan_context_t ctx, const char *line)
133 {
134 /*   ctrl_t ctrl = assuan_get_pointer (ctx); */
135
136   if (strstr (line, "--armor"))
137     ; /* FIXME */
138   else if (strstr (line, "--base64"))
139     ; /* FIXME */
140 }
141
142
143
144 \f
145 /*  RECIPIENT <userID>
146
147    Set the recipient for the encryption.  <userID> should be the
148    internal representation of the key; the server may accept any other
149    way of specification.  If this is a valid and trusted recipient the
150    server does respond with OK, otherwise the return is an ERR with
151    the reason why the recipient can't be used, the encryption will
152    then not be done for this recipient.  If the policy is not to
153    encrypt at all if not all recipients are valid, the client has to
154    take care of this.  All RECIPIENT commands are cumulative until a
155    RESET or an successful ENCRYPT command.  */
156 static int 
157 cmd_recipient (assuan_context_t ctx, char *line)
158 {
159   return gpg_error (GPG_ERR_NOT_SUPPORTED);
160 }
161
162
163 \f
164 /*  SIGNER <userID>
165
166    Set the signer's keys for the signature creation.  <userID> should
167    be the internal representation of the key; the server may accept
168    any other way of specification.  If this is a valid and usable
169    signing key the server does respond with OK, otherwise it returns
170    an ERR with the reason why the key can't be used, the signing will
171    then not be done for this key.  If the policy is not to sign at all
172    if not all signer keys are valid, the client has to take care of
173    this.  All SIGNER commands are cumulative until a RESET but they
174    are *not* reset by an SIGN command becuase it can be expected that
175    set of signers are used for more than one sign operation.
176
177    Note that this command returns an INV_RECP status which is a bit
178    strange, but they are very similar.  */
179 static int 
180 cmd_signer (assuan_context_t ctx, char *line)
181 {
182   return gpg_error (GPG_ERR_NOT_SUPPORTED);
183 }
184
185
186 \f
187 /*  ENCRYPT 
188
189    Do the actual encryption process.  Takes the plaintext from the
190    INPUT command, writes to the ciphertext to the file descriptor set
191    with the OUTPUT command, take the recipients form all the
192    recipients set so far.  If this command fails the clients should
193    try to delete all output currently done or otherwise mark it as
194    invalid.  GPG does ensure that there won't be any security problem
195    with leftover data on the output in this case.
196
197    This command should in general not fail, as all necessary checks
198    have been done while setting the recipients.  The input and output
199    pipes are closed.  */
200 static int 
201 cmd_encrypt (assuan_context_t ctx, char *line)
202 {
203   return gpg_error (GPG_ERR_NOT_SUPPORTED);
204 }
205
206
207 \f
208 /*  DECRYPT
209
210    This performs the decrypt operation after doing some checks on the
211    internal state (e.g. that only needed data has been set).   */
212 static int 
213 cmd_decrypt (assuan_context_t ctx, char *line)
214 {
215   return gpg_error (GPG_ERR_NOT_SUPPORTED);
216 }
217
218
219 \f
220 /*  VERIFY
221
222    This does a verify operation on the message send to the input-FD.
223    The result is written out using status lines.  If an output FD was
224    given, the signed text will be written to that.
225   
226    If the signature is a detached one, the server will inquire about
227    the signed material and the client must provide it.
228  */
229 static int 
230 cmd_verify (assuan_context_t ctx, char *line)
231 {
232   int rc;
233   ctrl_t ctrl = assuan_get_pointer (ctx);
234   int fd = assuan_get_input_fd (ctx);
235   int out_fd = assuan_get_output_fd (ctx);
236   FILE *out_fp = NULL;
237
238   if (fd == -1)
239     return gpg_error (GPG_ERR_ASS_NO_INPUT);
240
241   if (out_fd != -1)
242     {
243       out_fp = fdopen ( dup(out_fd), "w");
244       if (!out_fp)
245         return set_error (GPG_ERR_ASS_GENERAL, "fdopen() failed");
246     }
247
248   log_debug ("WARNING: The server mode work in progress and not ready for use\n");
249
250   /* Need to dup it because it might get closed and libassuan won't
251      know about it then. */
252   rc = gpg_verify (ctrl,
253                    dup (fd), 
254                    dup (ctrl->server_local->message_fd),
255                    out_fp);
256
257   if (out_fp)
258     fclose (out_fp);
259   close_message_fd (ctrl);
260   assuan_close_input_fd (ctx);
261   assuan_close_output_fd (ctx);
262
263   return rc;
264 }
265
266
267 \f
268 /*  SIGN [--detached]
269
270    Sign the data set with the INPUT command and write it to the sink
271    set by OUTPUT.  With "--detached" specified, a detached signature
272    is created.  */
273 static int 
274 cmd_sign (assuan_context_t ctx, char *line)
275 {
276   return gpg_error (GPG_ERR_NOT_SUPPORTED);
277 }
278
279
280 \f
281 /*  IMPORT
282
283   Import keys as read from the input-fd, return status message for
284   each imported one.  The import checks the validity of the key.  */
285 static int 
286 cmd_import (assuan_context_t ctx, char *line)
287 {
288   return gpg_error (GPG_ERR_NOT_SUPPORTED);
289 }
290
291
292 \f
293 /*  EXPORT [--data [--armor|--base64]] [--] pattern
294
295    Similar to the --export command line command, this command exports
296    public keys matching PATTERN.  The output is send to the output fd
297    unless the --data option has been used in which case the output
298    gets send inline using regular data lines.  The options "--armor"
299    and "--base" ospecify an output format if "--data" has been used.
300    Recall that in general the output format is set with the OUTPUT
301    command.
302  */
303 static int 
304 cmd_export (assuan_context_t ctx, char *line)
305 {
306   return gpg_error (GPG_ERR_NOT_SUPPORTED);
307 }
308
309
310 \f
311 /*  DELKEYS
312
313     Fixme
314 */
315 static int 
316 cmd_delkeys (assuan_context_t ctx, char *line)
317 {
318   return gpg_error (GPG_ERR_NOT_SUPPORTED);
319 }
320
321
322 \f
323 /*  MESSAGE FD[=<n>]
324
325    Set the file descriptor to read a message which is used with
326    detached signatures.  */
327 static int 
328 cmd_message (assuan_context_t ctx, char *line)
329 {
330   int rc;
331   int fd;
332   ctrl_t ctrl = assuan_get_pointer (ctx);
333
334   rc = assuan_command_parse_fd (ctx, line, &fd);
335   if (rc)
336     return rc;
337   if (fd == -1)
338     return gpg_error (GPG_ERR_ASS_NO_INPUT);
339   ctrl->server_local->message_fd = fd;
340   return 0;
341 }
342
343
344 \f
345 /* LISTKEYS [<patterns>]
346    LISTSECRETKEYS [<patterns>]
347
348    fixme
349 */
350 static int 
351 do_listkeys (assuan_context_t ctx, char *line, int mode)
352 {
353   return gpg_error (GPG_ERR_NOT_SUPPORTED);
354 }
355
356
357 static int 
358 cmd_listkeys (assuan_context_t ctx, char *line)
359 {
360   return do_listkeys (ctx, line, 3);
361 }
362
363
364 static int 
365 cmd_listsecretkeys (assuan_context_t ctx, char *line)
366 {
367   return do_listkeys (ctx, line, 2);
368 }
369
370
371 \f
372 /* GENKEY
373
374    Read the parameters in native format from the input fd and create a
375    new OpenPGP key.
376  */
377 static int 
378 cmd_genkey (assuan_context_t ctx, char *line)
379 {
380   return gpg_error (GPG_ERR_NOT_SUPPORTED);
381 }
382
383
384
385
386
387 \f
388 /* Helper to register our commands with libassuan. */
389 static int
390 register_commands (assuan_context_t ctx)
391 {
392   static struct 
393   {
394     const char *name;
395     int (*handler)(assuan_context_t, char *line);
396   } table[] = {
397     { "RECIPIENT",     cmd_recipient },
398     { "SIGNER",        cmd_signer    },
399     { "ENCRYPT",       cmd_encrypt   },
400     { "DECRYPT",       cmd_decrypt   },
401     { "VERIFY",        cmd_verify    },
402     { "SIGN",          cmd_sign      },
403     { "IMPORT",        cmd_import    },
404     { "EXPORT",        cmd_export    },
405     { "INPUT",         NULL          }, 
406     { "OUTPUT",        NULL          }, 
407     { "MESSAGE",       cmd_message   },
408     { "LISTKEYS",      cmd_listkeys  },
409     { "LISTSECRETKEYS",cmd_listsecretkeys },
410     { "GENKEY",        cmd_genkey    },
411     { "DELKEYS",       cmd_delkeys   },
412     { NULL }
413   };
414   int i, rc;
415
416   for (i=0; table[i].name; i++)
417     {
418       rc = assuan_register_command (ctx, table[i].name, table[i].handler);
419       if (rc)
420         return rc;
421     } 
422   return 0;
423 }
424
425
426
427 \f
428 /* Startup the server.  CTRL must have been allocated by the caller
429    and set to the default values. */
430 int
431 gpg_server (ctrl_t ctrl)
432 {
433   int rc;
434   int filedes[2];
435   assuan_context_t ctx;
436   static const char hello[] = ("GNU Privacy Guard's OpenPGP server "
437                                VERSION " ready");
438
439   /* We use a pipe based server so that we can work from scripts.
440      assuan_init_pipe_server will automagically detect when we are
441      called with a socketpair and ignore FILEDES in this case.  */
442   filedes[0] = 0;
443   filedes[1] = 1;
444   rc = assuan_init_pipe_server (&ctx, filedes);
445   if (rc)
446     {
447       log_error ("failed to initialize the server: %s\n", gpg_strerror (rc));
448       goto leave;
449     }
450
451   rc = register_commands (ctx);
452   if (rc)
453     {
454       log_error ("failed to the register commands with Assuan: %s\n",
455                  gpg_strerror(rc));
456       goto leave;
457     }
458
459   assuan_set_pointer (ctx, ctrl);
460   if (opt.verbose || opt.debug)
461     {
462       char *tmp = NULL;
463       const char *s1 = getenv ("GPG_AGENT_INFO");
464
465       if (asprintf (&tmp,
466                     "Home: %s\n"
467                     "Config: %s\n"
468                     "AgentInfo: %s\n"
469                     "%s",
470                     opt.homedir,
471                     "fixme: need config filename",
472                     s1?s1:"[not set]",
473                     hello) > 0)
474         {
475           assuan_set_hello_line (ctx, tmp);
476           free (tmp);
477         }
478     }
479   else
480     assuan_set_hello_line (ctx, hello);
481   assuan_register_reset_notify (ctx, reset_notify);
482   assuan_register_input_notify (ctx, input_notify);
483   assuan_register_output_notify (ctx, output_notify);
484   assuan_register_option_handler (ctx, option_handler);
485
486   ctrl->server_local = xtrycalloc (1, sizeof *ctrl->server_local);
487   if (!ctrl->server_local)
488     {
489       rc = gpg_error_from_syserror ();
490       goto leave;
491     }
492   ctrl->server_local->assuan_ctx = ctx;
493   ctrl->server_local->message_fd = -1;
494
495   if (DBG_ASSUAN)
496     assuan_set_log_stream (ctx, log_get_stream ());
497
498   for (;;)
499     {
500       rc = assuan_accept (ctx);
501       if (rc == -1)
502         {
503           rc = 0;
504           break;
505         }
506       else if (rc)
507         {
508           log_info ("Assuan accept problem: %s\n", gpg_strerror (rc));
509           break;
510         }
511       
512       rc = assuan_process (ctx);
513       if (rc)
514         {
515           log_info ("Assuan processing failed: %s\n", gpg_strerror (rc));
516           continue;
517         }
518     }
519
520  leave:
521   xfree (ctrl->server_local);
522   ctrl->server_local = NULL;
523   assuan_deinit_server (ctx);
524   return rc;
525 }
526