Fix detection of invalid signer keys.
[gpgme.git] / src / sign.c
1 /* sign.c - Signing function.
2    Copyright (C) 2000 Werner Koch (dd9jn)
3    Copyright (C) 2001, 2002, 2003, 2004, 2007 g10 Code GmbH
4
5    This file is part of GPGME.
6  
7    GPGME is free software; you can redistribute it and/or modify it
8    under the terms of the GNU Lesser General Public License as
9    published by the Free Software Foundation; either version 2.1 of
10    the License, or (at your option) any later version.
11    
12    GPGME is distributed in the hope that it will be useful, but
13    WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15    Lesser General Public License for more details.
16    
17    You should have received a copy of the GNU Lesser General Public
18    License along with this program; if not, write to the Free Software
19    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
20    02111-1307, USA.  */
21
22 #if HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25 #include <stdlib.h>
26 #include <string.h>
27 #include <errno.h>
28
29 /* Suppress warning for accessing deprecated member "class".  */
30 #define _GPGME_IN_GPGME 1
31 #include "gpgme.h"
32 #include "context.h"
33 #include "ops.h"
34 #include "util.h"
35 #include "debug.h"
36
37 \f
38 typedef struct
39 {
40   struct _gpgme_op_sign_result result;
41
42   /* A pointer to the next pointer of the last invalid signer in
43      the list.  This makes appending new invalid signers painless
44      while preserving the order.  */
45   gpgme_invalid_key_t *last_signer_p;
46
47   /* Likewise for signature information.  */
48   gpgme_new_signature_t *last_sig_p;
49
50   /* Flags used while processing the status lines.  */
51   unsigned int ignore_inv_recp:1;
52   unsigned int inv_sgnr_seen:1;
53 } *op_data_t;
54
55
56 static void
57 release_op_data (void *hook)
58 {
59   op_data_t opd = (op_data_t) hook;
60   gpgme_invalid_key_t invalid_signer = opd->result.invalid_signers;
61   gpgme_new_signature_t sig = opd->result.signatures;
62
63   while (invalid_signer)
64     {
65       gpgme_invalid_key_t next = invalid_signer->next;
66       if (invalid_signer->fpr)
67         free (invalid_signer->fpr);
68       free (invalid_signer);
69       invalid_signer = next;
70     }
71
72   while (sig)
73     {
74       gpgme_new_signature_t next = sig->next;
75       free (sig->fpr);
76       free (sig);
77       sig = next;
78     }
79 }
80
81
82 gpgme_sign_result_t
83 gpgme_op_sign_result (gpgme_ctx_t ctx)
84 {
85   void *hook;
86   op_data_t opd;
87   gpgme_error_t err;
88
89   TRACE_BEG (DEBUG_CTX, "gpgme_op_sign_result", ctx);
90
91   err = _gpgme_op_data_lookup (ctx, OPDATA_SIGN, &hook, -1, NULL);
92   opd = hook;
93   if (err || !opd)
94     {
95       TRACE_SUC0 ("result=(null)");
96       return NULL;
97     }
98
99   if (_gpgme_debug_trace ())
100     {
101       gpgme_invalid_key_t inv_key = opd->result.invalid_signers;
102       gpgme_new_signature_t sig = opd->result.signatures;
103       int inv_signers = 0;
104       int signatures = 0;
105
106       while (inv_key)
107         {
108           inv_signers++;
109           inv_key = inv_key->next;
110         }
111       while (sig)
112         {
113           signatures++;
114           sig = sig->next;
115         }
116
117       TRACE_LOG2 ("result: invalid signers: %i, signatures: %i",
118                   inv_signers, signatures);
119       inv_key = opd->result.invalid_signers;
120       while (inv_key)
121         {
122           TRACE_LOG3 ("result: invalid signer: fpr=%s, reason=%s <%s>",
123                       inv_key->fpr, gpgme_strerror (inv_key->reason),
124                       gpgme_strsource (inv_key->reason));
125           inv_key = inv_key->next;
126         }
127       sig = opd->result.signatures;
128       while (sig)
129         {
130           TRACE_LOG6 ("result: signature: type=%i, pubkey_algo=%i, "
131                       "hash_algo=%i, timestamp=%li, fpr=%s, sig_class=%i",
132                       sig->type, sig->pubkey_algo, sig->hash_algo,
133                       sig->timestamp, sig->fpr, sig->sig_class);
134           sig = sig->next;
135         }
136     }
137
138   TRACE_SUC1 ("result=%p", &opd->result);
139   return &opd->result;
140 }
141
142 \f
143 static gpgme_error_t
144 parse_sig_created (char *args, gpgme_new_signature_t *sigp)
145 {
146   gpgme_new_signature_t sig;
147   char *tail;
148
149   sig = malloc (sizeof (*sig));
150   if (!sig)
151     return gpg_error_from_errno (errno);
152
153   sig->next = NULL;
154   switch (*args)
155     {
156     case 'S':
157       sig->type = GPGME_SIG_MODE_NORMAL;
158       break;
159
160     case 'D':
161       sig->type = GPGME_SIG_MODE_DETACH;
162       break;
163
164     case 'C':
165       sig->type = GPGME_SIG_MODE_CLEAR;
166       break;
167
168     default:
169       /* The backend engine is not behaving.  */
170       free (sig);
171       return gpg_error (GPG_ERR_INV_ENGINE);
172     }
173
174   args++;
175   if (*args != ' ')
176     {
177       free (sig);
178       return gpg_error (GPG_ERR_INV_ENGINE);
179     }
180
181   errno = 0;
182   sig->pubkey_algo = strtol (args, &tail, 0);
183   if (errno || args == tail || *tail != ' ')
184     {
185       /* The crypto backend does not behave.  */
186       free (sig);
187       return gpg_error (GPG_ERR_INV_ENGINE);
188     }
189   args = tail;
190
191   sig->hash_algo = strtol (args, &tail, 0);
192   if (errno || args == tail || *tail != ' ')
193     {
194       /* The crypto backend does not behave.  */
195       free (sig);
196       return gpg_error (GPG_ERR_INV_ENGINE);
197     }
198   args = tail;
199
200   sig->sig_class = strtol (args, &tail, 0);
201   sig->class = sig->sig_class;
202   sig->_obsolete_class = sig->sig_class;
203   if (errno || args == tail || *tail != ' ')
204     {
205       /* The crypto backend does not behave.  */
206       free (sig);
207       return gpg_error (GPG_ERR_INV_ENGINE);
208     }
209   args = tail;
210
211   sig->timestamp = _gpgme_parse_timestamp (args, &tail);
212   if (sig->timestamp == -1 || args == tail || *tail != ' ')
213     {
214       /* The crypto backend does not behave.  */
215       free (sig);
216       return gpg_error (GPG_ERR_INV_ENGINE);
217     }
218   args = tail;
219   while (*args == ' ')
220     args++;
221
222   if (!*args)
223     {
224       /* The crypto backend does not behave.  */
225       free (sig);
226       return gpg_error (GPG_ERR_INV_ENGINE);
227     }
228
229   tail = strchr (args, ' ');
230   if (tail)
231     *tail = '\0';
232
233   sig->fpr = strdup (args);
234   if (!sig->fpr)
235     {
236       int saved_errno = errno;
237       free (sig);
238       return gpg_error_from_errno (saved_errno);
239     }
240   *sigp = sig;
241   return 0;
242 }
243
244
245 gpgme_error_t
246 _gpgme_sign_status_handler (void *priv, gpgme_status_code_t code, char *args)
247 {
248   gpgme_ctx_t ctx = (gpgme_ctx_t) priv;
249   gpgme_error_t err;
250   void *hook;
251   op_data_t opd;
252
253   err = _gpgme_passphrase_status_handler (priv, code, args);
254   if (err)
255     return err;
256
257   err = _gpgme_op_data_lookup (ctx, OPDATA_SIGN, &hook, -1, NULL);
258   opd = hook;
259   if (err)
260     return err;
261
262   switch (code)
263     {
264     case GPGME_STATUS_SIG_CREATED:
265       err = parse_sig_created (args, opd->last_sig_p);
266       if (err)
267         return err;
268
269       opd->last_sig_p = &(*opd->last_sig_p)->next;
270       break;
271
272     case GPGME_STATUS_INV_RECP:
273       if (opd->inv_sgnr_seen && opd->ignore_inv_recp)
274         break; 
275       /* FALLTROUGH */
276     case GPGME_STATUS_INV_SGNR:
277       if (code == GPGME_STATUS_INV_SGNR)
278         opd->inv_sgnr_seen = 1;
279       err = _gpgme_parse_inv_recp (args, opd->last_signer_p);
280       if (err)
281         return err;
282
283       opd->last_signer_p = &(*opd->last_signer_p)->next;
284       break;
285
286     case GPGME_STATUS_EOF:
287       if (opd->result.invalid_signers)
288         return gpg_error (GPG_ERR_UNUSABLE_SECKEY);
289       break;
290
291     default:
292       break;
293     }
294   return err;
295 }
296
297
298 static gpgme_error_t
299 sign_status_handler (void *priv, gpgme_status_code_t code, char *args)
300 {
301   gpgme_error_t err;
302
303   err = _gpgme_progress_status_handler (priv, code, args);
304   if (!err)
305     err = _gpgme_sign_status_handler (priv, code, args);
306   return err;
307 }
308
309
310 static gpgme_error_t
311 sign_init_result (gpgme_ctx_t ctx, int ignore_inv_recp)
312 {
313   gpgme_error_t err;
314   void *hook;
315   op_data_t opd;
316
317   err = _gpgme_op_data_lookup (ctx, OPDATA_SIGN, &hook,
318                                sizeof (*opd), release_op_data);
319   opd = hook;
320   if (err)
321     return err;
322   opd->last_signer_p = &opd->result.invalid_signers;
323   opd->last_sig_p = &opd->result.signatures;
324   opd->ignore_inv_recp = !!ignore_inv_recp;
325   opd->inv_sgnr_seen = 0;
326   return 0;
327 }
328
329 gpgme_error_t
330 _gpgme_op_sign_init_result (gpgme_ctx_t ctx)
331 {
332   return sign_init_result (ctx, 0);
333 }
334
335
336 static gpgme_error_t
337 sign_start (gpgme_ctx_t ctx, int synchronous, gpgme_data_t plain,
338             gpgme_data_t sig, gpgme_sig_mode_t mode)
339 {
340   gpgme_error_t err;
341
342   err = _gpgme_op_reset (ctx, synchronous);
343   if (err)
344     return err;
345
346   /* If we are using the CMS protocol, we ignore the INV_RECP status
347      code if a newer GPGSM is in use.  GPGMS does not support combined
348      sign+encrypt and thus this can't harm.  */
349   err = sign_init_result (ctx, (ctx->protocol == GPGME_PROTOCOL_CMS));
350   if (err)
351     return err;
352
353   if (mode != GPGME_SIG_MODE_NORMAL && mode != GPGME_SIG_MODE_DETACH
354       && mode != GPGME_SIG_MODE_CLEAR)
355     return gpg_error (GPG_ERR_INV_VALUE);
356
357   if (!plain)
358     return gpg_error (GPG_ERR_NO_DATA);
359   if (!sig)
360     return gpg_error (GPG_ERR_INV_VALUE);
361
362   if (ctx->passphrase_cb)
363     {
364       err = _gpgme_engine_set_command_handler
365         (ctx->engine, _gpgme_passphrase_command_handler, ctx, NULL);
366       if (err)
367         return err;
368     }
369
370   _gpgme_engine_set_status_handler (ctx->engine, sign_status_handler,
371                                     ctx);
372
373   return _gpgme_engine_op_sign (ctx->engine, plain, sig, mode, ctx->use_armor,
374                                 ctx->use_textmode, ctx->include_certs,
375                                 ctx /* FIXME */);
376 }
377
378
379 /* Sign the plaintext PLAIN and store the signature in SIG.  */
380 gpgme_error_t
381 gpgme_op_sign_start (gpgme_ctx_t ctx, gpgme_data_t plain, gpgme_data_t sig,
382                      gpgme_sig_mode_t mode)
383 {
384   TRACE_BEG3 (DEBUG_CTX, "gpgme_op_sign_start", ctx,
385               "plain=%p, sig=%p, mode=%i", plain, sig, mode);
386   return TRACE_ERR (sign_start (ctx, 0, plain, sig, mode));
387 }
388
389
390 /* Sign the plaintext PLAIN and store the signature in SIG.  */
391 gpgme_error_t
392 gpgme_op_sign (gpgme_ctx_t ctx, gpgme_data_t plain, gpgme_data_t sig,
393                gpgme_sig_mode_t mode)
394 {
395   gpgme_error_t err;
396
397   TRACE_BEG3 (DEBUG_CTX, "gpgme_op_sign_start", ctx,
398               "plain=%p, sig=%p, mode=%i", plain, sig, mode);
399   err = sign_start (ctx, 1, plain, sig, mode);
400   if (!err)
401     err = _gpgme_wait_one (ctx);
402   return TRACE_ERR (err);
403 }