Friedman is setting up his desk
[gnupg.git] / agent / command.c
1 /* command.c - gpg-agent command handler
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 /* FIXME: we should not use the default assuan buffering but setup
22    some buffering in secure mempory to protect session keys etc. */
23
24 #include <config.h>
25 #include <errno.h>
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <ctype.h>
30 #include <unistd.h>
31
32 #include "agent.h"
33 #include "../assuan/assuan.h"
34
35 #define set_error(e,t) assuan_set_error (ctx, ASSUAN_ ## e, (t))
36 #define digitp(a) ((a) >= '0' && (a) <= '9')
37 #define hexdigitp(a) (digitp (a)                     \
38                       || ((a) >= 'A' && (a) <= 'F')  \
39                       || ((a) >= 'a' && (a) <= 'f'))
40 #define atoi_1(p)   (*(p) - '0' )
41 #define atoi_2(p)   ((atoi_1(p) * 10) + atoi_1((p)+1))
42 /* assumes ASCII and pre-checked values */
43 #define xtoi_1(p)   (*(p) <= '9'? (*(p)- '0'): \
44                      *(p) <= 'F'? (*(p)-'A'+10):(*(p)-'a'+10))
45 #define xtoi_2(p)   ((xtoi_1(p) * 16) + xtoi_1((p)+1))
46
47 /* Data used to associate an Assuan context with local server data */
48 struct server_local_s {
49   ASSUAN_CONTEXT assuan_ctx;
50   int message_fd;
51 };
52
53 /* SETHASH <hexstring>
54
55   The client can use this command to tell the server about the data
56   (which usually is a hash) to be signed. */
57 static int
58 cmd_sethash (ASSUAN_CONTEXT ctx, char *line)
59 {
60   int n;
61   char *p;
62   CTRL ctrl = assuan_get_pointer (ctx);
63   unsigned char *buf;
64
65   /* parse the hash value */
66   for (p=line,n=0; hexdigitp (*p); p++, n++)
67     ;
68   if (*p)
69     return set_error (Parameter_Error, "invalid hexstring");
70   if ((n&1))
71     return set_error (Parameter_Error, "odd number of digits");
72   n /= 2;
73   if (n != 16 && n != 20 && n != 24 && n != 32)
74     return set_error (Parameter_Error, "unsupported length of hash");
75   if (n > MAX_DIGEST_LEN)
76     return set_error (Parameter_Error, "hash value to long");
77
78   buf = ctrl->digest.value;
79   ctrl->digest.valuelen = n;
80   for (p=line, n=0; n < ctrl->digest.valuelen; p += 2, n++)
81     buf[n] = xtoi_2 (p);
82   for (; n < ctrl->digest.valuelen; n++)
83     buf[n] = 0;
84   return 0;
85 }
86
87
88 /* PKSIGN <options>
89
90    Perform the actual sign operation. Neither input nor output are
91    sensitive to to eavesdropping */
92 static int
93 cmd_pksign (ASSUAN_CONTEXT ctx, char *line)
94 {
95   int rc;
96   CTRL ctrl = assuan_get_pointer (ctx);
97
98   /* fixme: check that all required data is available */
99   rc = agent_pksign (ctrl, assuan_get_data_fp (ctx));
100   /* fixme: return an error */
101   return 0;
102 }
103
104
105 /* MESSAGE FD=<n>
106
107    Set the file descriptor to read a message which is used with
108    detached signatures */
109 static int 
110 cmd_message (ASSUAN_CONTEXT ctx, char *line)
111 {
112 #if 0
113   char *endp;
114   int fd;
115   CTRL ctrl = assuan_get_pointer (ctx);
116
117   if (strncmp (line, "FD=", 3))
118     return set_error (Syntax_Error, "FD=<n> expected");
119   line += 3;
120   if (!digitp (*line))
121     return set_error (Syntax_Error, "number required");
122   fd = strtoul (line, &endp, 10);
123   if (*endp)
124     return set_error (Syntax_Error, "garbage found");
125   if (fd == -1)
126     return set_error (No_Input, NULL);
127
128   ctrl->server_local->message_fd = fd;
129   return 0;
130 #endif
131   return set_error (Not_Implemented, NULL);
132 }
133
134
135 \f
136 /* Tell the assuan library about our commands */
137 static int
138 register_commands (ASSUAN_CONTEXT ctx)
139 {
140   static struct {
141     const char *name;
142     int cmd_id;
143     int (*handler)(ASSUAN_CONTEXT, char *line);
144   } table[] = {
145     { "SETHASH",    0,  cmd_sethash },
146     { "PKSIGN",     0,  cmd_pksign },
147     { "",     ASSUAN_CMD_INPUT, NULL }, 
148     { "",     ASSUAN_CMD_OUTPUT, NULL }, 
149     { "MESSAGE",    0,  cmd_message },
150     { NULL }
151   };
152   int i, j, rc;
153
154   for (i=j=0; table[i].name; i++)
155     {
156       rc = assuan_register_command (ctx,
157                                     table[i].cmd_id? table[i].cmd_id
158                                                    : (ASSUAN_CMD_USER + j++),
159                                     table[i].name, table[i].handler);
160       if (rc)
161         return rc;
162     } 
163   return 0;
164 }
165
166
167 /* Startup the server */
168 void
169 start_command_handler (void)
170 {
171   int rc;
172   int filedes[2];
173   ASSUAN_CONTEXT ctx;
174   struct server_control_s ctrl;
175
176   memset (&ctrl, 0, sizeof ctrl);
177
178   /* For now we use a simple pipe based server so that we can work
179      from scripts.  We will later add options to run as a daemon and
180      wait for requests on a Unix domain socket */
181   filedes[0] = 0;
182   filedes[1] = 1;
183   rc = assuan_init_pipe_server (&ctx, filedes);
184   if (rc)
185     {
186       log_error ("failed to initialize the server: %s\n",
187                  assuan_strerror(rc));
188       agent_exit (2);
189     }
190   rc = register_commands (ctx);
191   if (rc)
192     {
193       log_error ("failed to the register commands with Assuan: %s\n",
194                  assuan_strerror(rc));
195       agent_exit (2);
196     }
197
198   assuan_set_pointer (ctx, &ctrl);
199   ctrl.server_local = xcalloc (1, sizeof *ctrl.server_local);
200   ctrl.server_local->assuan_ctx = ctx;
201   ctrl.server_local->message_fd = -1;
202
203   log_info ("Assuan started\n");
204   for (;;)
205     {
206       rc = assuan_accept (ctx);
207       if (rc == -1)
208         {
209           log_info ("Assuan terminated\n");
210           break;
211         }
212       else if (rc)
213         {
214           log_info ("Assuan accept problem: %s\n", assuan_strerror (rc));
215           break;
216         }
217       
218       rc = assuan_process (ctx);
219       if (rc)
220         {
221           log_info ("Assuan processing failed: %s\n", assuan_strerror (rc));
222           continue;
223         }
224     }
225
226
227   assuan_deinit_pipe_server (ctx);
228 }
229