diff --git a/g10/exec.c b/g10/exec.c new file mode 100644 index 000000000..1685cb6cb --- /dev/null +++ b/g10/exec.c @@ -0,0 +1,443 @@ +/* exec.c - generic call-a-program code + * Copyright (C) 2001 Free Software Foundation, Inc. + * + * This file is part of GnuPG. + * + * GnuPG is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * GnuPG is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "options.h" +#include "memory.h" +#include "i18n.h" +#include "iobuf.h" +#include "util.h" +#include "exec.h" + +#ifndef HAVE_MKDTEMP +char *mkdtemp(char *template); +#endif + +/* Makes a temp directory and filenames */ +static int make_tempdir(struct exec_info *info) +{ + const char *tmp=opt.temp_dir; + + /* Make up the temp dir and files in case we need them */ + if(tmp==NULL) + { + tmp=getenv("TMPDIR"); + if(tmp==NULL) + { + tmp=getenv("TMP"); + if(tmp==NULL) + { +#ifdef __riscos__ + tmp=""; +#else + tmp="/tmp"; +#endif + } + } + } + + info->tempdir=m_alloc(strlen(tmp)+1+10+1); + + sprintf(info->tempdir,"%s" DIRSEP_S "gpg-XXXXXX",tmp); + + if(mkdtemp(info->tempdir)==NULL) + log_error(_("%s: can't create directory: %s\n"), + info->tempdir,strerror(errno)); + else + { + info->madedir=1; + + info->tempfile_in=m_alloc(strlen(info->tempdir)+1+10+1); + sprintf(info->tempfile_in,"%s" DIRSEP_S "datain" EXTSEP_S "%s", + info->tempdir,info->binary?"bin":"txt"); + + info->tempfile_out=m_alloc(strlen(info->tempdir)+1+11+1); + sprintf(info->tempfile_out,"%s" DIRSEP_S "dataout" EXTSEP_S "%s", + info->tempdir,info->binary?"bin":"txt"); + } + + return info->madedir?0:G10ERR_GENERAL; +} + +/* Expands %i and %o in the args to the full temp files (within the + temp directory */ +static int expand_args(struct exec_info *info,const char *args_in) +{ + const char *ch=args_in; + int size,len; + + info->use_temp_files=0; + info->keep_temp_files=0; + + size=100; + info->command=m_alloc(size); + len=0; + info->command[0]='\0'; + + while(*ch!='\0') + { + if(*ch=='%') + { + char *append=NULL; + + ch++; + + switch(*ch) + { + case 'O': + info->keep_temp_files=1; + /* fall through */ + + case 'o': /* out */ + if(!info->madedir) + { + if(make_tempdir(info)) + goto fail; + } + append=info->tempfile_out; + info->use_temp_files=1; + break; + + case 'I': + info->keep_temp_files=1; + /* fall through */ + + case 'i': /* in */ + if(!info->madedir) + { + if(make_tempdir(info)) + goto fail; + } + append=info->tempfile_in; + info->use_temp_files=1; + break; + + case '%': + append="%"; + break; + } + + if(append) + { + while(strlen(append)+len>size-1) + { + size+=100; + info->command=m_realloc(info->command,size); + } + + strcat(info->command,append); + len+=strlen(append); + } + } + else + { + if(len==size-1) /* leave room for the \0 */ + { + size+=100; + info->command=m_realloc(info->command,size); + } + + info->command[len++]=*ch; + info->command[len]='\0'; + } + + ch++; + } + + /* printf("args expanded to \"%s\"\n",info->command); */ + + return 0; + + fail: + + m_free(info->command); + info->command=NULL; + + return G10ERR_GENERAL; +} + +/* Either handles the tempfile creation, or the fork/exec. If it + returns ok, then info->tochild is a FILE * that can be written + to. */ + +/* The rules are: if there are no args, then it's a fork/exec/pipe. + If there are args, but no tempfiles, then it's a fork/exec/pipe via + bash -c. If there are tempfiles, then it's a system. */ + +int exec_write(struct exec_info **info,const char *program, + const char *args_in,int writeonly,int binary) +{ + int ret=G10ERR_GENERAL; + + if(opt.exec_disable && !opt.no_perm_warn) + { + log_info(_("external program calls are disabled due to unsafe " + "options file permissions\n")); + + return ret; + } + +#if defined(HAVE_GETUID) && defined(HAVE_GETEUID) + /* There should be no way to get to this spot while still carrying + setuid privs. Just in case, bomb out if we are. */ + if(getuid()!=geteuid()) + BUG(); +#endif + + if(program==NULL && args_in==NULL) + BUG(); + + *info=m_alloc_clear(sizeof(struct exec_info)); + + (*info)->binary=binary; + (*info)->writeonly=writeonly; + + /* Expand the args, if any */ + if(args_in && expand_args(*info,args_in)) + goto fail; + +#ifdef EXEC_TEMPFILE_ONLY + if(!(*info)->use_temp_files) + { + log_error(_("this platform requires temp files when calling external " + "programs\n")); + goto fail; + } + +#else /* !EXEC_TEMPFILE_ONLY */ + + /* If there are no args, or there are args, but no temp files, we + can use fork/exec/pipe */ + if(args_in==NULL || (*info)->use_temp_files==0) + { + int to[2],from[2]; + + if(pipe(to)==-1) + goto fail; + + if(pipe(from)==-1) + { + close(to[0]); + close(to[1]); + goto fail; + } + + if(((*info)->child=fork())==-1) + { + close(to[0]); + close(to[1]); + close(from[0]); + close(from[1]); + goto fail; + } + + if((*info)->child==0) + { + char *shell=getenv("SHELL"); + + if(shell==NULL) + shell="/bin/sh"; + + /* I'm the child */ + + /* If the program isn't going to respond back, they get to + keep their stdout/stderr */ + if(!(*info)->writeonly) + { + /* implied close of STDERR */ + if(dup2(STDOUT_FILENO,STDERR_FILENO)==-1) + _exit(1); + + /* implied close of STDOUT */ + close(from[0]); + if(dup2(from[1],STDOUT_FILENO)==-1) + _exit(1); + } + + /* implied close of STDIN */ + close(to[1]); + if(dup2(to[0],STDIN_FILENO)==-1) + _exit(1); + + if(args_in==NULL) + { + /* fprintf(stderr,"execing: %s\n",program); */ + execlp(program,program,NULL); + } + else + { + /* fprintf(stderr,"execing: %s -c %s\n",shell,(*info)->command); */ + execlp(shell,shell,"-c",(*info)->command,NULL); + } + + /* If we get this far the exec failed. Clean up and return. */ + + log_error(_("unable to execute %s \"%s\": %s\n"), + args_in==NULL?"program":"shell", + args_in==NULL?program:shell, + strerror(errno)); + + if(errno==ENOENT) + _exit(127); + + _exit(1); + } + + /* I'm the parent */ + + close(to[0]); + + (*info)->tochild=fdopen(to[1],binary?"wb":"w"); + if((*info)->tochild==NULL) + { + close(to[1]); + ret=G10ERR_WRITE_FILE; + goto fail; + } + + close(from[1]); + + (*info)->fromchild=iobuf_fdopen(from[0],"r"); + if((*info)->fromchild==NULL) + { + close(from[0]); + ret=G10ERR_READ_FILE; + goto fail; + } + + return 0; + } +#endif /* !EXEC_TEMPFILE_ONLY */ + + /* It's not fork/exec/pipe, so create a temp file */ + (*info)->tochild=fopen((*info)->tempfile_in,binary?"wb":"w"); + if((*info)->tochild==NULL) + { + log_error(_("%s: can't create: %s\n"), + (*info)->tempfile_in,strerror(errno)); + ret=G10ERR_WRITE_FILE; + goto fail; + } + + return 0; + + fail: + exec_finish(*info); + return ret; +} + +int exec_read(struct exec_info *info) +{ + int ret=G10ERR_GENERAL; + + fclose(info->tochild); + info->tochild=NULL; + + if(info->use_temp_files) + { + /* printf("system command is %s\n",info->command); */ + + info->progreturn=system(info->command); + + if(info->progreturn==-1) + { + log_error(_("system error while calling external program: %s\n"), + strerror(errno)); + goto fail; + } + + info->progreturn=WEXITSTATUS(info->progreturn); + + if(info->progreturn==127) + { + log_error(_("unable to execute external program\n")); + goto fail; + } + + if(!info->writeonly) + { + info->fromchild=iobuf_open(info->tempfile_out); + if(info->fromchild==NULL) + { + log_error(_("unable to read external program response: %s\n"), + strerror(errno)); + ret=G10ERR_READ_FILE; + goto fail; + } + } + } + + return 0; + + fail: + exec_finish(info); + return ret; +} + +int exec_finish(struct exec_info *info) +{ + int ret=info->progreturn; + +#ifndef EXEC_TEMPFILE_ONLY + if(info->child>0) + { + if(waitpid(info->child,&info->progreturn,0)!=0 && + WIFEXITED(info->progreturn)) + ret=WEXITSTATUS(info->progreturn); + else + ret=127; + } +#endif + + if(info->fromchild) + iobuf_close(info->fromchild); + + if(info->tochild) + fclose(info->tochild); + + if(info->madedir && !info->keep_temp_files) + { + if(info->tempfile_in) + unlink(info->tempfile_in); + + if(info->tempfile_out) + unlink(info->tempfile_out); + + rmdir(info->tempdir); + } + + m_free(info->command); + m_free(info->tempdir); + m_free(info->tempfile_in); + m_free(info->tempfile_out); + m_free(info); + + return ret; +} diff --git a/g10/exec.h b/g10/exec.h new file mode 100644 index 000000000..48696d34a --- /dev/null +++ b/g10/exec.h @@ -0,0 +1,22 @@ +#ifndef _EXEC_H_ +#define _EXEC_H_ + +#include +#include +#include "iobuf.h" + +struct exec_info +{ + int progreturn,binary,writeonly,madedir,use_temp_files,keep_temp_files; + pid_t child; + FILE *tochild; + IOBUF fromchild; + char *command,*tempdir,*tempfile_in,*tempfile_out; +}; + +int exec_write(struct exec_info **info,const char *program, + const char *args_in,int writeonly,int binary); +int exec_read(struct exec_info *info); +int exec_finish(struct exec_info *info); + +#endif /* !_EXEC_H_ */ diff --git a/g10/photoid.c b/g10/photoid.c new file mode 100644 index 000000000..4cc96de61 --- /dev/null +++ b/g10/photoid.c @@ -0,0 +1,256 @@ +/* photoid.c - photo ID handling code + * Copyright (C) 2001 Free Software Foundation, Inc. + * + * This file is part of GnuPG. + * + * GnuPG is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * GnuPG is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "keydb.h" +#include "i18n.h" +#include "options.h" +#include "memory.h" +#include "status.h" +#include "util.h" +#include "packet.h" +#include "iobuf.h" +#include "exec.h" +#include "photoid.h" + +#define PHOTO_COMMAND_MAXLEN 1024 +#define DEFAULT_PHOTO_COMMAND "xloadimage -fork -quiet -title 'KeyID 0x%k' stdin" +#define PHOTO_FILENAME_TEMPLATE "gnupg-photo-id-XXXXXX" + +/* Generate a new photo id packet, or return NULL if canceled */ +PKT_user_id *generate_photo_id(PKT_public_key *pk) +{ + PKT_user_id *uid; + int error=1,i,len; + char *filename=NULL; + unsigned char *photo=NULL; + byte header[16]; + IOBUF file; + + header[0]=0x10; /* little side of photo header length */ + header[1]=0; /* big side of photo header length */ + header[2]=1; /* 1 == version of photo header */ + header[3]=1; /* 1 == JPEG */ + + for(i=4;i<16;i++) /* The reserved bytes */ + header[i]=0; + + uid=m_alloc_clear(sizeof(*uid)+50); + + printf(_("\nPick an image to use for your photo ID. " + "The image must be a JPEG file.\n" + "Remember that the image is stored within your public key. " + "If you use a\n" + "very large picture, your key will become very large as well!\n" + "Keeping the image close to 240x288 is a good size to use.\n")); + + while(photo==NULL) + { + printf("\n"); + + m_free(filename); + + filename=cpr_get("photoid.jpeg.add", + _("Enter JPEG filename for photo ID: ")); + + if(strlen(filename)==0) + goto scram; + + file=iobuf_open(filename); + if(!file) + { + log_error(_("Unable to open photo \"%s\": %s\n"), + filename,strerror(errno)); + continue; + } + + len=iobuf_get_filelength(file); + if(len>6144) + { + printf("This JPEG is really large (%d bytes) !\n",len); + if(!cpr_get_answer_is_yes("photoid.jpeg.size", + _("Are you sure you want to use it (y/n)? "))) + { + iobuf_close(file); + continue; + } + } + + photo=m_alloc(len); + iobuf_read(file,photo,len); + iobuf_close(file); + + /* Is it a JPEG? */ + if(photo[0]!=0xFF || photo[1]!=0xD8 || + photo[6]!='J' || photo[7]!='F' || photo[8]!='I' || photo[9]!='F') + { + log_error(_("\"%s\" is not a JPEG file\n"),filename); + m_free(photo); + photo=NULL; + continue; + } + + /* Build the packet */ + build_attribute_subpkt(uid,1,photo,len,header,16); + parse_attribute_subpkts(uid); + make_attribute_uidname(uid); + + show_photo(uid->attribs,pk); + switch(cpr_get_answer_yes_no_quit("photoid.jpeg.okay", + _("Is this photo correct (y/n/q)? "))) + { + case -1: + goto scram; + case 0: + free_attributes(uid); + m_free(photo); + photo=NULL; + continue; + } + } + + error=0; + + scram: + m_free(filename); + m_free(photo); + + if(error) + { + free_attributes(uid); + m_free(uid); + return NULL; + } + + return uid; +} + +void show_photo(const struct user_attribute *attr,PKT_public_key *pk) +{ + const char *ch; + char command[PHOTO_COMMAND_MAXLEN]={'\0'}; + int size=0; + u32 keyid[2]={0,0}; + struct exec_info *spawn; + + keyid_from_pk(pk,keyid); + + ch=opt.photo_viewer?opt.photo_viewer:DEFAULT_PHOTO_COMMAND; + + /* %-expandos */ + + /* make command grow */ + + while(*ch!='\0') + { + if(*ch=='%') + { + ch++; + + switch(*ch) + { + case 'k': /* short key id */ + if(size+8>PHOTO_COMMAND_MAXLEN-1) + goto fail; + + sprintf(&command[size],"%08lX",(ulong)keyid[1]); + size+=8; + break; + + case 'K': /* long key id */ + if(size+16>PHOTO_COMMAND_MAXLEN-1) + goto fail; + + sprintf(&command[size],"%08lX%08lX", + (ulong)keyid[0],(ulong)keyid[1]); + size+=16; + break; + + case 'f': /* fingerprint */ + { + byte array[MAX_FINGERPRINT_LEN]; + int len,i; + + fingerprint_from_pk(pk,array,&len); + + if(size+(len*2)>PHOTO_COMMAND_MAXLEN-1) + goto fail; + + for(i=0;iPHOTO_COMMAND_MAXLEN-1) + goto fail; + + strcat(command,"%"); + break; + + default: + if(size+2>PHOTO_COMMAND_MAXLEN-1) + goto fail; + + command[size++]='%'; + command[size++]=*ch; + break; + } + } + else + { + command[size++]=*ch; + if(size>PHOTO_COMMAND_MAXLEN-1) + goto fail; + } + + ch++; + } + + command[PHOTO_COMMAND_MAXLEN-1]='\0'; + + if(exec_write(&spawn,NULL,command,1,1)!=0) + goto fail; + + fwrite(attr->data,attr->len,1,spawn->tochild); + + if(exec_read(spawn)!=0) + goto fail; + + if(exec_finish(spawn)!=0) + goto fail; + + return; + + fail: + log_error("unable to display photo ID!\n"); +} diff --git a/g10/photoid.h b/g10/photoid.h new file mode 100644 index 000000000..0b037304f --- /dev/null +++ b/g10/photoid.h @@ -0,0 +1,11 @@ +/* Photo ID functions */ + +#ifndef _PHOTOID_H_ +#define _PHOTOID_H_ + +#include "packet.h" + +PKT_user_id *generate_photo_id(PKT_public_key *pk); +void show_photo(const struct user_attribute *attr,PKT_public_key *pk); + +#endif /* !_PHOTOID_H_ */ diff --git a/tools/ChangeLog b/tools/ChangeLog index 7198778b6..73cc5653b 100644 --- a/tools/ChangeLog +++ b/tools/ChangeLog @@ -1,3 +1,7 @@ +2001-12-21 David Shaw + + * gpgsplit.c (pkttype_to_string): PKT_PHOTO_ID -> PKT_ATTRIBUTE + 2001-10-23 Werner Koch * Makefile.am (gpgsplit_LDADD): Add ZLIBS. diff --git a/tools/gpgsplit.c b/tools/gpgsplit.c index 04b57a3d1..e7283a412 100644 --- a/tools/gpgsplit.c +++ b/tools/gpgsplit.c @@ -158,7 +158,7 @@ pkttype_to_string (int pkttype) case PKT_USER_ID : s = "user_id"; break; case PKT_PUBLIC_SUBKEY : s = "public_subkey"; break; case PKT_OLD_COMMENT : s = "old_comment"; break; - case PKT_PHOTO_ID : s = "photo_id"; break; + case PKT_ATTRIBUTE : s = "attribute"; break; case PKT_ENCRYPTED_MDC : s = "encrypted_mdc"; break; case PKT_MDC : s = "mdc"; break; case PKT_COMMENT : s = "comment"; break;