/* hgen+ */ /* * $Id: askkey-pinentry.c,v 1.3 2008/08/20 02:59:27 az Exp $ * * File: askkey-pinentry.c * Date: Wed Apr 11 14:54:15 2007 * Author: Alexander Zangerl (az) * * Abstract: * interface to pinentry programs for key requesting * */ /* hgen- */ /* * Modifications: * $Log: askkey-pinentry.c,v $ * Revision 1.3 2008/08/20 02:59:27 az * added dehexing * * Revision 1.2 2007/04/11 07:08:25 az * added passing of display variables to the pinentry * * Revision 1.1 2007/04/11 06:37:00 az * Initial revision * */ #include #include #include #include #include #include #include #include #include #define BAILOUT(x,...) (fprintf(stderr,x,## __VA_ARGS__),exit(1)) #define BAILSTD(x) (perror(x),exit(1)) #define USAGE "Usage: %s \ndisplayvars is a SINGLE arg, form key=value|key=value...\n",argv[0] #define PE "/usr/bin/pinentry","pinentry" #define LB 4096 #define FALLBACKCTYPE "iso-8859-1" /* %-unescape instring, inplace (safe as it gets only shorter) this does not touch a solo % or otherwise badly hexed stuff. */ char *dehex(char instring[]) { char *s,*d,cbuf[]="0xaa"; int n; for (s=d=instring;*s;) { if (*s=='%' && *(s+1) && *(s+2) && isxdigit(*(s+1)) && isxdigit(*(s+2))) { cbuf[2]=*(s+1); cbuf[3]=*(s+2); n=(int)strtol(cbuf,NULL,16); *d=n; /* 2 hexbytes, n can be only one byte */ s=s+3; /* past the % and the two chars */ ++d; /* only one step */ } else { /* no change, but copy from source to dest only if needed */ if (s!=d) *d=*s; ++d; ++s; } } /* terminate the output cleanly */ *d=0; return instring; } int main(int argc,char *argv[]) { int uid,gid,pid,res; char *info,*display,*pin,*p,*q,linebuffer[LB]; int dn[2],up[2]; FILE *cmd,*rsp; if (argc!=5) BAILOUT(USAGE); uid=atoi(argv[1]); gid=atoi(argv[2]); if (uid<=0 || gid<=0) BAILOUT(USAGE); info=argv[3]; display=argv[4]; /* setup pipes */ if (pipe(up) || pipe(dn)) BAILSTD("pipe failed"); if ((pid=fork())<0) BAILSTD("fork failed"); /* child runs pinentry as the given user and with given display envvars; i/o into pipes */ if (!pid) { if (setgid(gid)) BAILSTD("setgid failed"); if (setuid(uid)) BAILSTD("setuid failed"); for(p=display;p && *p;p=q) { if ((q=strchr(p,'|'))) *(q++)=0; if (putenv(p)) BAILSTD("putenv failed"); } if (dup2(dn[0],0)<0) BAILSTD("dup2 failed"); if (dup2(up[1],1)<0) BAILSTD("dup2 failed"); close(dn[1]); close(up[0]); execl(PE,NULL); BAILSTD("exec failed"); } /* parent needs to talk to pinentry */ close(dn[0]); close(up[1]); cmd=fdopen(dn[1],"w"); rsp=fdopen(up[0],"r"); if (!cmd || !rsp) BAILSTD("fdopen failed"); if (!fgets(linebuffer,sizeof(linebuffer),rsp) || strncmp(linebuffer,"OK",2)) BAILOUT("no greeting from pinentry\n"); fprintf(cmd,"SETDESC Enter secret for %s\n",info); fflush(cmd); if (!fgets(linebuffer,sizeof(linebuffer),rsp) || strncmp(linebuffer,"OK",2)) BAILOUT("setting description failed\n"); p=getenv("LC_CTYPE"); if (!p) p=FALLBACKCTYPE; fprintf(cmd,"OPTION lc-ctype=%s\n",p); fflush(cmd); if (!fgets(linebuffer,sizeof(linebuffer),rsp) || strncmp(linebuffer,"OK",2)) BAILOUT("setting ctype option failed\n"); fprintf(cmd,"GETPIN\n"); fflush(cmd); if (!fgets(linebuffer,sizeof(linebuffer),rsp) || strncmp(linebuffer,"D ",2)) BAILOUT("getting pin failed\n"); pin=linebuffer+2; fclose(cmd); fclose(rsp); wait(&res); p=strchr(pin,'\n'); if (p) *p=0; /* this is necessary because the pinentry tools are expecting to work within the gnupg/assuan framework, where passphrases are %- or hex-escaped on output... */ dehex(pin); printf("%s",pin); return WEXITSTATUS(res); }