1 /*      pinentry.c
2  *
3  *      Copyright 2011 Hans Alves <alves.h88@gmail.com>
4  *
5  *      This program is free software; you can redistribute it and/or modify
6  *      it under the terms of the GNU General Public License as published by
7  *      the Free Software Foundation; either version 2 of the License, or
8  *      (at your option) any later version.
9  *
10  *      This program is distributed in the hope that it will be useful,
11  *      but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  *      GNU General Public License for more details.
14  *
15  *      You should have received a copy of the GNU General Public License
16  *      along with this program; if not, write to the Free Software
17  *      Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
18  *      MA 02110-1301, USA.
19  */
20 
21 /* for fdopen() */
22 #define _POSIX_SOURCE 1
23 #define _POSIX_C_SOURCE 1
24 
25 #include "geanypg.h"
26 
geanypg_getname(const char * uid_hint)27 static const char * geanypg_getname(const char * uid_hint)
28 {
29     int space = 0;
30     if (!uid_hint)
31         return NULL;
32     while (*uid_hint && !(space && *uid_hint != ' '))
33     {
34         if (*uid_hint == ' ')
35             space = 1;
36         ++uid_hint;
37     }
38     return uid_hint;
39 }
40 
41 #ifdef __unix__
42 
43 #include <unistd.h>
44 #include <sys/types.h>
45 #include <sys/wait.h>
46 
geanypg_read_till(int fd,char delim)47 static void geanypg_read_till(int fd, char delim)
48 {
49     while (1)
50     {
51         char val;
52         ssize_t rv = read(fd, &val, 1);
53         if (rv <= 0 || val == delim)
54             break;
55     }
56 }
57 
geanypg_read(int fd,char delim,int max,char * buffer)58 static int geanypg_read(int fd, char delim, int max, char * buffer)
59 {
60     int idx;
61     ssize_t rv = 1;
62     char ch = 0;
63     for (idx = 0; (idx < max - 1) && rv > 0 && ch != delim; ++idx)
64     {
65         rv = read(fd, &ch, 1);
66         buffer[idx] = ch;
67     }
68     buffer[idx ? idx - 1 : 0] = 0;
69     return idx ? idx - 1 : 0;
70 }
geanypg_passphrase_cb(void * hook,const char * uid_hint,const char * passphrase_info,int prev_was_bad,int fd)71 gpgme_error_t geanypg_passphrase_cb(void * hook,
72                                     const char * uid_hint,
73                                     const char * passphrase_info,
74                                     int prev_was_bad ,
75                                     int fd)
76 {
77     int outpipe[2];
78     int inpipe[2];
79     int childpid;
80     int status;
81     char readbuffer[2080] = {0}; /* pinentry should at least support passphrases of up to 2048 characters */
82     FILE * childin;
83 
84     if (pipe(outpipe))
85     {
86         g_warning("%s", strerror(errno));
87         return gpgme_error_from_errno(errno);
88     }
89     if (pipe(inpipe))
90     {
91         g_warning("%s", strerror(errno));
92         return gpgme_error_from_errno(errno);
93     }
94 
95     childpid = fork();
96     if (!childpid)
97     { /* pinentry */
98         char arg1[] = "pinentry";
99         char * argv[] = {NULL, NULL};
100 
101         argv[0] = arg1;
102 
103         close(outpipe[READ]);
104         dup2(outpipe[WRITE], STDOUT_FILENO);
105 
106         close(inpipe[WRITE]);
107         dup2(inpipe[READ], STDIN_FILENO);
108 
109         execvp(*argv, argv);
110         /* shouldn't get here */
111         g_warning("%s: %s", _("Could not use pinentry."), strerror(errno));
112         exit(1); /* kill the child */
113     }
114     /* GeanpyPG */
115     close(outpipe[WRITE]);
116     close(inpipe[READ]);
117     childin = fdopen(inpipe[WRITE], "w");
118 
119     /* To understand what's happening here, read the pinentry documentation */
120     geanypg_read(outpipe[READ], ' ', 2049, readbuffer);
121     if (strncmp(readbuffer, "OK", 3))
122     {
123         g_warning(_("Unexpected output from pinentry."));
124         fclose(childin);
125         waitpid(childpid, &status, 0);
126         close(outpipe[READ]);
127         close(fd);
128         return gpgme_err_make(GPG_ERR_SOURCE_PINENTRY, GPG_ERR_GENERAL);
129     }
130     geanypg_read_till(outpipe[READ], '\n'); /* read the rest of the first line after OK */
131     fprintf(childin, "SETTITLE GeanyPG %s\n", _("Passphrase entry"));
132     fflush(childin);
133     geanypg_read_till(outpipe[READ], '\n');
134 
135     fprintf(childin, "SETPROMPT %s:\n", (uid_hint && *uid_hint ? "" : _("Passphrase")));
136     fflush(childin);
137     geanypg_read_till(outpipe[READ], '\n');
138 
139     fprintf(childin, "SETDESC %s: %s\n",
140                      (uid_hint && *uid_hint ? _("Enter passphrase for") : ""),
141                      (uid_hint && *uid_hint ? geanypg_getname(uid_hint) : ""));
142     fflush(childin);
143     geanypg_read_till(outpipe[READ], '\n');
144 
145     fprintf(childin, "GETPIN\n");
146     fflush(childin);
147 
148     geanypg_read(outpipe[READ], ' ', 2049, readbuffer);
149     if (!strncmp(readbuffer, "D", 2))
150     {
151         while (1)
152         {
153             char val;
154             register ssize_t rv = read(outpipe[READ], &val, 1);
155             if (rv <= 0 || val == '\n')
156             {
157                 while (!write(fd, "\n", 1));
158                 break;
159             }
160             while (!write(fd, &val, 1));
161         }
162     }
163     else
164     {
165         unsigned long errval;
166         if (!strncmp(readbuffer, "ERR", 4))
167         {
168             geanypg_read(outpipe[READ], ' ', 2049, readbuffer);
169             sscanf(readbuffer, "%lu", &errval);
170             geanypg_read(outpipe[READ], '\n', 2049, readbuffer);
171             g_warning("%s %lu %s", _("pinentry gave error"), errval, readbuffer);
172         }
173         else
174             g_warning(_("Unexpected error from pinentry."));
175         fclose(childin);
176         waitpid(childpid, &status, 0);
177         close(outpipe[READ]);
178         close(fd);
179         return gpgme_err_make(GPG_ERR_SOURCE_PINENTRY,
180             (!strncmp(readbuffer, "canceled", 9) ? GPG_ERR_CANCELED : GPG_ERR_GENERAL));
181     }
182 
183 
184     fclose(childin);
185     waitpid(childpid, &status, 0);
186     close(outpipe[READ]);
187     close(fd);
188     return GPG_ERR_NO_ERROR;
189 }
190 
191 #else
192 
geanypg_passphrase_cb(void * hook,const char * uid_hint,const char * passphrase_info,int prev_was_bad,int fd)193 gpgme_error_t geanypg_passphrase_cb(void *hook,
194                                     const char *uid_hint,
195                                     const char *passphrase_info,
196                                     int prev_was_bad ,
197                                     int fd)
198 {
199     dialogs_show_msgbox(GTK_MESSAGE_ERROR, _("Error, Passphrase input without using gpg-agent is not supported on Windows yet."));
200     return gpgme_err_make(GPG_ERR_SOURCE_PINENTRY, GPG_ERR_CANCELED);
201 }
202 #endif
203 
204