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