Line | Count | Source |
1 | | /* photoid.c - photo ID handling code |
2 | | * Copyright (C) 2001, 2002, 2005, 2006, 2008, 2011 Free Software Foundation, Inc. |
3 | | * |
4 | | * This file is part of GnuPG. |
5 | | * |
6 | | * GnuPG is free software; you can redistribute it and/or modify |
7 | | * it under the terms of the GNU General Public License as published by |
8 | | * the Free Software Foundation; either version 3 of the License, or |
9 | | * (at your option) any later version. |
10 | | * |
11 | | * GnuPG is distributed in the hope that it will be useful, |
12 | | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
13 | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
14 | | * GNU General Public License for more details. |
15 | | * |
16 | | * You should have received a copy of the GNU General Public License |
17 | | * along with this program; if not, see <https://www.gnu.org/licenses/>. |
18 | | */ |
19 | | |
20 | | #include <config.h> |
21 | | #include <errno.h> |
22 | | #include <stdio.h> |
23 | | #include <string.h> |
24 | | #include <unistd.h> |
25 | | #ifdef _WIN32 |
26 | | # ifdef HAVE_WINSOCK2_H |
27 | | # include <winsock2.h> |
28 | | # endif |
29 | | # include <windows.h> |
30 | | #endif |
31 | | |
32 | | #include "gpg.h" |
33 | | #include "../common/util.h" |
34 | | #include "packet.h" |
35 | | #include "../common/status.h" |
36 | | #include "keydb.h" |
37 | | #include "../common/i18n.h" |
38 | | #include "../common/iobuf.h" |
39 | | #include "options.h" |
40 | | #include "main.h" |
41 | | #include "photoid.h" |
42 | | #include "../common/ttyio.h" |
43 | | #include "trustdb.h" |
44 | | |
45 | | #if defined (_WIN32) |
46 | | /* This is a nicer system() for windows that waits for programs to |
47 | | return before returning control to the caller. I hate helpful |
48 | | computers. */ |
49 | | static int |
50 | | w32_system (const char *command) |
51 | | { |
52 | | if (!strncmp (command, "!ShellExecute ", 14)) |
53 | | { |
54 | | SHELLEXECUTEINFOW see; |
55 | | wchar_t *wname; |
56 | | int waitms; |
57 | | |
58 | | command = command + 14; |
59 | | while (spacep (command)) |
60 | | command++; |
61 | | waitms = atoi (command); |
62 | | if (waitms < 0) |
63 | | waitms = 0; |
64 | | else if (waitms > 60*1000) |
65 | | waitms = 60000; |
66 | | while (*command && !spacep (command)) |
67 | | command++; |
68 | | while (spacep (command)) |
69 | | command++; |
70 | | |
71 | | wname = utf8_to_wchar (command); |
72 | | if (!wname) |
73 | | return -1; |
74 | | |
75 | | memset (&see, 0, sizeof see); |
76 | | see.cbSize = sizeof see; |
77 | | see.fMask = (SEE_MASK_NOCLOSEPROCESS |
78 | | | SEE_MASK_NOASYNC |
79 | | | SEE_MASK_FLAG_NO_UI |
80 | | | SEE_MASK_NO_CONSOLE); |
81 | | see.lpVerb = L"open"; |
82 | | see.lpFile = (LPCWSTR)wname; |
83 | | see.nShow = SW_SHOW; |
84 | | |
85 | | if (DBG_EXTPROG) |
86 | | log_debug ("running ShellExecuteEx(open,'%s')\n", command); |
87 | | if (!ShellExecuteExW (&see)) |
88 | | { |
89 | | if (DBG_EXTPROG) |
90 | | log_debug ("ShellExecuteEx failed: rc=%d\n", (int)GetLastError ()); |
91 | | xfree (wname); |
92 | | return -1; |
93 | | } |
94 | | if (DBG_EXTPROG) |
95 | | { |
96 | | /* hInstApp has HINSTANCE type. The documentations says |
97 | | that it's not a true HINSTANCE and it can be cast only to |
98 | | an int. */ |
99 | | int hinstance = (intptr_t)see.hInstApp; |
100 | | |
101 | | log_debug ("ShellExecuteEx succeeded (hProcess=%p,hInstApp=%d)\n", |
102 | | see.hProcess, hinstance); |
103 | | } |
104 | | |
105 | | if (!see.hProcess) |
106 | | { |
107 | | gnupg_usleep (waitms*1000); |
108 | | if (DBG_EXTPROG) |
109 | | log_debug ("ShellExecuteEx ready (wait=%dms)\n", waitms); |
110 | | } |
111 | | else |
112 | | { |
113 | | WaitForSingleObject (see.hProcess, INFINITE); |
114 | | if (DBG_EXTPROG) |
115 | | log_debug ("ShellExecuteEx ready\n"); |
116 | | } |
117 | | CloseHandle (see.hProcess); |
118 | | |
119 | | xfree (wname); |
120 | | } |
121 | | else |
122 | | { |
123 | | char *string; |
124 | | wchar_t *wstring; |
125 | | PROCESS_INFORMATION pi; |
126 | | STARTUPINFOW si; |
127 | | |
128 | | /* We must use a copy of the command as CreateProcess modifies |
129 | | * this argument. */ |
130 | | string = xstrdup (command); |
131 | | wstring = utf8_to_wchar (string); |
132 | | xfree (string); |
133 | | if (!wstring) |
134 | | return -1; |
135 | | |
136 | | memset (&pi, 0, sizeof(pi)); |
137 | | memset (&si, 0, sizeof(si)); |
138 | | si.cb = sizeof (si); |
139 | | |
140 | | if (!CreateProcessW (NULL, wstring, NULL, NULL, FALSE, |
141 | | DETACHED_PROCESS, |
142 | | NULL, NULL, &si, &pi)) |
143 | | { |
144 | | xfree (wstring); |
145 | | return -1; |
146 | | } |
147 | | |
148 | | /* Wait for the child to exit */ |
149 | | WaitForSingleObject (pi.hProcess, INFINITE); |
150 | | |
151 | | CloseHandle (pi.hProcess); |
152 | | CloseHandle (pi.hThread); |
153 | | xfree (wstring); |
154 | | } |
155 | | |
156 | | return 0; |
157 | | } |
158 | | #endif /*_W32*/ |
159 | | |
160 | | /* Generate a new photo id packet, or return NULL if canceled. |
161 | | FIXME: Should we add a duplicates check similar to generate_user_id? */ |
162 | | PKT_user_id * |
163 | | generate_photo_id (ctrl_t ctrl, PKT_public_key *pk,const char *photo_name) |
164 | 0 | { |
165 | 0 | PKT_user_id *uid; |
166 | 0 | int error=1,i; |
167 | 0 | uint64_t len; |
168 | 0 | char *filename; |
169 | 0 | byte *photo=NULL; |
170 | 0 | byte header[16]; |
171 | 0 | IOBUF file; |
172 | |
|
173 | 0 | header[0]=0x10; /* little side of photo header length */ |
174 | 0 | header[1]=0; /* big side of photo header length */ |
175 | 0 | header[2]=1; /* 1 == version of photo header */ |
176 | 0 | header[3]=1; /* 1 == JPEG */ |
177 | |
|
178 | 0 | for(i=4;i<16;i++) /* The reserved bytes */ |
179 | 0 | header[i]=0; |
180 | |
|
181 | 0 | #define EXTRA_UID_NAME_SPACE 71 |
182 | 0 | uid=xmalloc_clear(sizeof(*uid)+71); |
183 | |
|
184 | 0 | if(photo_name && *photo_name) |
185 | 0 | filename=make_filename(photo_name,(void *)NULL); |
186 | 0 | else |
187 | 0 | { |
188 | 0 | tty_printf(_("\nPick an image to use for your photo ID." |
189 | 0 | " The image must be a JPEG file.\n" |
190 | 0 | "Remember that the image is stored within your public key." |
191 | 0 | " If you use a\n" |
192 | 0 | "very large picture, your key will become very large" |
193 | 0 | " as well!\n" |
194 | 0 | "Keeping the image close to 240x288 is a good size" |
195 | 0 | " to use.\n")); |
196 | 0 | filename=NULL; |
197 | 0 | } |
198 | |
|
199 | 0 | while(photo==NULL) |
200 | 0 | { |
201 | 0 | if(filename==NULL) |
202 | 0 | { |
203 | 0 | char *tempname; |
204 | |
|
205 | 0 | tty_printf("\n"); |
206 | |
|
207 | 0 | tty_enable_completion(NULL); |
208 | |
|
209 | 0 | tempname=cpr_get("photoid.jpeg.add", |
210 | 0 | _("Enter JPEG filename for photo ID: ")); |
211 | |
|
212 | 0 | tty_disable_completion(); |
213 | |
|
214 | 0 | filename=make_filename(tempname,(void *)NULL); |
215 | |
|
216 | 0 | xfree(tempname); |
217 | |
|
218 | 0 | if(strlen(filename)==0) |
219 | 0 | goto scram; |
220 | 0 | } |
221 | | |
222 | 0 | file=iobuf_open(filename); |
223 | 0 | if (file && is_secured_file (iobuf_get_fd (file))) |
224 | 0 | { |
225 | 0 | iobuf_close (file); |
226 | 0 | file = NULL; |
227 | 0 | gpg_err_set_errno (EPERM); |
228 | 0 | } |
229 | 0 | if(!file) |
230 | 0 | { |
231 | 0 | log_error(_("unable to open JPEG file '%s': %s\n"), |
232 | 0 | filename,strerror(errno)); |
233 | 0 | xfree(filename); |
234 | 0 | filename=NULL; |
235 | 0 | continue; |
236 | 0 | } |
237 | | |
238 | | |
239 | 0 | len = iobuf_get_filelength(file); |
240 | 0 | if(len>6144) |
241 | 0 | { |
242 | | /* We silently skip JPEGs larger than 1MiB because we have a |
243 | | * 2MiB limit on the user ID packets and we need some limit |
244 | | * anyway because the returned u64 is larger than the u32 or |
245 | | * OpenPGP. Note that the diagnostic may print a wrong |
246 | | * value if the value is really large; we don't fix this to |
247 | | * avoid a string change. */ |
248 | 0 | tty_printf( _("This JPEG is really large (%d bytes) !\n"), (int)len); |
249 | 0 | if(len > 1024*1024 |
250 | 0 | || !cpr_get_answer_is_yes("photoid.jpeg.size", |
251 | 0 | _("Are you sure you want to use it? (y/N) "))) |
252 | 0 | { |
253 | 0 | iobuf_close(file); |
254 | 0 | xfree(filename); |
255 | 0 | filename=NULL; |
256 | 0 | continue; |
257 | 0 | } |
258 | 0 | } |
259 | | |
260 | 0 | photo=xmalloc(len); |
261 | 0 | iobuf_read(file,photo,len); |
262 | 0 | iobuf_close(file); |
263 | | |
264 | | /* Is it a JPEG? */ |
265 | 0 | if(photo[0]!=0xFF || photo[1]!=0xD8) |
266 | 0 | { |
267 | 0 | log_error(_("'%s' is not a JPEG file\n"),filename); |
268 | 0 | xfree(photo); |
269 | 0 | photo=NULL; |
270 | 0 | xfree(filename); |
271 | 0 | filename=NULL; |
272 | 0 | continue; |
273 | 0 | } |
274 | | |
275 | | /* Build the packet */ |
276 | 0 | build_attribute_subpkt(uid,1,photo,len,header,16); |
277 | 0 | parse_attribute_subpkts(uid); |
278 | 0 | make_attribute_uidname(uid, EXTRA_UID_NAME_SPACE); |
279 | | |
280 | | /* Showing the photo is not safe when noninteractive since the |
281 | | "user" may not be able to dismiss a viewer window! */ |
282 | 0 | if(opt.command_fd==-1) |
283 | 0 | { |
284 | 0 | show_photos (ctrl, uid->attribs, uid->numattribs, pk, uid); |
285 | 0 | switch(cpr_get_answer_yes_no_quit("photoid.jpeg.okay", |
286 | 0 | _("Is this photo correct (y/N/q)? "))) |
287 | 0 | { |
288 | 0 | case -1: |
289 | 0 | goto scram; |
290 | 0 | case 0: |
291 | 0 | free_attributes(uid); |
292 | 0 | xfree(photo); |
293 | 0 | photo=NULL; |
294 | 0 | xfree(filename); |
295 | 0 | filename=NULL; |
296 | 0 | continue; |
297 | 0 | } |
298 | 0 | } |
299 | 0 | } |
300 | | |
301 | 0 | error=0; |
302 | 0 | uid->ref=1; |
303 | |
|
304 | 0 | scram: |
305 | 0 | xfree(filename); |
306 | 0 | xfree(photo); |
307 | |
|
308 | 0 | if(error) |
309 | 0 | { |
310 | 0 | free_attributes(uid); |
311 | 0 | xfree(uid); |
312 | 0 | return NULL; |
313 | 0 | } |
314 | | |
315 | 0 | return uid; |
316 | 0 | } |
317 | | |
318 | | /* Returns 0 for error, 1 for valid */ |
319 | | int parse_image_header(const struct user_attribute *attr,byte *type,u32 *len) |
320 | 421 | { |
321 | 421 | u16 headerlen; |
322 | | |
323 | 421 | if(attr->len<3) |
324 | 7 | return 0; |
325 | | |
326 | | /* For historical reasons (i.e. "oops!"), the header length is |
327 | | little endian. */ |
328 | 414 | headerlen=(attr->data[1]<<8) | attr->data[0]; |
329 | | |
330 | 414 | if(headerlen>attr->len) |
331 | 56 | return 0; |
332 | | |
333 | 358 | if(type && attr->len>=4) |
334 | 273 | { |
335 | 273 | if(attr->data[2]==1) /* header version 1 */ |
336 | 164 | *type=attr->data[3]; |
337 | 109 | else |
338 | 109 | *type=0; |
339 | 273 | } |
340 | | |
341 | 358 | *len=attr->len-headerlen; |
342 | | |
343 | 358 | if(*len==0) |
344 | 33 | return 0; |
345 | | |
346 | 325 | return 1; |
347 | 358 | } |
348 | | |
349 | | /* style==0 for extension, 1 for name, 2 for MIME type. Remember that |
350 | | the "name" style string could be used in a user ID name field, so |
351 | | make sure it is not too big (see parse-packet.c:parse_attribute). |
352 | | Extensions should be 3 characters long for the best cross-platform |
353 | | compatibility. */ |
354 | | const char * |
355 | | image_type_to_string(byte type,int style) |
356 | 325 | { |
357 | 325 | const char *string; |
358 | | |
359 | 325 | switch(type) |
360 | 325 | { |
361 | 114 | case 1: /* jpeg */ |
362 | 114 | if(style==0) |
363 | 0 | string="jpg"; |
364 | 114 | else if(style==1) |
365 | 114 | string="jpeg"; |
366 | 0 | else |
367 | 0 | string="image/jpeg"; |
368 | 114 | break; |
369 | | |
370 | 211 | default: |
371 | 211 | if(style==0) |
372 | 0 | string="bin"; |
373 | 211 | else if(style==1) |
374 | 211 | string="unknown"; |
375 | 0 | else |
376 | 0 | string="image/x-unknown"; |
377 | 211 | break; |
378 | 325 | } |
379 | | |
380 | 325 | return string; |
381 | 325 | } |
382 | | |
383 | | #if !defined(FIXED_PHOTO_VIEWER) && !defined(DISABLE_PHOTO_VIEWER) |
384 | | static const char * |
385 | | get_default_photo_command(void) |
386 | 0 | { |
387 | | #if defined(_WIN32) |
388 | | return "!ShellExecute 400 %i"; |
389 | | #elif defined(__APPLE__) |
390 | | /* OS X. This really needs more than just __APPLE__. */ |
391 | | return "open %I"; |
392 | | #else |
393 | 0 | if (!path_access ("xloadimage", X_OK)) |
394 | 0 | return "xloadimage -fork -quiet -title 'KeyID 0x%k' stdin"; |
395 | 0 | else if (!path_access ("display",X_OK)) |
396 | 0 | return "display -title 'KeyID 0x%k' %i"; |
397 | 0 | else if (getuid () && !path_access ("xdg-open", X_OK)) |
398 | 0 | { |
399 | | /* xdg-open spawns the actual program and exits so we need to |
400 | | * keep the temp file */ |
401 | 0 | return "xdg-open %I"; |
402 | 0 | } |
403 | 0 | else |
404 | 0 | return "/bin/true"; |
405 | 0 | #endif |
406 | 0 | } |
407 | | #endif |
408 | | |
409 | | #ifndef DISABLE_PHOTO_VIEWER |
410 | | struct spawn_info |
411 | | { |
412 | | unsigned int keep_temp_file; |
413 | | char *command; |
414 | | char *tempdir; |
415 | | char *tempfile; |
416 | | }; |
417 | | |
418 | | #ifdef NO_EXEC |
419 | | static void |
420 | | show_photo (const char *command, const char *name, const void *image, u32 len) |
421 | | { |
422 | | log_error(_("no remote program execution supported\n")); |
423 | | } |
424 | | #else /* ! NO_EXEC */ |
425 | | #include "../common/membuf.h" |
426 | | |
427 | | /* Makes a temp directory and filenames */ |
428 | | static int |
429 | | setup_input_file (struct spawn_info *info, const char *name) |
430 | 0 | { |
431 | 0 | char *tmp = opt.temp_dir; |
432 | 0 | int len; |
433 | 0 | #define TEMPLATE "gpg-XXXXXX" |
434 | | |
435 | | /* Initialize by the length of last part in the path + 1 */ |
436 | 0 | len = strlen (DIRSEP_S) + strlen (TEMPLATE) + 1; |
437 | | |
438 | | /* Make up the temp dir and file in case we need them */ |
439 | 0 | if (tmp) |
440 | 0 | { |
441 | 0 | len += strlen (tmp); |
442 | 0 | info->tempdir = xmalloc (len); |
443 | 0 | } |
444 | 0 | else |
445 | 0 | { |
446 | | #if defined (_WIN32) |
447 | | int ret; |
448 | | |
449 | | tmp = xmalloc (MAX_PATH+1); |
450 | | if (!tmp) |
451 | | return -1; |
452 | | |
453 | | ret = GetTempPath (MAX_PATH-len, tmp); |
454 | | if (ret == 0 || ret >= MAX_PATH-len) |
455 | | strcpy (tmp, "c:\\windows\\temp"); |
456 | | else |
457 | | { |
458 | | /* GetTempPath may return with \ on the end */ |
459 | | while (ret > 0 && tmp[ret-1] == '\\') |
460 | | { |
461 | | tmp[ret-1]='\0'; |
462 | | ret--; |
463 | | } |
464 | | } |
465 | | |
466 | | len += ret; |
467 | | info->tempdir = tmp; |
468 | | #else /* More unixish systems */ |
469 | 0 | if (!(tmp = getenv ("TMPDIR")) |
470 | 0 | && !(tmp = getenv ("TMP"))) |
471 | 0 | tmp = "/tmp"; |
472 | |
|
473 | 0 | len += strlen (tmp); |
474 | 0 | info->tempdir = xmalloc (len); |
475 | 0 | #endif |
476 | 0 | } |
477 | |
|
478 | 0 | if (info->tempdir == NULL) |
479 | 0 | return -1; |
480 | | |
481 | 0 | sprintf (info->tempdir, "%s" DIRSEP_S TEMPLATE, tmp); |
482 | |
|
483 | 0 | if (gnupg_mkdtemp (info->tempdir) == NULL) |
484 | 0 | { |
485 | 0 | log_error (_("can't create directory '%s': %s\n"), |
486 | 0 | info->tempdir, strerror (errno)); |
487 | 0 | return -1; |
488 | 0 | } |
489 | | |
490 | 0 | info->tempfile = xmalloc (strlen (info->tempdir) + strlen (DIRSEP_S) |
491 | 0 | + strlen (name) + 1); |
492 | 0 | if (info->tempfile == NULL) |
493 | 0 | { |
494 | 0 | xfree (info->tempdir); |
495 | 0 | info->tempdir = NULL; |
496 | 0 | return -1; |
497 | 0 | } |
498 | 0 | sprintf (info->tempfile, "%s" DIRSEP_S "%s", info->tempdir, name); |
499 | 0 | return 0; |
500 | 0 | } |
501 | | |
502 | | /* Expands %i or %I in the args to the full temp file within the temp |
503 | | directory. */ |
504 | | static int |
505 | | expand_args (struct spawn_info *info, const char *args_in, const char *name) |
506 | 0 | { |
507 | 0 | const char *ch = args_in; |
508 | 0 | membuf_t command; |
509 | |
|
510 | 0 | info->keep_temp_file = 0; |
511 | |
|
512 | 0 | if (DBG_EXTPROG) |
513 | 0 | log_debug ("expanding string \"%s\"\n", args_in); |
514 | |
|
515 | 0 | init_membuf (&command, 100); |
516 | |
|
517 | 0 | while (*ch != '\0') |
518 | 0 | { |
519 | 0 | if (*ch == '%') |
520 | 0 | { |
521 | 0 | const char *append = NULL; |
522 | |
|
523 | 0 | ch++; |
524 | |
|
525 | 0 | switch (*ch) |
526 | 0 | { |
527 | 0 | case 'I': |
528 | 0 | info->keep_temp_file = 1; |
529 | | /* fall through */ |
530 | |
|
531 | 0 | case 'i': /* in */ |
532 | 0 | if (info->tempfile == NULL) |
533 | 0 | { |
534 | 0 | if (setup_input_file (info, name) < 0) |
535 | 0 | goto fail; |
536 | 0 | } |
537 | 0 | append = info->tempfile; |
538 | 0 | break; |
539 | | |
540 | 0 | case '%': |
541 | 0 | append = "%"; |
542 | 0 | break; |
543 | 0 | } |
544 | | |
545 | 0 | if (append) |
546 | 0 | put_membuf_str (&command, append); |
547 | 0 | } |
548 | 0 | else |
549 | 0 | put_membuf (&command, ch, 1); |
550 | | |
551 | 0 | ch++; |
552 | 0 | } |
553 | | |
554 | 0 | put_membuf (&command, "", 1); /* Terminate string. */ |
555 | |
|
556 | 0 | info->command = get_membuf (&command, NULL); |
557 | 0 | if (!info->command) |
558 | 0 | return -1; |
559 | | |
560 | 0 | if(DBG_EXTPROG) |
561 | 0 | log_debug("args expanded to \"%s\", use %s, keep %u\n", info->command, |
562 | 0 | info->tempfile, info->keep_temp_file); |
563 | |
|
564 | 0 | return 0; |
565 | | |
566 | 0 | fail: |
567 | 0 | xfree (get_membuf (&command, NULL)); |
568 | 0 | return -1; |
569 | 0 | } |
570 | | |
571 | | #ifndef EXEC_TEMPFILE_ONLY |
572 | | static void |
573 | | fill_command_argv (const char *argv[4], const char *command) |
574 | 0 | { |
575 | 0 | argv[0] = getenv ("SHELL"); |
576 | 0 | if (argv[0] == NULL) |
577 | 0 | argv[0] = "/bin/sh"; |
578 | |
|
579 | 0 | argv[1] = "-c"; |
580 | 0 | argv[2] = command; |
581 | 0 | argv[3] = NULL; |
582 | 0 | } |
583 | | #endif |
584 | | |
585 | | static void |
586 | | run_with_pipe (struct spawn_info *info, const void *image, u32 len) |
587 | 0 | { |
588 | | #ifdef EXEC_TEMPFILE_ONLY |
589 | | (void)info; |
590 | | (void)image; |
591 | | (void)len; |
592 | | log_error (_("this platform requires temporary files when calling" |
593 | | " external programs\n")); |
594 | | return; |
595 | | #else /* !EXEC_TEMPFILE_ONLY */ |
596 | 0 | gpg_error_t err; |
597 | 0 | const char *argv[4]; |
598 | 0 | gpgrt_process_t proc; |
599 | |
|
600 | 0 | fill_command_argv (argv, info->command); |
601 | 0 | err = gpgrt_process_spawn (argv[0], argv+1, GPGRT_PROCESS_STDIN_PIPE, |
602 | 0 | NULL, &proc); |
603 | 0 | if (err) |
604 | 0 | log_error (_("unable to execute shell '%s': %s\n"), |
605 | 0 | argv[0], gpg_strerror (err)); |
606 | 0 | else |
607 | 0 | { |
608 | 0 | int fd_in; |
609 | |
|
610 | 0 | err = gpgrt_process_get_fds (proc, 0, &fd_in, NULL, NULL); |
611 | 0 | if (err) |
612 | 0 | log_error ("unable to get pipe connection '%s': %s\n", |
613 | 0 | argv[2], gpg_strerror (err)); |
614 | 0 | else |
615 | 0 | { |
616 | 0 | write (fd_in, image, len); |
617 | 0 | close (fd_in); |
618 | 0 | } |
619 | |
|
620 | 0 | err = gpgrt_process_wait (proc, 1); |
621 | 0 | if (err) |
622 | 0 | log_error (_("unnatural exit of external program\n")); |
623 | |
|
624 | 0 | gpgrt_process_release (proc); |
625 | 0 | } |
626 | 0 | #endif /* !EXEC_TEMPFILE_ONLY */ |
627 | 0 | } |
628 | | |
629 | | static int |
630 | | create_temp_file (struct spawn_info *info, const void *ptr, u32 len) |
631 | 0 | { |
632 | 0 | if (DBG_EXTPROG) |
633 | 0 | log_debug ("using temp file '%s'\n", info->tempfile); |
634 | | |
635 | | /* It's not fork/exec/pipe, so create a temp file */ |
636 | 0 | if ( is_secured_filename (info->tempfile) ) |
637 | 0 | { |
638 | 0 | log_error (_("can't create '%s': %s\n"), |
639 | 0 | info->tempfile, strerror (EPERM)); |
640 | 0 | gpg_err_set_errno (EPERM); |
641 | 0 | return -1; |
642 | 0 | } |
643 | 0 | else |
644 | 0 | { |
645 | 0 | estream_t fp = es_fopen (info->tempfile, "wb"); |
646 | |
|
647 | 0 | if (fp) |
648 | 0 | { |
649 | 0 | es_fwrite (ptr, len, 1, fp); |
650 | 0 | es_fclose (fp); |
651 | 0 | return 0; |
652 | 0 | } |
653 | 0 | else |
654 | 0 | { |
655 | 0 | int save = errno; |
656 | 0 | log_error (_("can't create '%s': %s\n"), |
657 | 0 | info->tempfile, strerror(errno)); |
658 | 0 | gpg_err_set_errno (save); |
659 | 0 | return -1; |
660 | 0 | } |
661 | 0 | } |
662 | 0 | } |
663 | | |
664 | | static void |
665 | | show_photo (const char *command, const char *name, const void *image, u32 len) |
666 | 0 | { |
667 | 0 | struct spawn_info *spawn; |
668 | |
|
669 | 0 | spawn = xmalloc_clear (sizeof (struct spawn_info)); |
670 | 0 | if (!spawn) |
671 | 0 | return; |
672 | | |
673 | | /* Expand the args */ |
674 | 0 | if (expand_args (spawn, command, name) < 0) |
675 | 0 | { |
676 | 0 | xfree (spawn); |
677 | 0 | return; |
678 | 0 | } |
679 | | |
680 | 0 | if (DBG_EXTPROG) |
681 | 0 | log_debug ("running command: %s\n", spawn->command); |
682 | |
|
683 | 0 | if (spawn->tempfile == NULL) |
684 | 0 | run_with_pipe (spawn, image, len); |
685 | 0 | else if (create_temp_file (spawn, image, len) == 0) |
686 | 0 | { |
687 | | #if defined (_WIN32) |
688 | | if (w32_system (spawn->command) < 0) |
689 | | log_error (_("system error while calling external program: %s\n"), |
690 | | strerror (errno)); |
691 | | #else |
692 | 0 | gpg_error_t err; |
693 | 0 | const char *argv[4]; |
694 | |
|
695 | 0 | fill_command_argv (argv, spawn->command); |
696 | 0 | err = gpgrt_process_spawn (argv[0], argv+1, 0, NULL, NULL); |
697 | 0 | if (err) |
698 | 0 | log_error (_("unnatural exit of external program\n")); |
699 | 0 | #endif |
700 | |
|
701 | 0 | if (!spawn->keep_temp_file) |
702 | 0 | { |
703 | 0 | if (unlink (spawn->tempfile) < 0) |
704 | 0 | log_info (_("WARNING: unable to remove tempfile (%s) '%s': %s\n"), |
705 | 0 | "in", spawn->tempfile, strerror(errno)); |
706 | |
|
707 | 0 | if (rmdir (spawn->tempdir) < 0) |
708 | 0 | log_info (_("WARNING: unable to remove temp directory '%s': %s\n"), |
709 | 0 | spawn->tempdir, strerror(errno)); |
710 | 0 | } |
711 | 0 | } |
712 | |
|
713 | 0 | xfree(spawn->command); |
714 | 0 | xfree(spawn->tempdir); |
715 | 0 | xfree(spawn->tempfile); |
716 | 0 | xfree(spawn); |
717 | 0 | } |
718 | | #endif |
719 | | #endif |
720 | | |
721 | | |
722 | | void |
723 | | show_photos (ctrl_t ctrl, const struct user_attribute *attrs, int count, |
724 | | PKT_public_key *pk, PKT_user_id *uid) |
725 | 0 | { |
726 | | #ifdef DISABLE_PHOTO_VIEWER |
727 | | (void)attrs; |
728 | | (void)count; |
729 | | (void)pk; |
730 | | (void)uid; |
731 | | #else /*!DISABLE_PHOTO_VIEWER*/ |
732 | 0 | int i; |
733 | 0 | struct expando_args args; |
734 | 0 | u32 len; |
735 | 0 | u32 kid[2]={0,0}; |
736 | |
|
737 | 0 | if (opt.exec_disable && !opt.no_perm_warn) |
738 | 0 | { |
739 | 0 | log_info (_("external program calls are disabled due to unsafe " |
740 | 0 | "options file permissions\n")); |
741 | 0 | return; |
742 | 0 | } |
743 | | |
744 | | #if defined(HAVE_GETUID) && defined(HAVE_GETEUID) |
745 | | /* There should be no way to get to this spot while still carrying |
746 | | setuid privs. Just in case, bomb out if we are. */ |
747 | | if ( getuid () != geteuid ()) |
748 | | BUG (); |
749 | | #endif |
750 | | |
751 | 0 | memset (&args, 0, sizeof(args)); |
752 | 0 | args.pk = pk; |
753 | 0 | args.validity_info = get_validity_info (ctrl, NULL, pk, uid); |
754 | 0 | args.validity_string = get_validity_string (ctrl, pk, uid); |
755 | 0 | namehash_from_uid (uid); |
756 | 0 | args.namehash = uid->namehash; |
757 | |
|
758 | 0 | if (pk) |
759 | 0 | keyid_from_pk (pk, kid); |
760 | |
|
761 | 0 | es_fflush (es_stdout); |
762 | |
|
763 | | #ifdef FIXED_PHOTO_VIEWER |
764 | | opt.photo_viewer = FIXED_PHOTO_VIEWER; |
765 | | #else |
766 | 0 | if (!opt.photo_viewer) |
767 | 0 | opt.photo_viewer = get_default_photo_command (); |
768 | 0 | #endif |
769 | |
|
770 | 0 | for (i=0; i<count; i++) |
771 | 0 | if (attrs[i].type == ATTRIB_IMAGE |
772 | 0 | && parse_image_header (&attrs[i], &args.imagetype, &len)) |
773 | 0 | { |
774 | 0 | char *command, *name; |
775 | 0 | int offset = attrs[i].len-len; |
776 | | |
777 | | /* make command grow */ |
778 | 0 | command = pct_expando (ctrl, opt.photo_viewer,&args); |
779 | 0 | if(!command) |
780 | 0 | goto fail; |
781 | 0 | if (!*command) |
782 | 0 | { |
783 | 0 | xfree (command); |
784 | 0 | goto fail; |
785 | 0 | } |
786 | | |
787 | 0 | name = xmalloc (1 + 16 + strlen(EXTSEP_S) |
788 | 0 | + strlen (image_type_to_string (args.imagetype, 0))); |
789 | |
|
790 | 0 | if (!name) |
791 | 0 | { |
792 | 0 | xfree (command); |
793 | 0 | goto fail; |
794 | 0 | } |
795 | | |
796 | | /* Make the filename. Notice we are not using the image |
797 | | encoding type for more than cosmetics. Most external image |
798 | | viewers can handle a multitude of types, and even if one |
799 | | cannot understand a particular type, we have no way to know |
800 | | which. The spec permits this, by the way. -dms */ |
801 | | |
802 | | #ifdef USE_ONLY_8DOT3 |
803 | | sprintf (name,"%08lX" EXTSEP_S "%s", (ulong)kid[1], |
804 | | image_type_to_string (args.imagetype, 0)); |
805 | | #else |
806 | 0 | sprintf (name, "%08lX%08lX" EXTSEP_S "%s", |
807 | 0 | (ulong)kid[0], (ulong)kid[1], |
808 | 0 | image_type_to_string (args.imagetype, 0)); |
809 | 0 | #endif |
810 | |
|
811 | 0 | show_photo (command, name, &attrs[i].data[offset], len); |
812 | 0 | xfree (name); |
813 | 0 | xfree (command); |
814 | 0 | } |
815 | | |
816 | 0 | return; |
817 | | |
818 | 0 | fail: |
819 | 0 | log_error(_("unable to display photo ID!\n")); |
820 | 0 | #endif /*!DISABLE_PHOTO_VIEWER*/ |
821 | 0 | } |