1 /* command.c - TPM2daemon command handler
2 * Copyright (C) 2001, 2002, 2003, 2004, 2005,
3 * 2007, 2008, 2009, 2011 Free Software Foundation, Inc.
5 * This file is part of GnuPG.
7 * GnuPG is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 3 of the License, or
10 * (at your option) any later version.
12 * GnuPG is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, see <https://www.gnu.org/licenses/>.
19 * SPDX-License-Identifier: GPL-3.0-or-later
34 #include "tpm2daemon.h"
38 #include "../common/asshelp.h"
39 #include "../common/server-help.h"
41 /* Maximum length allowed as a PIN; used for INQUIRE NEEDPIN */
42 #define MAXLEN_PIN 100
44 /* Maximum allowed size of key data as used in inquiries. */
45 #define MAXLEN_KEYDATA 4096
47 /* Maximum allowed total data size for SETDATA. */
48 #define MAXLEN_SETDATA 4096
50 /* Maximum allowed size of certificate data as used in inquiries. */
51 #define MAXLEN_CERTDATA 16384
54 #define set_error(e,t) assuan_set_error (ctx, gpg_error (e), (t))
56 /* Data used to associate an Assuan context with local server data.
57 This object describes the local properties of one session. */
60 /* We keep a list of all active sessions with the anchor at
61 SESSION_LIST (see below). This field is used for linking. */
62 struct server_local_s *next_session;
64 /* This object is usually assigned to a CTRL object (which is
65 globally visible). While enumerating all sessions we sometimes
66 need to access data of the CTRL object; thus we keep a
70 /* The Assuan context used by this session/server. */
71 assuan_context_t assuan_ctx;
73 #ifdef HAVE_W32_SYSTEM
74 unsigned long event_signal; /* Or 0 if not used. */
76 int event_signal; /* Or 0 if not used. */
79 /* True if the card has been removed and a reset is required to
80 continue operation. */
83 /* If set to true we will be terminate ourself at the end of the
90 /* To keep track of all running sessions, we link all active server
91 contexts and the anchor in this variable. */
92 static struct server_local_s *session_list;
96 reset_notify (assuan_context_t ctx, char *line)
106 option_handler (assuan_context_t ctx, const char *key, const char *value)
108 ctrl_t ctrl = assuan_get_pointer (ctx);
110 if (!strcmp (key, "event-signal"))
112 /* A value of 0 is allowed to reset the event signal. */
113 #ifdef HAVE_W32_SYSTEM
115 return gpg_error (GPG_ERR_ASS_PARAMETER);
116 ctrl->server_local->event_signal = strtoul (value, NULL, 16);
118 int i = *value? atoi (value) : -1;
120 return gpg_error (GPG_ERR_ASS_PARAMETER);
121 ctrl->server_local->event_signal = i;
130 pin_cb (ctrl_t ctrl, const char *info, char **retstr)
132 assuan_context_t ctx = ctrl->ctx;
135 unsigned char *value;
139 log_debug ("asking for PIN '%s'\n", info);
141 rc = gpgrt_asprintf (&command, "NEEDPIN %s", info);
143 return gpg_error (gpg_err_code_from_errno (errno));
145 /* Fixme: Write an inquire function which returns the result in
146 secure memory and check all further handling of the PIN. */
147 rc = assuan_inquire (ctx, command, &value, &valuelen, MAXLEN_PIN);
154 /* We require that the returned value is an UTF-8 string */
156 return gpg_error (GPG_ERR_INV_RESPONSE);
158 *retstr = (char*)value;
162 static const char hlp_import[] =
165 "This command is used to convert a public and secret key to tpm format.\n"
166 "keydata is communicated via an inquire KEYDATA command\n"
167 "The keydata is expected to be the usual canonical encoded\n"
168 "S-expression. The return will be a TPM format S-expression\n"
170 "A PIN will be requested.";
172 cmd_import (assuan_context_t ctx, char *line)
174 ctrl_t ctrl = assuan_get_pointer (ctx);
176 unsigned char *keydata;
180 unsigned char *shadow_info = NULL;
183 line = skip_options (line);
186 return set_error (GPG_ERR_ASS_PARAMETER, "additional parameters given");
188 /* Now get the actual keydata. */
189 assuan_begin_confidential (ctx);
190 rc = assuan_inquire (ctx, "KEYDATA", &keydata, &keydatalen, MAXLEN_KEYDATA);
191 assuan_end_confidential (ctx);
195 if ((rc = tpm2_start (&tssc)))
197 gcry_sexp_new (&s_key, keydata, keydatalen, 0);
198 rc = tpm2_import_key (ctrl, tssc, pin_cb, &shadow_info, &shadow_len,
200 gcry_sexp_release (s_key);
205 rc = assuan_send_data (ctx, shadow_info, shadow_len);
214 static const char hlp_pksign[] =
217 "Get the TPM to produce a signature. KEYDATA will request the TPM\n"
218 "form S-expression (returned by IMPORT) and EXTRA will be the hash\n"
219 "to sign. The TPM currently deduces hash type from length.\n"
221 "A PIN will be requested.";
223 cmd_pksign (assuan_context_t ctx, char *line)
225 ctrl_t ctrl = assuan_get_pointer (ctx);
227 unsigned char *shadow_info;
231 TPMI_ALG_PUBLIC type;
232 unsigned char *digest;
237 line = skip_options (line);
240 return set_error (GPG_ERR_ASS_PARAMETER, "additional parameters given");
242 /* Now get the actual keydata. */
243 rc = assuan_inquire (ctx, "KEYDATA", &shadow_info, &len, MAXLEN_KEYDATA);
247 rc = assuan_inquire (ctx, "EXTRA", &digest, &digestlen, MAXLEN_KEYDATA);
251 rc = tpm2_start (&tssc);
255 rc = tpm2_load_key (tssc, shadow_info, &key, &type);
259 rc = tpm2_sign (ctrl, tssc, key, pin_cb, type, digest, digestlen,
262 tpm2_flush_handle (tssc, key);
270 rc = assuan_send_data (ctx, sig, siglen);
281 static const char hlp_pkdecrypt[] =
283 "Get the TPM to recover a symmetric key. KEYDATA will request the TPM\n"
284 "form S-expression (returned by IMPORT) and EXTRA will be the input\n"
285 "to derive or decrypt. The return will be the symmetric key\n"
288 "A PIN will be requested.";
290 cmd_pkdecrypt (assuan_context_t ctx, char *line)
292 ctrl_t ctrl = assuan_get_pointer (ctx);
294 unsigned char *shadow_info;
298 TPMI_ALG_PUBLIC type;
299 unsigned char *crypto;
304 line = skip_options (line);
307 return set_error (GPG_ERR_ASS_PARAMETER, "additional parameters given");
309 /* Now get the actual keydata. */
310 rc = assuan_inquire (ctx, "KEYDATA", &shadow_info, &len, MAXLEN_KEYDATA);
314 rc = assuan_inquire (ctx, "EXTRA", &crypto, &cryptolen, MAXLEN_KEYDATA);
318 rc = tpm2_start (&tssc);
322 rc = tpm2_load_key (tssc, shadow_info, &key, &type);
326 if (type == TPM_ALG_RSA)
327 rc = tpm2_rsa_decrypt (ctrl, tssc, key, pin_cb, crypto,
328 cryptolen, &buf, &buflen);
329 else if (type == TPM_ALG_ECC)
330 rc = tpm2_ecc_decrypt (ctrl, tssc, key, pin_cb, crypto,
331 cryptolen, &buf, &buflen);
333 tpm2_flush_handle (tssc, key);
341 rc = assuan_send_data (ctx, buf, buflen);
352 static const char hlp_killtpm2d[] =
357 cmd_killtpm2d (assuan_context_t ctx, char *line)
359 ctrl_t ctrl = assuan_get_pointer (ctx);
363 ctrl->server_local->stopme = 1;
364 assuan_set_flag (ctx, ASSUAN_FORCE_CLOSE, 1);
370 /* Tell the assuan library about our commands */
372 register_commands (assuan_context_t ctx)
376 assuan_handler_t handler;
377 const char * const help;
379 { "IMPORT", cmd_import, hlp_import },
380 { "PKSIGN", cmd_pksign, hlp_pksign },
381 { "PKDECRYPT", cmd_pkdecrypt, hlp_pkdecrypt },
382 { "KILLTPM2D", cmd_killtpm2d, hlp_killtpm2d },
387 for (i=0; table[i].name; i++)
389 rc = assuan_register_command (ctx, table[i].name, table[i].handler,
394 assuan_set_hello_line (ctx, "GNU Privacy Guard's TPM2 server ready");
396 assuan_register_reset_notify (ctx, reset_notify);
397 assuan_register_option_handler (ctx, option_handler);
402 /* Startup the server. If FD is given as -1 this is simple pipe
403 server, otherwise it is a regular server. Returns true if there
404 are no more active asessions. */
406 tpm2d_command_handler (ctrl_t ctrl, int fd)
409 assuan_context_t ctx = NULL;
412 rc = assuan_new (&ctx);
415 log_error ("failed to allocate assuan context: %s\n",
422 assuan_fd_t filedes[2];
424 filedes[0] = assuan_fdopen (0);
425 filedes[1] = assuan_fdopen (1);
426 rc = assuan_init_pipe_server (ctx, filedes);
430 rc = assuan_init_socket_server (ctx, INT2FD (fd),
431 ASSUAN_SOCKET_SERVER_ACCEPTED);
435 log_error ("failed to initialize the server: %s\n",
439 rc = register_commands (ctx);
442 log_error ("failed to register commands with Assuan: %s\n",
446 assuan_set_pointer (ctx, ctrl);
449 /* Allocate and initialize the server object. Put it into the list
450 of active sessions. */
451 ctrl->server_local = xcalloc (1, sizeof *ctrl->server_local);
452 ctrl->server_local->next_session = session_list;
453 session_list = ctrl->server_local;
454 ctrl->server_local->ctrl_backlink = ctrl;
455 ctrl->server_local->assuan_ctx = ctx;
457 /* Command processing loop. */
460 rc = assuan_accept (ctx);
467 log_info ("Assuan accept problem: %s\n", gpg_strerror (rc));
471 rc = assuan_process (ctx);
474 log_info ("Assuan processing failed: %s\n", gpg_strerror (rc));
479 /* Release the server object. */
480 if (session_list == ctrl->server_local)
481 session_list = ctrl->server_local->next_session;
484 struct server_local_s *sl;
486 for (sl=session_list; sl->next_session; sl = sl->next_session)
487 if (sl->next_session == ctrl->server_local)
489 if (!sl->next_session)
491 sl->next_session = ctrl->server_local->next_session;
493 stopme = ctrl->server_local->stopme;
494 xfree (ctrl->server_local);
495 ctrl->server_local = NULL;
497 /* Release the Assuan context. */
498 assuan_release (ctx);
503 /* If there are no more sessions return true. */
504 return !session_list;