2 Copyright (C) 2006-2023 Ben Kibbey <bjk@luxsci.net>
4 This file is part of libpwmd.
6 This library is free software; you can redistribute it and/or
7 modify it under the terms of the GNU Lesser General Public
8 License version 2.1 as published by the Free Software Foundation.
10 This library 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 GNU
13 Lesser General Public License for more details.
15 You should have received a copy of the GNU Lesser General Public
16 License along with this library; if not, write to the Free Software
17 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
24 #include <sys/types.h>
40 static gpg_error_t
set_pinentry_strings (pwm_t
* pwm
, pwmd_pinentry_t which
);
43 launch_pinentry (pwm_t
* pwm
)
47 assuan_fd_t child_list
[] = { ASSUAN_INVALID_FD
};
49 const char **p
= argv
;
50 static struct assuan_malloc_hooks mhooks
= {
51 pwmd_malloc
, pwmd_realloc
, pwmd_free
55 if (pwm
->pinentry_display
)
58 *p
++ = pwm
->pinentry_display
;
61 if (pwm
->pinentry_tty
)
64 *p
++ = pwm
->pinentry_tty
;
67 if (pwm
->pinentry_term
)
70 *p
++ = pwm
->pinentry_term
;
73 if (pwm
->pinentry_lcctype
)
76 *p
++ = pwm
->pinentry_lcctype
;
79 if (pwm
->pinentry_lcmessages
)
81 *p
++ = "--lc-messages";
82 *p
++ = pwm
->pinentry_lcmessages
;
86 rc
= assuan_new_ext (&ctx
, GPG_ERR_SOURCE_PINENTRY
, &mhooks
, NULL
, NULL
);
90 rc
= assuan_pipe_connect (ctx
, PINENTRY_PATH
, argv
, child_list
, NULL
, NULL
, 0);
97 pwm
->pinentry_pid
= assuan_get_pid (ctx
);
99 rc
= set_pinentry_strings (pwm
, 0);
101 _pinentry_disconnect (pwm
);
107 pinentry_command (pwm_t
* pwm
, char **result
, size_t * len
, const char *cmd
)
117 gpg_error_t rc
= launch_pinentry (pwm
);
123 return _assuan_command (pwm
, pwm
->pctx
, result
, len
, cmd
);
128 quality_cb (void *data
, const char *line
)
130 #define MAX_ENTROPY 80 // in bits
135 if (strncmp (line
, "QUALITY ", 8))
136 return GPG_ERR_INV_ARG
;
138 match
= ZxcvbnMatch (line
+8, NULL
, NULL
);
139 match
= (match
/MAX_ENTROPY
)*100;
140 snprintf (buf
, sizeof (buf
), "%u", (unsigned)match
);
141 return assuan_send_data (pwm
->pctx
, buf
, strlen (buf
));
146 set_pinentry_strings (pwm_t
* pwm
, pwmd_pinentry_t which
)
148 char *tmp
, *desc
= NULL
;
151 if (which
!= PWMD_PINENTRY_USER
)
153 rc
= pinentry_command (pwm
, NULL
, NULL
, "SETERROR ");
158 tmp
= pwmd_malloc (ASSUAN_LINELENGTH
+ 1);
160 return gpg_error_from_errno (ENOMEM
);
162 if (!pwm
->pinentry_prompt
)
164 pwm
->pinentry_prompt
= pwmd_strdup (N_("Passphrase:"));
165 if (!pwm
->pinentry_prompt
)
168 return GPG_ERR_ENOMEM
;
172 if (!pwm
->pinentry_desc
)
174 if (which
== PWMD_PINENTRY_OPEN
|| which
== PWMD_PINENTRY_OPEN_FAILED
)
176 if (pwm
->passphrase_hint
&& pwm
->passphrase_info
)
178 char *hint
= pwmd_strdup (pwm
->passphrase_hint
), *hintp
= hint
;
179 char *info
= pwmd_strdup (pwm
->passphrase_info
), *infop
;
180 char *userid
= strchr (hint
, ' ');
182 if (userid
&& *userid
)
185 infop
= strchr (info
, ' ');
188 char *p
= strchr (++infop
, ' ');
191 infop
[strlen(infop
)-strlen(p
)] = 0;
196 desc
= pwmd_strdup_printf (N_
197 ("Please enter the passphrase to unlock the OpenPGP secret key:%%0A%%0AClient: %s%%0AData file: %s%%0AKey ID: %s (0x%s)%%0AMain key ID: 0x%s"), pwm
->name
? pwm
->name
: N_("unknown"), pwm
->filename
, userid
, hintp
, infop
);
203 desc
= pwmd_strdup_printf (N_ ("A passphrase is required to decrypt the encrypted%%0Adata file \"%s\". Please enter the passphrase below."), pwm
->filename
);
206 else if (which
== PWMD_PINENTRY_SAVE
207 || which
== PWMD_PINENTRY_SAVE_FAILED
)
209 if (pwm
->bulk_id
&& !strcmp (pwm
->bulk_id
, "GENKEY"))
210 desc
= pwmd_strdup_printf (N_("Please enter the passphrase to use to protect the newly generated keypair."));
212 desc
= pwmd_strdup_printf (N_("Please enter the passphrase to use to encrypt%%0Athe data file \"%s\"."), pwm
->filename
);
214 else if (which
== PWMD_PINENTRY_SAVE_CONFIRM
)
216 pwmd_strdup_printf (N_
217 ("Please re-enter the passphrase for confirmation."),
223 return GPG_ERR_ENOMEM
;
227 desc
= pwm
->pinentry_desc
;
229 if (which
== PWMD_PINENTRY_USER
)
231 snprintf (tmp
, ASSUAN_LINELENGTH
, "SETERROR %s",
232 pwm
->pinentry_error
? pwm
->pinentry_error
: "");
233 rc
= pinentry_command (pwm
, NULL
, NULL
, tmp
);
240 snprintf (tmp
, ASSUAN_LINELENGTH
, "SETDESC %s",
241 pwm
->pinentry_desc
? pwm
->pinentry_desc
: "");
245 if (which
== PWMD_PINENTRY_SAVE
)
247 snprintf (tmp
, ASSUAN_LINELENGTH
, "SETREPEAT %s", N_("Repeat:"));
248 rc
= pinentry_command (pwm
, NULL
, NULL
, tmp
);
251 snprintf (tmp
, ASSUAN_LINELENGTH
, "SETREPEATERROR %s", N_("Passphrases do not match."));
252 rc
= pinentry_command (pwm
, NULL
, NULL
, tmp
);
255 snprintf (tmp
, ASSUAN_LINELENGTH
, "SETREPEATOK %s", N_("Passphrases match."));
256 rc
= pinentry_command (pwm
, NULL
, NULL
, tmp
);
257 if (gpg_err_code (rc
) == GPG_ERR_ASS_UNKNOWN_CMD
)
263 if (!rc
&& (which
== PWMD_PINENTRY_SAVE_FAILED
264 || which
== PWMD_PINENTRY_OPEN_FAILED
))
266 if (which
== PWMD_PINENTRY_SAVE_FAILED
)
267 snprintf (tmp
, ASSUAN_LINELENGTH
, "SETERROR %s",
268 N_("Passphrases do not match, try again."));
271 if (pwm
->pinentry_tries
)
273 strcpy (tmp
, "SETERROR ");
274 snprintf (tmp
+ strlen ("SETERROR "), ASSUAN_LINELENGTH
,
275 N_("Bad passphrase (try %i of %i)"),
276 pwm
->pinentry_try
+ 1, pwm
->pinentry_tries
);
279 snprintf (tmp
, ASSUAN_LINELENGTH
, "SETERROR %s",
280 N_("Bad passphrase, try again"));
283 rc
= pinentry_command (pwm
, NULL
, NULL
, tmp
);
291 snprintf (tmp
, ASSUAN_LINELENGTH
, "SETDESC %s", desc
);
293 if (pwm
->pinentry_desc
!= desc
)
298 rc
= pinentry_command (pwm
, NULL
, NULL
, tmp
);
305 snprintf (tmp
, ASSUAN_LINELENGTH
, "SETPROMPT %s", pwm
->pinentry_prompt
);
306 rc
= pinentry_command (pwm
, NULL
, NULL
, tmp
);
309 if (!rc
&& (which
== PWMD_PINENTRY_SAVE
||
310 which
== PWMD_PINENTRY_SAVE_FAILED
))
312 rc
= pinentry_command (pwm
, NULL
, NULL
, "SETQUALITYBAR");
315 pwm
->_inquire_func
= quality_cb
;
316 pwm
->_inquire_data
= pwm
;
321 if (!rc
&& pwm
->pinentry_timeout
>= 0)
325 snprintf(buf
, sizeof(buf
), "SETTIMEOUT %i", pwm
->pinentry_timeout
);
326 rc
= pinentry_command(pwm
, NULL
, NULL
, buf
);
333 _pinentry_disconnect (pwm_t
* pwm
)
336 assuan_release (pwm
->pctx
);
339 pwm
->pinentry_pid
= -1;
343 _getpin (pwm_t
* pwm
, char **result
, size_t * len
, pwmd_pinentry_t which
)
345 gpg_error_t rc
= set_pinentry_strings (pwm
, which
);
349 _pinentry_disconnect (pwm
);
353 rc
= pinentry_command (pwm
, result
, len
,
354 which
== PWMD_PINENTRY_CONFIRM
? "CONFIRM" : "GETPIN");
357 _pinentry_disconnect (pwm
);
359 /* This lets PWMD_OPTION_PINENTRY_TIMEOUT work. Note that it is not
360 * thread safe due to the global variables. */
361 if (gpg_err_code (rc
) == GPG_ERR_EOF
)
362 rc
= GPG_ERR_CANCELED
;
364 else if (which
!= PWMD_PINENTRY_CONFIRM
&& len
&& result
)
367 *len
= strlen (*result
); // remove the null byte
370 *result
= pwmd_malloc (1);
380 _pwmd_getpin (pwm_t
* pwm
, const char *filename
, char **result
,
381 size_t * len
, pwmd_pinentry_t which
)
387 return GPG_ERR_INV_ARG
;
390 if (which
== PWMD_PINENTRY_CLOSE
)
392 _pinentry_disconnect (pwm
);
396 if (!result
&& which
!= PWMD_PINENTRY_CONFIRM
)
397 return GPG_ERR_INV_ARG
;
399 pwm
->filename
= (char *) filename
;
400 rc
= _getpin (pwm
, result
, len
, which
);