Imported Upstream version 2.3.1
[platform/upstream/gpg2.git] / tpm2d / command.c
1 /* command.c - TPM2daemon command handler
2  * Copyright (C) 2001, 2002, 2003, 2004, 2005,
3  *               2007, 2008, 2009, 2011  Free Software Foundation, Inc.
4  *
5  * This file is part of GnuPG.
6  *
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.
11  *
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.
16  *
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
20  */
21
22 #include <config.h>
23 #include <errno.h>
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <ctype.h>
28 #include <unistd.h>
29 #include <signal.h>
30 #ifdef USE_NPTH
31 # include <npth.h>
32 #endif
33
34 #include "tpm2daemon.h"
35 #include "tpm2.h"
36 #include <assuan.h>
37 #include <ksba.h>
38 #include "../common/asshelp.h"
39 #include "../common/server-help.h"
40
41 /* Maximum length allowed as a PIN; used for INQUIRE NEEDPIN */
42 #define MAXLEN_PIN 100
43
44 /* Maximum allowed size of key data as used in inquiries. */
45 #define MAXLEN_KEYDATA 4096
46
47 /* Maximum allowed total data size for SETDATA.  */
48 #define MAXLEN_SETDATA 4096
49
50 /* Maximum allowed size of certificate data as used in inquiries. */
51 #define MAXLEN_CERTDATA 16384
52
53
54 #define set_error(e,t) assuan_set_error (ctx, gpg_error (e), (t))
55
56 /* Data used to associate an Assuan context with local server data.
57    This object describes the local properties of one session.  */
58 struct server_local_s
59 {
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;
63
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
67      backpointer here. */
68   ctrl_t ctrl_backlink;
69
70   /* The Assuan context used by this session/server. */
71   assuan_context_t assuan_ctx;
72
73 #ifdef HAVE_W32_SYSTEM
74   unsigned long event_signal;   /* Or 0 if not used. */
75 #else
76   int event_signal;             /* Or 0 if not used. */
77 #endif
78
79   /* True if the card has been removed and a reset is required to
80      continue operation. */
81   int card_removed;
82
83   /* If set to true we will be terminate ourself at the end of the
84      this session.  */
85   int stopme;
86
87 };
88
89
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;
93
94 \f
95 static gpg_error_t
96 reset_notify (assuan_context_t ctx, char *line)
97 {
98   (void) ctx;
99   (void) line;
100
101   return 0;
102 }
103
104
105 static gpg_error_t
106 option_handler (assuan_context_t ctx, const char *key, const char *value)
107 {
108   ctrl_t ctrl = assuan_get_pointer (ctx);
109
110   if (!strcmp (key, "event-signal"))
111     {
112       /* A value of 0 is allowed to reset the event signal. */
113 #ifdef HAVE_W32_SYSTEM
114       if (!*value)
115         return gpg_error (GPG_ERR_ASS_PARAMETER);
116       ctrl->server_local->event_signal = strtoul (value, NULL, 16);
117 #else
118       int i = *value? atoi (value) : -1;
119       if (i < 0)
120         return gpg_error (GPG_ERR_ASS_PARAMETER);
121       ctrl->server_local->event_signal = i;
122 #endif
123     }
124
125  return 0;
126 }
127
128
129 static gpg_error_t
130 pin_cb (ctrl_t ctrl, const char *info, char **retstr)
131 {
132   assuan_context_t ctx = ctrl->ctx;
133   char *command;
134   int rc;
135   unsigned char *value;
136   size_t valuelen;
137
138   *retstr = NULL;
139   log_debug ("asking for PIN '%s'\n", info);
140
141   rc = gpgrt_asprintf (&command, "NEEDPIN %s", info);
142   if (rc < 0)
143     return gpg_error (gpg_err_code_from_errno (errno));
144
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);
148   xfree (command);
149   if (rc)
150     return rc;
151
152   if (!valuelen)
153     {
154       /* We require that the returned value is an UTF-8 string */
155       xfree (value);
156       return gpg_error (GPG_ERR_INV_RESPONSE);
157     }
158   *retstr = (char*)value;
159   return 0;
160 }
161
162 static const char hlp_import[] =
163   "IMPORT\n"
164   "\n"
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"
169   "\n"
170   "A PIN will be requested.";
171 static gpg_error_t
172 cmd_import (assuan_context_t ctx, char *line)
173 {
174   ctrl_t ctrl = assuan_get_pointer (ctx);
175   int rc;
176   unsigned char *keydata;
177   size_t keydatalen;
178   TSS_CONTEXT *tssc;
179   gcry_sexp_t s_key;
180   unsigned char *shadow_info = NULL;
181   size_t shadow_len;
182
183   line = skip_options (line);
184
185   if (*line)
186     return set_error (GPG_ERR_ASS_PARAMETER, "additional parameters given");
187
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);
192   if (rc)
193     return rc;
194
195   if ((rc = tpm2_start (&tssc)))
196     goto out;
197   gcry_sexp_new (&s_key, keydata, keydatalen, 0);
198   rc = tpm2_import_key (ctrl, tssc, pin_cb, &shadow_info, &shadow_len,
199                         s_key, opt.parent);
200   gcry_sexp_release (s_key);
201   tpm2_end (tssc);
202   if (rc)
203     goto out;
204
205   rc = assuan_send_data (ctx, shadow_info, shadow_len);
206
207  out:
208   xfree (shadow_info);
209   xfree (keydata);
210
211   return rc;
212 }
213
214 static const char hlp_pksign[] =
215   "PKSIGN\n"
216   "\n"
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"
220   "\n"
221   "A PIN will be requested.";
222 static gpg_error_t
223 cmd_pksign (assuan_context_t ctx, char *line)
224 {
225   ctrl_t ctrl = assuan_get_pointer (ctx);
226   int rc;
227   unsigned char *shadow_info;
228   size_t len;
229   TSS_CONTEXT *tssc;
230   TPM_HANDLE key;
231   TPMI_ALG_PUBLIC type;
232   unsigned char *digest;
233   size_t digestlen;
234   unsigned char *sig;
235   size_t siglen;
236
237   line = skip_options (line);
238
239   if (*line)
240     return set_error (GPG_ERR_ASS_PARAMETER, "additional parameters given");
241
242   /* Now get the actual keydata. */
243   rc = assuan_inquire (ctx, "KEYDATA", &shadow_info, &len, MAXLEN_KEYDATA);
244   if (rc)
245     return rc;
246
247   rc = assuan_inquire (ctx, "EXTRA", &digest, &digestlen, MAXLEN_KEYDATA);
248   if (rc)
249     goto out_freeshadow;
250
251   rc = tpm2_start (&tssc);
252   if (rc)
253     goto out;
254
255   rc = tpm2_load_key (tssc, shadow_info, &key, &type);
256   if (rc)
257     goto end_out;
258
259   rc = tpm2_sign (ctrl, tssc, key, pin_cb, type, digest, digestlen,
260                  &sig, &siglen);
261
262   tpm2_flush_handle (tssc, key);
263
264  end_out:
265   tpm2_end (tssc);
266
267   if (rc)
268     goto out;
269
270   rc = assuan_send_data (ctx, sig, siglen);
271   xfree (sig);
272
273  out:
274   xfree (digest);
275  out_freeshadow:
276   xfree (shadow_info);
277
278   return rc;
279 }
280
281 static const char hlp_pkdecrypt[] =
282   "PKDECRYPT\n"
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"
286   "\n"
287   "\n"
288   "A PIN will be requested.";
289 static gpg_error_t
290 cmd_pkdecrypt (assuan_context_t ctx, char *line)
291 {
292   ctrl_t ctrl = assuan_get_pointer (ctx);
293   int rc;
294   unsigned char *shadow_info;
295   size_t len;
296   TSS_CONTEXT *tssc;
297   TPM_HANDLE key;
298   TPMI_ALG_PUBLIC type;
299   unsigned char *crypto;
300   size_t cryptolen;
301   char *buf;
302   size_t buflen;
303
304   line = skip_options (line);
305
306   if (*line)
307     return set_error (GPG_ERR_ASS_PARAMETER, "additional parameters given");
308
309   /* Now get the actual keydata. */
310   rc = assuan_inquire (ctx, "KEYDATA", &shadow_info, &len, MAXLEN_KEYDATA);
311   if (rc)
312     return rc;
313
314   rc = assuan_inquire (ctx, "EXTRA", &crypto, &cryptolen, MAXLEN_KEYDATA);
315   if (rc)
316     goto out_freeshadow;
317
318   rc = tpm2_start (&tssc);
319   if (rc)
320     goto out;
321
322   rc = tpm2_load_key (tssc, shadow_info, &key, &type);
323   if (rc)
324     goto end_out;
325
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);
332
333   tpm2_flush_handle (tssc, key);
334
335  end_out:
336   tpm2_end (tssc);
337
338   if (rc)
339     goto out;
340
341   rc = assuan_send_data (ctx, buf, buflen);
342   xfree (buf);
343
344  out:
345   xfree (crypto);
346  out_freeshadow:
347   xfree (shadow_info);
348
349   return rc;
350 }
351
352 static const char hlp_killtpm2d[] =
353   "KILLTPM2D\n"
354   "\n"
355   "Commit suicide.";
356 static gpg_error_t
357 cmd_killtpm2d (assuan_context_t ctx, char *line)
358 {
359   ctrl_t ctrl = assuan_get_pointer (ctx);
360
361   (void)line;
362
363   ctrl->server_local->stopme = 1;
364   assuan_set_flag (ctx, ASSUAN_FORCE_CLOSE, 1);
365   return 0;
366 }
367
368
369 \f
370 /* Tell the assuan library about our commands */
371 static int
372 register_commands (assuan_context_t ctx)
373 {
374   static struct {
375     const char *name;
376     assuan_handler_t handler;
377     const char * const help;
378   } table[] = {
379     { "IMPORT",       cmd_import,     hlp_import },
380     { "PKSIGN",       cmd_pksign,     hlp_pksign },
381     { "PKDECRYPT",    cmd_pkdecrypt,  hlp_pkdecrypt },
382     { "KILLTPM2D",    cmd_killtpm2d,  hlp_killtpm2d },
383     { NULL }
384   };
385   int i, rc;
386
387   for (i=0; table[i].name; i++)
388     {
389       rc = assuan_register_command (ctx, table[i].name, table[i].handler,
390                                     table[i].help);
391       if (rc)
392         return rc;
393     }
394   assuan_set_hello_line (ctx, "GNU Privacy Guard's TPM2 server ready");
395
396   assuan_register_reset_notify (ctx, reset_notify);
397   assuan_register_option_handler (ctx, option_handler);
398   return 0;
399 }
400
401
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.  */
405 int
406 tpm2d_command_handler (ctrl_t ctrl, int fd)
407 {
408   int rc;
409   assuan_context_t ctx = NULL;
410   int stopme;
411
412   rc = assuan_new (&ctx);
413   if (rc)
414     {
415       log_error ("failed to allocate assuan context: %s\n",
416                  gpg_strerror (rc));
417       tpm2d_exit (2);
418     }
419
420   if (fd == -1)
421     {
422       assuan_fd_t filedes[2];
423
424       filedes[0] = assuan_fdopen (0);
425       filedes[1] = assuan_fdopen (1);
426       rc = assuan_init_pipe_server (ctx, filedes);
427     }
428   else
429     {
430       rc = assuan_init_socket_server (ctx, INT2FD (fd),
431                                       ASSUAN_SOCKET_SERVER_ACCEPTED);
432     }
433   if (rc)
434     {
435       log_error ("failed to initialize the server: %s\n",
436                  gpg_strerror (rc));
437       tpm2d_exit (2);
438     }
439   rc = register_commands (ctx);
440   if (rc)
441     {
442       log_error ("failed to register commands with Assuan: %s\n",
443                  gpg_strerror (rc));
444       tpm2d_exit (2);
445     }
446   assuan_set_pointer (ctx, ctrl);
447   ctrl->ctx = ctx;
448
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;
456
457   /* Command processing loop. */
458   for (;;)
459     {
460       rc = assuan_accept (ctx);
461       if (rc == -1)
462         {
463           break;
464         }
465       else if (rc)
466         {
467           log_info ("Assuan accept problem: %s\n", gpg_strerror (rc));
468           break;
469         }
470
471       rc = assuan_process (ctx);
472       if (rc)
473         {
474           log_info ("Assuan processing failed: %s\n", gpg_strerror (rc));
475           continue;
476         }
477     }
478
479   /* Release the server object.  */
480   if (session_list == ctrl->server_local)
481     session_list = ctrl->server_local->next_session;
482   else
483     {
484       struct server_local_s *sl;
485
486       for (sl=session_list; sl->next_session; sl = sl->next_session)
487         if (sl->next_session == ctrl->server_local)
488           break;
489       if (!sl->next_session)
490           BUG ();
491       sl->next_session = ctrl->server_local->next_session;
492     }
493   stopme = ctrl->server_local->stopme;
494   xfree (ctrl->server_local);
495   ctrl->server_local = NULL;
496
497   /* Release the Assuan context.  */
498   assuan_release (ctx);
499
500   if (stopme)
501     tpm2d_exit (0);
502
503   /* If there are no more sessions return true.  */
504   return !session_list;
505 }