Line data Source code
1 : /* op-support.c - Supporting functions.
2 : Copyright (C) 2002, 2003, 2004, 2007 g10 Code GmbH
3 :
4 : This file is part of GPGME.
5 :
6 : GPGME is free software; you can redistribute it and/or modify it
7 : under the terms of the GNU Lesser General Public License as
8 : published by the Free Software Foundation; either version 2.1 of
9 : the License, or (at your option) any later version.
10 :
11 : GPGME is distributed in the hope that it will be useful, but
12 : WITHOUT ANY WARRANTY; without even the implied warranty of
13 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 : Lesser General Public License for more details.
15 :
16 : You should have received a copy of the GNU Lesser General Public
17 : License along with this program; if not, see <https://www.gnu.org/licenses/>.
18 : */
19 :
20 : #if HAVE_CONFIG_H
21 : #include <config.h>
22 : #endif
23 : #include <stdlib.h>
24 : #include <errno.h>
25 : #include <string.h>
26 : #ifdef HAVE_LOCALE_H
27 : #include <locale.h>
28 : #endif
29 :
30 : #include "gpgme.h"
31 : #include "context.h"
32 : #include "ops.h"
33 : #include "util.h"
34 : #include "debug.h"
35 :
36 : #if GPG_ERROR_VERSION_NUMBER < 0x011700 /* 1.23 */
37 : # define GPG_ERR_SUBKEYS_EXP_OR_REV 217
38 : #endif
39 :
40 :
41 :
42 : gpgme_error_t
43 16643 : _gpgme_op_data_lookup (gpgme_ctx_t ctx, ctx_op_data_id_t type, void **hook,
44 : int size, void (*cleanup) (void *))
45 : {
46 : struct ctx_op_data *data;
47 :
48 16643 : if (!ctx)
49 0 : return gpg_error (GPG_ERR_INV_VALUE);
50 :
51 16643 : data = ctx->op_data;
52 36156 : while (data && data->type != type)
53 2870 : data = data->next;
54 16643 : if (!data)
55 : {
56 877 : if (size < 0)
57 : {
58 0 : *hook = NULL;
59 0 : return 0;
60 : }
61 :
62 877 : data = calloc (1, sizeof (struct ctx_op_data) + size);
63 877 : if (!data)
64 0 : return gpg_error_from_syserror ();
65 877 : data->magic = CTX_OP_DATA_MAGIC;
66 877 : data->next = ctx->op_data;
67 877 : data->type = type;
68 877 : data->cleanup = cleanup;
69 877 : data->hook = (void *) (((char *) data) + sizeof (struct ctx_op_data));
70 877 : data->references = 1;
71 877 : ctx->op_data = data;
72 : }
73 16643 : *hook = data->hook;
74 16643 : return 0;
75 : }
76 :
77 :
78 : /* type is: 0: asynchronous operation (use global or user event loop).
79 : 1: synchronous operation (always use private event loop).
80 : 2: asynchronous private operation (use private or user
81 : event loop).
82 : 256: Modification flag to suppress the engine reset.
83 : */
84 : gpgme_error_t
85 892 : _gpgme_op_reset (gpgme_ctx_t ctx, int type)
86 : {
87 892 : gpgme_error_t err = 0;
88 : struct gpgme_io_cbs io_cbs;
89 892 : int no_reset = (type & 256);
90 892 : int reuse_engine = 0;
91 :
92 892 : type &= 255;
93 :
94 892 : _gpgme_release_result (ctx);
95 896 : LOCK (ctx->lock);
96 896 : ctx->canceled = 0;
97 896 : ctx->redraw_suggested = 0;
98 896 : UNLOCK (ctx->lock);
99 :
100 896 : if (ctx->engine && no_reset)
101 39 : reuse_engine = 1;
102 857 : else if (ctx->engine)
103 : {
104 : /* Attempt to reset an existing engine. */
105 :
106 288 : err = _gpgme_engine_reset (ctx->engine);
107 288 : if (gpg_err_code (err) == GPG_ERR_NOT_IMPLEMENTED)
108 : {
109 284 : _gpgme_engine_release (ctx->engine);
110 284 : ctx->engine = NULL;
111 : }
112 : }
113 :
114 896 : if (!ctx->engine)
115 : {
116 : gpgme_engine_info_t info;
117 852 : info = ctx->engine_info;
118 2112 : while (info && info->protocol != ctx->protocol)
119 408 : info = info->next;
120 :
121 852 : if (!info)
122 0 : return gpg_error (GPG_ERR_UNSUPPORTED_PROTOCOL);
123 :
124 : /* Create an engine object. */
125 852 : err = _gpgme_engine_new (info, &ctx->engine);
126 853 : if (err)
127 0 : return err;
128 : }
129 :
130 897 : if (!reuse_engine)
131 : {
132 857 : err = 0;
133 : #ifdef LC_CTYPE
134 857 : err = _gpgme_engine_set_locale (ctx->engine, LC_CTYPE, ctx->lc_ctype);
135 : #endif
136 : #ifdef LC_MESSAGES
137 857 : if (!err)
138 675 : err = _gpgme_engine_set_locale (ctx->engine,
139 675 : LC_MESSAGES, ctx->lc_messages);
140 : #endif
141 857 : if (gpg_err_code (err) == GPG_ERR_NOT_IMPLEMENTED)
142 182 : err = 0;
143 :
144 857 : if (!err)
145 : {
146 857 : err = _gpgme_engine_set_pinentry_mode (ctx->engine,
147 : ctx->pinentry_mode);
148 857 : if (gpg_err_code (err) == GPG_ERR_NOT_IMPLEMENTED)
149 206 : err = 0;
150 : }
151 :
152 857 : if (!err && ctx->status_cb && ctx->full_status)
153 : {
154 4 : _gpgme_engine_set_status_cb (ctx->engine,
155 : ctx->status_cb, ctx->status_cb_value);
156 : }
157 :
158 856 : if (err)
159 : {
160 0 : _gpgme_engine_release (ctx->engine);
161 0 : ctx->engine = NULL;
162 0 : return err;
163 : }
164 : }
165 :
166 896 : if (ctx->sub_protocol != GPGME_PROTOCOL_DEFAULT)
167 : {
168 0 : err = _gpgme_engine_set_protocol (ctx->engine, ctx->sub_protocol);
169 0 : if (err)
170 0 : return err;
171 : }
172 :
173 896 : if (type == 1 || (type == 2 && !ctx->io_cbs.add))
174 : {
175 : /* Use private event loop. */
176 892 : io_cbs.add = _gpgme_add_io_cb;
177 892 : io_cbs.add_priv = ctx;
178 892 : io_cbs.remove = _gpgme_remove_io_cb;
179 892 : io_cbs.event = _gpgme_wait_private_event_cb;
180 892 : io_cbs.event_priv = ctx;
181 : }
182 4 : else if (! ctx->io_cbs.add)
183 : {
184 : /* Use global event loop. */
185 3 : io_cbs.add = _gpgme_add_io_cb;
186 3 : io_cbs.add_priv = ctx;
187 3 : io_cbs.remove = _gpgme_remove_io_cb;
188 3 : io_cbs.event = _gpgme_wait_global_event_cb;
189 3 : io_cbs.event_priv = ctx;
190 : }
191 : else
192 : {
193 : /* Use user event loop. */
194 1 : io_cbs.add = _gpgme_wait_user_add_io_cb;
195 1 : io_cbs.add_priv = ctx;
196 1 : io_cbs.remove = _gpgme_wait_user_remove_io_cb;
197 1 : io_cbs.event = _gpgme_wait_user_event_cb;
198 1 : io_cbs.event_priv = ctx;
199 : }
200 896 : _gpgme_engine_set_io_cbs (ctx->engine, &io_cbs);
201 896 : return err;
202 : }
203 :
204 :
205 : /* Parse the INV_RECP or INV_SNDR status line in ARGS and return the
206 : result in KEY. If KC_FPR (from the KEY_CONSIDERED status line) is
207 : not NULL take the KC_FLAGS in account. */
208 : gpgme_error_t
209 6 : _gpgme_parse_inv_recp (char *args, int for_signing,
210 : const char *kc_fpr, unsigned int kc_flags,
211 : gpgme_invalid_key_t *key)
212 : {
213 : gpgme_invalid_key_t inv_key;
214 : char *tail;
215 : long int reason;
216 :
217 : (void)for_signing;
218 :
219 6 : inv_key = calloc (1, sizeof (*inv_key));
220 6 : if (!inv_key)
221 0 : return gpg_error_from_syserror ();
222 6 : inv_key->next = NULL;
223 6 : gpg_err_set_errno (0);
224 6 : reason = strtol (args, &tail, 0);
225 6 : if (errno || args == tail || (*tail && *tail != ' '))
226 : {
227 : /* The crypto backend does not behave. */
228 0 : free (inv_key);
229 0 : return trace_gpg_error (GPG_ERR_INV_ENGINE);
230 : }
231 :
232 6 : switch (reason)
233 : {
234 : case 0:
235 2 : if (kc_fpr && (kc_flags & 2))
236 2 : inv_key->reason = gpg_error (GPG_ERR_SUBKEYS_EXP_OR_REV);
237 : else
238 0 : inv_key->reason = gpg_error (GPG_ERR_GENERAL);
239 2 : break;
240 :
241 : case 1:
242 0 : inv_key->reason = gpg_error (GPG_ERR_NO_PUBKEY);
243 0 : break;
244 :
245 : case 2:
246 0 : inv_key->reason = gpg_error (GPG_ERR_AMBIGUOUS_NAME);
247 0 : break;
248 :
249 : case 3:
250 0 : inv_key->reason = gpg_error (GPG_ERR_WRONG_KEY_USAGE);
251 0 : break;
252 :
253 : case 4:
254 0 : inv_key->reason = gpg_error (GPG_ERR_CERT_REVOKED);
255 0 : break;
256 :
257 : case 5:
258 0 : inv_key->reason = gpg_error (GPG_ERR_CERT_EXPIRED);
259 0 : break;
260 :
261 : case 6:
262 0 : inv_key->reason = gpg_error (GPG_ERR_NO_CRL_KNOWN);
263 0 : break;
264 :
265 : case 7:
266 0 : inv_key->reason = gpg_error (GPG_ERR_CRL_TOO_OLD);
267 0 : break;
268 :
269 : case 8:
270 0 : inv_key->reason = gpg_error (GPG_ERR_NO_POLICY_MATCH);
271 0 : break;
272 :
273 : case 9:
274 4 : inv_key->reason = gpg_error (GPG_ERR_NO_SECKEY);
275 4 : break;
276 :
277 : case 10:
278 0 : inv_key->reason = gpg_error (GPG_ERR_PUBKEY_NOT_TRUSTED);
279 0 : break;
280 :
281 : case 11:
282 0 : inv_key->reason = gpg_error (GPG_ERR_MISSING_CERT);
283 0 : break;
284 :
285 : case 12:
286 0 : inv_key->reason = gpg_error (GPG_ERR_MISSING_ISSUER_CERT);
287 0 : break;
288 :
289 : case 13:
290 0 : inv_key->reason = gpg_error (252); /*GPG_ERR_KEY_DISABLED*/
291 0 : break;
292 :
293 : case 14:
294 0 : inv_key->reason = gpg_error (GPG_ERR_INV_USER_ID);
295 0 : break;
296 :
297 : default:
298 0 : inv_key->reason = gpg_error (GPG_ERR_GENERAL);
299 0 : break;
300 : }
301 :
302 18 : while (*tail && *tail == ' ')
303 6 : tail++;
304 6 : if (*tail)
305 : {
306 6 : inv_key->fpr = strdup (tail);
307 6 : if (!inv_key->fpr)
308 : {
309 0 : free (inv_key);
310 0 : return gpg_error_from_syserror ();
311 : }
312 : }
313 :
314 6 : *key = inv_key;
315 6 : return 0;
316 : }
317 :
318 :
319 :
320 : /* Parse a KEY_CONSIDERED status line in ARGS and store the
321 : * fingerprint and the flags at R_FPR and R_FLAGS. The caller must
322 : * free the value at R_FPR on success. */
323 : gpgme_error_t
324 337 : _gpgme_parse_key_considered (const char *args,
325 : char **r_fpr, unsigned int *r_flags)
326 : {
327 : char *pend;
328 : size_t n;
329 :
330 337 : *r_fpr = NULL;
331 :
332 337 : pend = strchr (args, ' ');
333 337 : if (!pend || pend == args)
334 0 : return trace_gpg_error (GPG_ERR_INV_ENGINE); /* Bogus status line. */
335 337 : n = pend - args;
336 337 : *r_fpr = malloc (n + 1);
337 337 : if (!*r_fpr)
338 0 : return gpg_error_from_syserror ();
339 337 : memcpy (*r_fpr, args, n);
340 337 : (*r_fpr)[n] = 0;
341 337 : args = pend + 1;
342 :
343 337 : gpg_err_set_errno (0);
344 337 : *r_flags = strtoul (args, &pend, 0);
345 337 : if (errno || args == pend || (*pend && *pend != ' '))
346 : {
347 0 : free (*r_fpr);
348 0 : *r_fpr = NULL;
349 0 : return trace_gpg_error (GPG_ERR_INV_ENGINE);
350 : }
351 :
352 337 : return 0;
353 : }
354 :
355 :
356 : /* Parse the PLAINTEXT status line in ARGS and return the result in
357 : FILENAMEP. */
358 : gpgme_error_t
359 94 : _gpgme_parse_plaintext (char *args, char **filenamep)
360 : {
361 : char *tail;
362 :
363 188 : while (*args == ' ')
364 0 : args++;
365 94 : if (*args == '\0')
366 0 : return 0;
367 :
368 : /* First argument is file type. */
369 376 : while (*args != ' ' && *args != '\0')
370 188 : args++;
371 282 : while (*args == ' ')
372 94 : args++;
373 94 : if (*args == '\0')
374 0 : return 0;
375 :
376 : /* Second argument is the timestamp. */
377 1101 : while (*args != ' ' && *args != '\0')
378 913 : args++;
379 282 : while (*args == ' ')
380 94 : args++;
381 94 : if (*args == '\0')
382 63 : return 0;
383 :
384 31 : tail = args;
385 228 : while (*tail != ' ' && *tail != '\0')
386 166 : tail++;
387 31 : *tail = '\0';
388 31 : if (filenamep && *args != '\0')
389 : {
390 31 : char *filename = strdup (args);
391 31 : if (!filename)
392 0 : return gpg_error_from_syserror ();
393 :
394 31 : *filenamep = filename;
395 : }
396 31 : return 0;
397 : }
398 :
399 :
400 : /* Parse a FAILURE status line and return the error code. ARGS is
401 : modified to contain the location part. */
402 : gpgme_error_t
403 8 : _gpgme_parse_failure (char *args)
404 : {
405 : char *where, *which;
406 :
407 8 : where = strchr (args, ' ');
408 8 : if (!where)
409 2 : return trace_gpg_error (GPG_ERR_INV_ENGINE);
410 :
411 6 : *where = '\0';
412 6 : which = where + 1;
413 :
414 6 : where = strchr (which, ' ');
415 6 : if (where)
416 0 : *where = '\0';
417 :
418 6 : where = args;
419 :
420 6 : return atoi (which);
421 : }
|