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