2f0de1d15669afc0554985d6d37aecb809887480
[gpgme.git] / gpgme / sign.c
1 /* sign.c -  signing functions
2  *      Copyright (C) 2000 Werner Koch (dd9jn)
3  *      Copyright (C) 2001 g10 Code GmbH
4  *
5  * This file is part of GPGME.
6  *
7  * GPGME is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  *
12  * GPGME is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
20  */
21
22 #include <config.h>
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <assert.h>
27
28 #include "util.h"
29 #include "context.h"
30 #include "ops.h"
31
32 #define SKIP_TOKEN_OR_RETURN(a) do { \
33     while (*(a) && *(a) != ' ') (a)++; \
34     while (*(a) == ' ') (a)++; \
35     if (!*(a)) \
36         return; /* oops */ \
37 } while (0)
38
39 struct sign_result_s
40 {
41   int no_passphrase;
42   int okay;
43   void *last_pw_handle;
44   char *userid_hint;
45   char *passphrase_info;
46   int bad_passphrase;
47   GpgmeData xmlinfo;
48 };
49
50 void
51 _gpgme_release_sign_result (SignResult result)
52 {
53   if (!result)
54     return;
55   gpgme_data_release (result->xmlinfo);
56   xfree (result->userid_hint);
57   xfree (result->passphrase_info);
58   xfree (result);
59 }
60
61 /* parse the args and save the information 
62  * <type> <pubkey algo> <hash algo> <class> <timestamp> <key fpr>
63  * in an XML structure.  With args of NULL the xml structure is closed.
64  */
65 static void
66 append_xml_siginfo (GpgmeData *rdh, char *args)
67 {
68     GpgmeData dh;
69     char helpbuf[100];
70     int i;
71     char *s;
72     unsigned long ul;
73
74     if ( !*rdh ) {
75         if (gpgme_data_new (rdh)) {
76             return; /* fixme: We are ignoring out-of-core */
77         }
78         dh = *rdh;
79         _gpgme_data_append_string (dh, "<GnupgOperationInfo>\n");
80     }
81     else {
82         dh = *rdh;
83         _gpgme_data_append_string (dh, "  </signature>\n");
84     }
85
86     if (!args) { /* just close the XML containter */
87         _gpgme_data_append_string (dh, "</GnupgOperationInfo>\n");
88         return;
89     }
90
91     _gpgme_data_append_string (dh, "  <signature>\n");
92     
93     _gpgme_data_append_string (dh,
94                                *args == 'D'? "    <detached/>\n":
95                                *args == 'C'? "    <cleartext/>\n":
96                                *args == 'S'? "    <standard/>\n":"");
97     SKIP_TOKEN_OR_RETURN (args);
98
99     sprintf (helpbuf, "    <algo>%d</algo>\n", atoi (args));
100     _gpgme_data_append_string (dh, helpbuf);
101     SKIP_TOKEN_OR_RETURN (args);
102
103     i = atoi (args);
104     sprintf (helpbuf, "    <hashalgo>%d</hashalgo>\n", atoi (args));
105     _gpgme_data_append_string (dh, helpbuf);
106     switch (i) {
107       case  1: s = "pgp-md5"; break;
108       case  2: s = "pgp-sha1"; break;
109       case  3: s = "pgp-ripemd160"; break;
110       case  5: s = "pgp-md2"; break;
111       case  6: s = "pgp-tiger192"; break;
112       case  7: s = "pgp-haval-5-160"; break;
113       case  8: s = "pgp-sha256"; break;
114       case  9: s = "pgp-sha384"; break;
115       case 10: s = "pgp-sha512"; break;
116       default: s = "pgp-unknown"; break;
117     }
118     sprintf (helpbuf, "    <micalg>%s</micalg>\n", s);
119     _gpgme_data_append_string (dh,helpbuf);
120     SKIP_TOKEN_OR_RETURN (args);
121     
122     sprintf (helpbuf, "    <sigclass>%.2s</sigclass>\n", args);
123     _gpgme_data_append_string (dh, helpbuf);
124     SKIP_TOKEN_OR_RETURN (args);
125
126     ul = strtoul (args, NULL, 10);
127     sprintf (helpbuf, "    <created>%lu</created>\n", ul);
128     _gpgme_data_append_string (dh, helpbuf);
129     SKIP_TOKEN_OR_RETURN (args);
130
131     /* count the length of the finperprint */
132     for (i=0; args[i] && args[i] != ' '; i++)
133         ;
134     _gpgme_data_append_string (dh, "    <fpr>");
135     _gpgme_data_append (dh, args, i);
136     _gpgme_data_append_string (dh, "</fpr>\n");
137 }
138
139
140
141 static void
142 sign_status_handler (GpgmeCtx ctx, GpgStatusCode code, char *args)
143 {
144     if (ctx->out_of_core)
145         return;
146     if (!ctx->result.sign)
147       {
148         ctx->result.sign = xtrycalloc (1, sizeof *ctx->result.sign);
149         if (!ctx->result.sign)
150           {
151             ctx->out_of_core = 1;
152             return;
153           }
154       }
155
156     switch (code) {
157       case STATUS_EOF:
158         if (ctx->result.sign->okay) {
159             append_xml_siginfo (&ctx->result.sign->xmlinfo, NULL);
160             _gpgme_set_op_info (ctx, ctx->result.sign->xmlinfo);
161             ctx->result.sign->xmlinfo = NULL;
162         }
163         break;
164
165       case STATUS_USERID_HINT:
166         xfree (ctx->result.sign->userid_hint);
167         if (!(ctx->result.sign->userid_hint = xtrystrdup (args)) )
168             ctx->out_of_core = 1;
169         break;
170
171       case STATUS_BAD_PASSPHRASE:
172         ctx->result.sign->bad_passphrase++;
173         break;
174
175       case STATUS_GOOD_PASSPHRASE:
176         ctx->result.sign->bad_passphrase = 0;
177         break;
178
179       case STATUS_NEED_PASSPHRASE:
180       case STATUS_NEED_PASSPHRASE_SYM:
181         xfree (ctx->result.sign->passphrase_info);
182         if (!(ctx->result.sign->passphrase_info = xtrystrdup (args)) )
183             ctx->out_of_core = 1;
184         break;
185
186       case STATUS_MISSING_PASSPHRASE:
187         DEBUG0 ("missing passphrase - stop\n");
188         ctx->result.sign->no_passphrase = 1;
189         break;
190
191       case STATUS_SIG_CREATED: 
192         /* fixme: we have no error return for multiple signatures */
193         append_xml_siginfo (&ctx->result.sign->xmlinfo, args);
194         ctx->result.sign->okay =1;
195         break;
196
197       default:
198         break;
199     }
200 }
201
202 static const char *
203 command_handler ( void *opaque, GpgStatusCode code, const char *key )
204 {
205     GpgmeCtx c = opaque;
206
207     if (!c->result.sign)
208       {
209         c->result.sign = xtrycalloc (1, sizeof *c->result.sign);
210         if (!c->result.sign)
211           {
212             c->out_of_core = 1;
213             return NULL;
214           }
215       }
216
217     if ( !code ) {
218         /* We have been called for cleanup */
219         if ( c->passphrase_cb ) { 
220             /* Fixme: take the key in account */
221             c->passphrase_cb (c->passphrase_cb_value, 0, 
222                               &c->result.sign->last_pw_handle );
223         }
224         
225         return NULL;
226     }
227
228     if ( !key || !c->passphrase_cb )
229         return NULL;
230     
231     if ( code == STATUS_GET_HIDDEN && !strcmp (key, "passphrase.enter") ) {
232         const char *userid_hint = c->result.sign->userid_hint;
233         const char *passphrase_info = c->result.sign->passphrase_info;
234         int bad_passphrase = c->result.sign->bad_passphrase;
235         char *buf;
236         const char *s;
237
238         c->result.sign->bad_passphrase = 0;
239         if (!userid_hint)
240             userid_hint = "[User ID hint missing]";
241         if (!passphrase_info)
242             passphrase_info = "[passphrase info missing]";
243         buf = xtrymalloc ( 20 + strlen (userid_hint)
244                            + strlen (passphrase_info) + 3);
245         if (!buf) {
246             c->out_of_core = 1;
247             return NULL;
248         }
249         sprintf (buf, "%s\n%s\n%s",
250                  bad_passphrase? "TRY_AGAIN":"ENTER",
251                  userid_hint, passphrase_info );
252
253         s = c->passphrase_cb (c->passphrase_cb_value,
254                               buf, &c->result.sign->last_pw_handle );
255         xfree (buf);
256         return s;
257     }
258     
259     return NULL;
260 }
261
262
263 GpgmeError
264 gpgme_op_sign_start ( GpgmeCtx c, GpgmeData in, GpgmeData out,
265                       GpgmeSigMode mode )
266 {
267     int rc = 0;
268     int i;
269     GpgmeKey key;
270
271     fail_on_pending_request( c );
272     c->pending = 1;
273
274     _gpgme_release_result (c);
275     c->out_of_core = 0;
276
277
278     if ( mode != GPGME_SIG_MODE_NORMAL
279          && mode != GPGME_SIG_MODE_DETACH
280          && mode != GPGME_SIG_MODE_CLEAR )
281         return mk_error (Invalid_Value);
282         
283     /* create a process object */
284     _gpgme_gpg_release (c->gpg);
285     c->gpg = NULL;
286     rc = _gpgme_gpg_new ( &c->gpg );
287     if (rc)
288         goto leave;
289
290     _gpgme_gpg_set_status_handler ( c->gpg, sign_status_handler, c );
291     if (c->passphrase_cb) {
292         rc = _gpgme_gpg_set_command_handler ( c->gpg, command_handler, c );
293         if (rc)
294             goto leave;
295     }
296
297     /* build the commandline */
298     if ( mode == GPGME_SIG_MODE_CLEAR ) {
299         _gpgme_gpg_add_arg ( c->gpg, "--clearsign" );
300     }
301     else {
302         _gpgme_gpg_add_arg ( c->gpg, "--sign" );
303         if ( mode == GPGME_SIG_MODE_DETACH )
304             _gpgme_gpg_add_arg ( c->gpg, "--detach" );
305         if ( c->use_armor )
306             _gpgme_gpg_add_arg ( c->gpg, "--armor" );
307         if ( c->use_textmode )
308             _gpgme_gpg_add_arg ( c->gpg, "--textmode" );
309     }
310     for (i=0; i < c->verbosity; i++)
311         _gpgme_gpg_add_arg ( c->gpg, "--verbose" );
312     for (i=0; (key = gpgme_signers_enum (c, i)); i++ ) {
313         const char *s = gpgme_key_get_string_attr (key, GPGME_ATTR_KEYID,
314                                                    NULL, 0);
315         if (s) {
316             _gpgme_gpg_add_arg (c->gpg, "-u");
317             _gpgme_gpg_add_arg (c->gpg, s);
318         }
319         gpgme_key_unref (key);
320     }
321
322     
323     /* Check the supplied data */
324     if ( gpgme_data_get_type (in) == GPGME_DATA_TYPE_NONE ) {
325         rc = mk_error (No_Data);
326         goto leave;
327     }
328     _gpgme_data_set_mode (in, GPGME_DATA_MODE_OUT );
329     if ( !out || gpgme_data_get_type (out) != GPGME_DATA_TYPE_NONE ) {
330         rc = mk_error (Invalid_Value);
331         goto leave;
332     }
333     _gpgme_data_set_mode (out, GPGME_DATA_MODE_IN );
334
335     /* tell the gpg object about the data */
336     _gpgme_gpg_add_data ( c->gpg, in, 0 );
337     _gpgme_gpg_add_data ( c->gpg, out, 1 );
338
339     /* and kick off the process */
340     rc = _gpgme_gpg_spawn ( c->gpg, c );
341
342  leave:
343     if (rc) {
344         c->pending = 0; 
345         _gpgme_gpg_release ( c->gpg ); c->gpg = NULL;
346     }
347     return rc;
348 }
349
350
351 /**
352  * gpgme_op_sign:
353  * @c: The context
354  * @in: Data to be signed
355  * @out: Detached signature
356  * @mode: Signature creation mode
357  * 
358  * Create a detached signature for @in and write it to @out.
359  * The data will be signed using either the default key or the ones
360  * defined through @c.
361  * The defined modes for signature create are:
362  * <literal>
363  * GPGME_SIG_MODE_NORMAL (or 0) 
364  * GPGME_SIG_MODE_DETACH
365  * GPGME_SIG_MODE_CLEAR
366  * </literal>
367  * Note that the settings done by gpgme_set_armor() and gpgme_set_textmode()
368  * are ignore for @mode GPGME_SIG_MODE_CLEAR.
369  * 
370  * Return value: 0 on success or an error code.
371  **/
372 GpgmeError
373 gpgme_op_sign (GpgmeCtx c, GpgmeData in, GpgmeData out, GpgmeSigMode mode)
374 {
375     GpgmeError err = gpgme_op_sign_start ( c, in, out, mode );
376     if ( !err ) {
377         gpgme_wait (c, 1);
378         if (!c->result.sign)
379             err = mk_error (General_Error);
380         else if (c->out_of_core)
381             err = mk_error (Out_Of_Core);
382         else {
383             if (c->result.sign->no_passphrase)
384                 err = mk_error (No_Passphrase);
385             else if (!c->result.sign->okay)
386                 err = mk_error (No_Data); /* Hmmm: choose a better error? */
387         }
388         c->pending = 0;
389     }
390     return err;
391 }
392
393
394
395
396
397
398
399
400