As promised a agent which already does some things. Not very useful
[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 #if MAX_DIGEST_LEN < 20
48 #error MAX_DIGEST_LEN shorter than keygrip
49 #endif
50
51 /* Data used to associate an Assuan context with local server data */
52 struct server_local_s {
53   ASSUAN_CONTEXT assuan_ctx;
54   int message_fd;
55 };
56
57
58 static void
59 reset_notify (ASSUAN_CONTEXT ctx)
60 {
61   CTRL ctrl = assuan_get_pointer (ctx);
62
63   memset (ctrl->keygrip, 0, 20);
64   ctrl->digest.valuelen = 0;
65 }
66
67 /* SIGKEY <hexstring_with_keygrip>
68
69    Set the  key used for a sign operation */
70 static int
71 cmd_sigkey (ASSUAN_CONTEXT ctx, char *line)
72 {
73   int n;
74   char *p;
75   CTRL ctrl = assuan_get_pointer (ctx);
76   unsigned char *buf;
77
78   /* parse the hash value */
79   for (p=line,n=0; hexdigitp (*p); p++, n++)
80     ;
81   if (*p)
82     return set_error (Parameter_Error, "invalid hexstring");
83   if ((n&1))
84     return set_error (Parameter_Error, "odd number of digits");
85   n /= 2;
86   if (n != 20)
87     return set_error (Parameter_Error, "invalid length of keygrip");
88
89   buf = ctrl->keygrip;
90   for (p=line, n=0; n < 20; p += 2, n++)
91     buf[n] = xtoi_2 (p);
92   return 0;
93 }
94
95 /* SETHASH <algonumber> <hexstring> 
96
97   The client can use this command to tell the server about the data
98   (which usually is a hash) to be signed. */
99 static int
100 cmd_sethash (ASSUAN_CONTEXT ctx, char *line)
101 {
102   int n;
103   char *p;
104   CTRL ctrl = assuan_get_pointer (ctx);
105   unsigned char *buf;
106   char *endp;
107   int algo;
108
109   /* parse the algo number and check it */
110   algo = (int)strtoul (line, &endp, 10);
111   for (line = endp; *line == ' ' || *line == '\t'; line++)
112     ;
113   if (!algo || gcry_md_test_algo (algo))
114     return set_error (Unsupported_Algorithm, NULL);
115   ctrl->digest.algo = algo;
116
117   /* parse the hash value */
118   for (p=line,n=0; hexdigitp (*p); p++, n++)
119     ;
120   if (*p)
121     return set_error (Parameter_Error, "invalid hexstring");
122   if ((n&1))
123     return set_error (Parameter_Error, "odd number of digits");
124   n /= 2;
125   if (n != 16 && n != 20 && n != 24 && n != 32)
126     return set_error (Parameter_Error, "unsupported length of hash");
127   if (n > MAX_DIGEST_LEN)
128     return set_error (Parameter_Error, "hash value to long");
129
130   buf = ctrl->digest.value;
131   ctrl->digest.valuelen = n;
132   for (p=line, n=0; n < ctrl->digest.valuelen; p += 2, n++)
133     buf[n] = xtoi_2 (p);
134   for (; n < ctrl->digest.valuelen; n++)
135     buf[n] = 0;
136   return 0;
137 }
138
139
140 /* PKSIGN <options>
141
142    Perform the actual sign operation. Neither input nor output are
143    sensitive to to eavesdropping */
144 static int
145 cmd_pksign (ASSUAN_CONTEXT ctx, char *line)
146 {
147   int rc;
148   CTRL ctrl = assuan_get_pointer (ctx);
149
150   /* fixme: check that all required data is available */
151   rc = agent_pksign (ctrl, assuan_get_data_fp (ctx));
152   /* fixme: return an error */
153   return 0;
154 }
155
156
157 \f
158 /* Tell the assuan library about our commands */
159 static int
160 register_commands (ASSUAN_CONTEXT ctx)
161 {
162   static struct {
163     const char *name;
164     int cmd_id;
165     int (*handler)(ASSUAN_CONTEXT, char *line);
166   } table[] = {
167     { "SIGKEY",     0,  cmd_sigkey },
168     { "SETHASH",    0,  cmd_sethash },
169     { "PKSIGN",     0,  cmd_pksign },
170     { "",     ASSUAN_CMD_INPUT, NULL }, 
171     { "",     ASSUAN_CMD_OUTPUT, NULL }, 
172     { NULL }
173   };
174   int i, j, rc;
175
176   for (i=j=0; table[i].name; i++)
177     {
178       rc = assuan_register_command (ctx,
179                                     table[i].cmd_id? table[i].cmd_id
180                                                    : (ASSUAN_CMD_USER + j++),
181                                     table[i].name, table[i].handler);
182       if (rc)
183         return rc;
184     } 
185   assuan_register_reset_notify (ctx, reset_notify);
186   return 0;
187 }
188
189
190 /* Startup the server */
191 void
192 start_command_handler (void)
193 {
194   int rc;
195   int filedes[2];
196   ASSUAN_CONTEXT ctx;
197   struct server_control_s ctrl;
198
199   memset (&ctrl, 0, sizeof ctrl);
200
201   /* For now we use a simple pipe based server so that we can work
202      from scripts.  We will later add options to run as a daemon and
203      wait for requests on a Unix domain socket */
204   filedes[0] = 0;
205   filedes[1] = 1;
206   rc = assuan_init_pipe_server (&ctx, filedes);
207   if (rc)
208     {
209       log_error ("failed to initialize the server: %s\n",
210                  assuan_strerror(rc));
211       agent_exit (2);
212     }
213   rc = register_commands (ctx);
214   if (rc)
215     {
216       log_error ("failed to the register commands with Assuan: %s\n",
217                  assuan_strerror(rc));
218       agent_exit (2);
219     }
220
221   assuan_set_pointer (ctx, &ctrl);
222   ctrl.server_local = xcalloc (1, sizeof *ctrl.server_local);
223   ctrl.server_local->assuan_ctx = ctx;
224   ctrl.server_local->message_fd = -1;
225
226   log_info ("Assuan started\n");
227   for (;;)
228     {
229       rc = assuan_accept (ctx);
230       if (rc == -1)
231         {
232           log_info ("Assuan terminated\n");
233           break;
234         }
235       else if (rc)
236         {
237           log_info ("Assuan accept problem: %s\n", assuan_strerror (rc));
238           break;
239         }
240       
241       rc = assuan_process (ctx);
242       if (rc)
243         {
244           log_info ("Assuan processing failed: %s\n", assuan_strerror (rc));
245           continue;
246         }
247     }
248
249
250   assuan_deinit_pipe_server (ctx);
251 }
252