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 18745 : _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 18745 : if (!ctx)
49 0 : return gpg_error (GPG_ERR_INV_VALUE);
50 :
51 18745 : data = ctx->op_data;
52 40739 : while (data && data->type != type)
53 3249 : data = data->next;
54 18745 : if (!data)
55 : {
56 978 : if (size < 0)
57 : {
58 0 : *hook = NULL;
59 0 : return 0;
60 : }
61 :
62 978 : data = calloc (1, sizeof (struct ctx_op_data) + size);
63 978 : if (!data)
64 0 : return gpg_error_from_syserror ();
65 978 : data->magic = CTX_OP_DATA_MAGIC;
66 978 : data->next = ctx->op_data;
67 978 : data->type = type;
68 978 : data->cleanup = cleanup;
69 978 : data->hook = (void *) (((char *) data) + sizeof (struct ctx_op_data));
70 978 : data->references = 1;
71 978 : ctx->op_data = data;
72 : }
73 18745 : *hook = data->hook;
74 18745 : 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 1013 : _gpgme_op_reset (gpgme_ctx_t ctx, int type)
86 : {
87 1013 : gpgme_error_t err = 0;
88 : struct gpgme_io_cbs io_cbs;
89 1013 : int no_reset = (type & 256);
90 1013 : int reuse_engine = 0;
91 :
92 1013 : type &= 255;
93 :
94 1013 : _gpgme_release_result (ctx);
95 1013 : LOCK (ctx->lock);
96 1014 : ctx->canceled = 0;
97 1014 : ctx->redraw_suggested = 0;
98 1014 : UNLOCK (ctx->lock);
99 :
100 1014 : if (ctx->engine && no_reset)
101 16 : reuse_engine = 1;
102 998 : else if (ctx->engine)
103 : {
104 : /* Attempt to reset an existing engine. */
105 :
106 300 : err = _gpgme_engine_reset (ctx->engine);
107 300 : if (gpg_err_code (err) == GPG_ERR_NOT_IMPLEMENTED)
108 : {
109 296 : _gpgme_engine_release (ctx->engine);
110 296 : ctx->engine = NULL;
111 : }
112 : }
113 :
114 1014 : if (!ctx->engine)
115 : {
116 : gpgme_engine_info_t info;
117 994 : info = ctx->engine_info;
118 2528 : while (info && info->protocol != ctx->protocol)
119 540 : info = info->next;
120 :
121 994 : if (!info)
122 0 : return gpg_error (GPG_ERR_UNSUPPORTED_PROTOCOL);
123 :
124 : /* Create an engine object. */
125 994 : err = _gpgme_engine_new (info, &ctx->engine);
126 994 : if (err)
127 12 : return err;
128 : }
129 :
130 1002 : if (!reuse_engine)
131 : {
132 986 : err = 0;
133 : #ifdef LC_CTYPE
134 986 : err = _gpgme_engine_set_locale (ctx->engine, LC_CTYPE, ctx->lc_ctype);
135 : #endif
136 : #ifdef LC_MESSAGES
137 985 : if (!err)
138 767 : err = _gpgme_engine_set_locale (ctx->engine,
139 767 : LC_MESSAGES, ctx->lc_messages);
140 : #endif
141 986 : if (gpg_err_code (err) == GPG_ERR_NOT_IMPLEMENTED)
142 218 : err = 0;
143 :
144 986 : _gpgme_engine_set_engine_flags (ctx->engine, ctx);
145 :
146 985 : if (!err)
147 : {
148 985 : err = _gpgme_engine_set_pinentry_mode (ctx->engine,
149 : ctx->pinentry_mode);
150 986 : if (gpg_err_code (err) == GPG_ERR_NOT_IMPLEMENTED)
151 232 : err = 0;
152 : }
153 :
154 986 : if (!err && ctx->status_cb && ctx->full_status)
155 : {
156 4 : _gpgme_engine_set_status_cb (ctx->engine,
157 : ctx->status_cb, ctx->status_cb_value);
158 : }
159 :
160 986 : if (err)
161 : {
162 0 : _gpgme_engine_release (ctx->engine);
163 0 : ctx->engine = NULL;
164 0 : return err;
165 : }
166 : }
167 :
168 1002 : if (ctx->sub_protocol != GPGME_PROTOCOL_DEFAULT)
169 : {
170 0 : err = _gpgme_engine_set_protocol (ctx->engine, ctx->sub_protocol);
171 0 : if (err)
172 0 : return err;
173 : }
174 :
175 1002 : if (type == 1 || (type == 2 && !ctx->io_cbs.add))
176 : {
177 : /* Use private event loop. */
178 998 : io_cbs.add = _gpgme_add_io_cb;
179 998 : io_cbs.add_priv = ctx;
180 998 : io_cbs.remove = _gpgme_remove_io_cb;
181 998 : io_cbs.event = _gpgme_wait_private_event_cb;
182 998 : io_cbs.event_priv = ctx;
183 : }
184 4 : else if (! ctx->io_cbs.add)
185 : {
186 : /* Use global event loop. */
187 3 : io_cbs.add = _gpgme_add_io_cb;
188 3 : io_cbs.add_priv = ctx;
189 3 : io_cbs.remove = _gpgme_remove_io_cb;
190 3 : io_cbs.event = _gpgme_wait_global_event_cb;
191 3 : io_cbs.event_priv = ctx;
192 : }
193 : else
194 : {
195 : /* Use user event loop. */
196 1 : io_cbs.add = _gpgme_wait_user_add_io_cb;
197 1 : io_cbs.add_priv = ctx;
198 1 : io_cbs.remove = _gpgme_wait_user_remove_io_cb;
199 1 : io_cbs.event = _gpgme_wait_user_event_cb;
200 1 : io_cbs.event_priv = ctx;
201 : }
202 1002 : _gpgme_engine_set_io_cbs (ctx->engine, &io_cbs);
203 1002 : return err;
204 : }
205 :
206 :
207 : /* Parse the INV_RECP or INV_SNDR status line in ARGS and return the
208 : result in KEY. If KC_FPR (from the KEY_CONSIDERED status line) is
209 : not NULL take the KC_FLAGS in account. */
210 : gpgme_error_t
211 6 : _gpgme_parse_inv_recp (char *args, int for_signing,
212 : const char *kc_fpr, unsigned int kc_flags,
213 : gpgme_invalid_key_t *key)
214 : {
215 : gpgme_invalid_key_t inv_key;
216 : char *tail;
217 : long int reason;
218 :
219 : (void)for_signing;
220 :
221 6 : inv_key = calloc (1, sizeof (*inv_key));
222 6 : if (!inv_key)
223 0 : return gpg_error_from_syserror ();
224 6 : inv_key->next = NULL;
225 6 : gpg_err_set_errno (0);
226 6 : reason = strtol (args, &tail, 0);
227 6 : if (errno || args == tail || (*tail && *tail != ' '))
228 : {
229 : /* The crypto backend does not behave. */
230 0 : free (inv_key);
231 0 : return trace_gpg_error (GPG_ERR_INV_ENGINE);
232 : }
233 :
234 6 : switch (reason)
235 : {
236 : case 0:
237 2 : if (kc_fpr && (kc_flags & 2))
238 2 : inv_key->reason = gpg_error (GPG_ERR_SUBKEYS_EXP_OR_REV);
239 : else
240 0 : inv_key->reason = gpg_error (GPG_ERR_GENERAL);
241 2 : break;
242 :
243 : case 1:
244 0 : inv_key->reason = gpg_error (GPG_ERR_NO_PUBKEY);
245 0 : break;
246 :
247 : case 2:
248 0 : inv_key->reason = gpg_error (GPG_ERR_AMBIGUOUS_NAME);
249 0 : break;
250 :
251 : case 3:
252 0 : inv_key->reason = gpg_error (GPG_ERR_WRONG_KEY_USAGE);
253 0 : break;
254 :
255 : case 4:
256 0 : inv_key->reason = gpg_error (GPG_ERR_CERT_REVOKED);
257 0 : break;
258 :
259 : case 5:
260 0 : inv_key->reason = gpg_error (GPG_ERR_CERT_EXPIRED);
261 0 : break;
262 :
263 : case 6:
264 0 : inv_key->reason = gpg_error (GPG_ERR_NO_CRL_KNOWN);
265 0 : break;
266 :
267 : case 7:
268 0 : inv_key->reason = gpg_error (GPG_ERR_CRL_TOO_OLD);
269 0 : break;
270 :
271 : case 8:
272 0 : inv_key->reason = gpg_error (GPG_ERR_NO_POLICY_MATCH);
273 0 : break;
274 :
275 : case 9:
276 4 : inv_key->reason = gpg_error (GPG_ERR_NO_SECKEY);
277 4 : break;
278 :
279 : case 10:
280 0 : inv_key->reason = gpg_error (GPG_ERR_PUBKEY_NOT_TRUSTED);
281 0 : break;
282 :
283 : case 11:
284 0 : inv_key->reason = gpg_error (GPG_ERR_MISSING_CERT);
285 0 : break;
286 :
287 : case 12:
288 0 : inv_key->reason = gpg_error (GPG_ERR_MISSING_ISSUER_CERT);
289 0 : break;
290 :
291 : case 13:
292 0 : inv_key->reason = gpg_error (252); /*GPG_ERR_KEY_DISABLED*/
293 0 : break;
294 :
295 : case 14:
296 0 : inv_key->reason = gpg_error (GPG_ERR_INV_USER_ID);
297 0 : break;
298 :
299 : default:
300 0 : inv_key->reason = gpg_error (GPG_ERR_GENERAL);
301 0 : break;
302 : }
303 :
304 18 : while (*tail && *tail == ' ')
305 6 : tail++;
306 6 : if (*tail)
307 : {
308 6 : inv_key->fpr = strdup (tail);
309 6 : if (!inv_key->fpr)
310 : {
311 0 : free (inv_key);
312 0 : return gpg_error_from_syserror ();
313 : }
314 : }
315 :
316 6 : *key = inv_key;
317 6 : return 0;
318 : }
319 :
320 :
321 :
322 : /* Parse a KEY_CONSIDERED status line in ARGS and store the
323 : * fingerprint and the flags at R_FPR and R_FLAGS. The caller must
324 : * free the value at R_FPR on success. */
325 : gpgme_error_t
326 256 : _gpgme_parse_key_considered (const char *args,
327 : char **r_fpr, unsigned int *r_flags)
328 : {
329 : char *pend;
330 : size_t n;
331 :
332 256 : *r_fpr = NULL;
333 :
334 256 : pend = strchr (args, ' ');
335 256 : if (!pend || pend == args)
336 0 : return trace_gpg_error (GPG_ERR_INV_ENGINE); /* Bogus status line. */
337 256 : n = pend - args;
338 256 : *r_fpr = malloc (n + 1);
339 256 : if (!*r_fpr)
340 0 : return gpg_error_from_syserror ();
341 256 : memcpy (*r_fpr, args, n);
342 256 : (*r_fpr)[n] = 0;
343 256 : args = pend + 1;
344 :
345 256 : gpg_err_set_errno (0);
346 256 : *r_flags = strtoul (args, &pend, 0);
347 256 : if (errno || args == pend || (*pend && *pend != ' '))
348 : {
349 0 : free (*r_fpr);
350 0 : *r_fpr = NULL;
351 0 : return trace_gpg_error (GPG_ERR_INV_ENGINE);
352 : }
353 :
354 256 : return 0;
355 : }
356 :
357 :
358 : /* Parse the PLAINTEXT status line in ARGS and return the result in
359 : FILENAMEP. */
360 : gpgme_error_t
361 101 : _gpgme_parse_plaintext (char *args, char **filenamep, int *r_mime)
362 : {
363 : char *tail;
364 :
365 202 : while (*args == ' ')
366 0 : args++;
367 101 : if (*args == '\0')
368 0 : return 0;
369 :
370 : /* First argument is file type (a one byte uppercase hex value). */
371 101 : if (args[0] == '6' && args[1] == 'D')
372 0 : *r_mime = 1;
373 404 : while (*args != ' ' && *args != '\0')
374 202 : args++;
375 303 : while (*args == ' ')
376 101 : args++;
377 101 : if (*args == '\0')
378 0 : return 0;
379 :
380 : /* Second argument is the timestamp. */
381 1212 : while (*args != ' ' && *args != '\0')
382 1010 : args++;
383 303 : while (*args == ' ')
384 101 : args++;
385 101 : if (*args == '\0')
386 44 : return 0;
387 :
388 57 : tail = args;
389 666 : while (*tail != ' ' && *tail != '\0')
390 552 : tail++;
391 57 : *tail = '\0';
392 57 : if (filenamep && *args != '\0')
393 : {
394 57 : char *filename = strdup (args);
395 57 : if (!filename)
396 0 : return gpg_error_from_syserror ();
397 :
398 57 : *filenamep = filename;
399 : }
400 57 : return 0;
401 : }
402 :
403 :
404 : /* Parse a FAILURE status line and return the error code. ARGS is
405 : * modified to contain the location part. Note that for now we ignore
406 : * failure codes with a location of gpg-exit; they are too trouble
407 : * some. Instead we should eventually record that error in the
408 : * context and provide a function to return a fuller error
409 : * description; this could then also show the location of the error
410 : * (e.g. "option- parser") to make it easier for the user to detect
411 : * the actual error. */
412 : gpgme_error_t
413 13 : _gpgme_parse_failure (char *args)
414 : {
415 : char *where, *which;
416 :
417 13 : if (!strncmp (args, "gpg-exit", 8))
418 5 : return 0;
419 :
420 8 : where = strchr (args, ' ');
421 8 : if (!where)
422 2 : return trace_gpg_error (GPG_ERR_INV_ENGINE);
423 :
424 6 : *where = '\0';
425 6 : which = where + 1;
426 :
427 6 : where = strchr (which, ' ');
428 6 : if (where)
429 0 : *where = '\0';
430 :
431 6 : return atoi (which);
432 : }
|