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