qemu: tpm: do not update profile name for transient domains
[libvirt.git] / tools / virt-pki-validate.c
blobe693ffaed6c30959aa5351af097d3fc0feae370d
1 /*
2 * SPDX-License-Identifier: GPL-2.0-or-later
3 */
5 #include <config.h>
6 #include "internal.h"
8 #include <getopt.h>
9 #include <stdio.h>
10 #include <stdlib.h>
12 #include <gnutls/gnutls.h>
13 #include <gnutls/x509.h>
15 #include "virgettext.h"
16 #include "virfile.h"
17 #include "virnettlsconfig.h"
18 #include "virnettlscert.h"
19 #include "virutil.h"
20 #include "virt-validate-common.h"
22 static bool
23 virPKIValidateFile(const char *file,
24 uid_t owner,
25 gid_t group,
26 mode_t mode)
28 struct stat sb;
29 if (stat(file, &sb) < 0)
30 return false;
32 if (sb.st_uid != owner ||
33 sb.st_gid != group)
34 return false;
36 return (sb.st_mode & 0777) == mode;
39 #define FILE_REQUIRE_EXISTS(scope, path, message, hint, ...) \
40 do { \
41 virValidateCheck(scope, "%s", message); \
42 if (!virFileExists(path)) { \
43 virValidateFail(VIR_VALIDATE_FAIL, hint, __VA_ARGS__); \
44 ok = false; \
45 goto done; \
46 } else { \
47 virValidatePass(); \
48 } \
49 } while (0)
51 #define FILE_REQUIRE_ACCESS(scope, path, message, uid, gid, mode, hint, ...) \
52 do { \
53 virValidateCheck(scope, "%s", message); \
54 if (!virPKIValidateFile(path, uid, gid, mode)) { \
55 virValidateFail(VIR_VALIDATE_FAIL, hint, __VA_ARGS__); \
56 ok = false; \
57 } else { \
58 virValidatePass(); \
59 } \
60 } while (0)
62 static bool
63 virPKIValidateTrust(bool system, const char *path)
65 g_autofree char *cacert = NULL, *cacrl = NULL;
66 bool ok = true;
68 if (system) {
69 virNetTLSConfigSystemTrust(&cacert,
70 &cacrl);
72 FILE_REQUIRE_EXISTS("TRUST",
73 LIBVIRT_PKI_DIR,
74 _("Checking if system PKI dir exists"),
75 _("The system PKI dir %1$s is usually installed as part of the base filesystem or openssl packages"),
76 LIBVIRT_PKI_DIR);
78 FILE_REQUIRE_ACCESS("TRUST",
79 LIBVIRT_PKI_DIR,
80 _("Checking system PKI dir access"),
81 0, 0, 0755,
82 _("The system PKI dir %1$s must be accessible to all users. As root, run: chown root.root; chmod 0755 %2$s"),
83 LIBVIRT_PKI_DIR, LIBVIRT_PKI_DIR);
86 FILE_REQUIRE_EXISTS("TRUST",
87 LIBVIRT_CACERT_DIR,
88 _("Checking if system CA dir exists"),
89 _("The system CA dir %1$s is usually installed as part of the base filesystem or openssl packages"),
90 LIBVIRT_CACERT_DIR);
92 FILE_REQUIRE_ACCESS("TRUST",
93 LIBVIRT_CACERT_DIR,
94 _("Checking system CA dir access"),
95 0, 0, 0755,
96 _("The system CA dir %1$s must be accessible to all users. As root, run: chown root.root; chmod 0755 %2$s"),
97 LIBVIRT_CACERT_DIR, LIBVIRT_CACERT_DIR);
98 } else if (path) {
99 virNetTLSConfigCustomTrust(path,
100 &cacert,
101 &cacrl);
103 FILE_REQUIRE_EXISTS("TRUST",
104 path,
105 _("Checking if custom PKI base dir exists"),
106 _("Create the dir %1$s"),
107 path);
109 FILE_REQUIRE_ACCESS("TRUST",
110 path,
111 _("Checking custom PKI base dir access"),
112 getuid(), getgid(), 0700,
113 _("The PKI base dir %1$s must not be accessible to other users. Run: chown %2$d.%3$d %4$s; chmod 0700 %5$s"),
114 path, getuid(), getgid(), path, path);
115 } else {
116 g_autofree char *pkipath = virNetTLSConfigUserPKIBaseDir();
118 virNetTLSConfigUserTrust(&cacert,
119 &cacrl);
121 FILE_REQUIRE_EXISTS("TRUST",
122 pkipath,
123 _("Checking if user PKI base dir exists"),
124 _("Create the dir %1$s"),
125 pkipath);
127 FILE_REQUIRE_ACCESS("TRUST",
128 pkipath,
129 _("Checking user PKI base dir access"),
130 getuid(), getgid(), 0700,
131 _("The PKI base dir %1$s must not be accessible to other users. Run: chown %2$d.%3$d %4$s; chmod 0700 %5$s"),
132 pkipath, getuid(), getgid(), pkipath, pkipath);
135 FILE_REQUIRE_EXISTS("TRUST",
136 cacert,
137 _("Checking if CA cert exists"),
138 _("The machine cannot act as a client or server. See https://libvirt.org/kbase/tlscerts.html#setting-up-a-certificate-authority-ca on how to install %1$s"),
139 cacert);
141 if (system) {
142 FILE_REQUIRE_ACCESS("TRUST",
143 cacert,
144 _("Checking CA cert access"),
145 0, 0, 0644,
146 _("The CA certificate %1$s must be accessible to all users. As root run: chown root.root %2$s; chmod 0644 %3$s"),
147 cacert, cacert, cacert);
148 } else {
149 FILE_REQUIRE_ACCESS("TRUST",
150 cacert,
151 _("Checking CA cert access"),
152 getuid(), getgid(), 0600,
153 _("The CA certificate %1$s must not be accessible to other users. As this user, run: chown %2$d.%3$d %4$s; chmod 0600 %5$s"),
154 cacert, getuid(), getgid(), cacert, cacert);
157 done:
158 return ok;
161 static bool
162 virPKIValidateIdentity(bool isServer, bool system, const char *path)
164 g_autofree char *cacert = NULL, *cacrl = NULL;
165 g_autofree char *cert = NULL, *key = NULL;
166 bool ok = true;
167 const char *scope = isServer ? "SERVER" : "CLIENT";
169 if (system) {
170 virNetTLSConfigSystemTrust(&cacert,
171 &cacrl);
172 virNetTLSConfigSystemIdentity(isServer,
173 &cert,
174 &key);
176 FILE_REQUIRE_EXISTS(scope,
177 LIBVIRT_CERT_DIR,
178 _("Checking if system cert dir exists"),
179 _("The system cert dir %1$s is usually installed as part of the libvirt package"),
180 LIBVIRT_CERT_DIR);
182 FILE_REQUIRE_ACCESS(scope,
183 LIBVIRT_CERT_DIR,
184 _("Checking system cert dir access"),
185 0, 0, 0755,
186 _("The system cert dir %1$s must be accessible to all users. As root, run: chown root.root; chmod 0755 %2$s"),
187 LIBVIRT_CERT_DIR, LIBVIRT_CERT_DIR);
189 FILE_REQUIRE_EXISTS(scope,
190 LIBVIRT_KEY_DIR,
191 _("Checking if system key dir exists"),
192 _("The system key dir %1$s is usually installed as part of the libvirt package"),
193 LIBVIRT_KEY_DIR);
195 FILE_REQUIRE_ACCESS(scope,
196 LIBVIRT_KEY_DIR,
197 _("Checking system key dir access"),
198 0, 0, 0755,
199 _("The system key dir %1$s must be accessible to all users. As root, run: chown root.root; chmod 0755 %2$s"),
200 LIBVIRT_KEY_DIR, LIBVIRT_KEY_DIR);
201 } else if (path) {
202 virNetTLSConfigCustomTrust(path,
203 &cacert,
204 &cacrl);
205 virNetTLSConfigCustomIdentity(path,
206 isServer,
207 &cert,
208 &key);
209 } else {
210 virNetTLSConfigUserTrust(&cacert,
211 &cacrl);
212 virNetTLSConfigUserIdentity(isServer,
213 &cert,
214 &key);
217 FILE_REQUIRE_EXISTS(scope,
218 key,
219 _("Checking if key exists"),
220 isServer ?
221 _("The machine cannot act as a server. See https://libvirt.org/kbase/tlscerts.html#issuing-server-certificates on how to regenerate %1$s") :
222 _("The machine cannot act as a client. See https://libvirt.org/kbase/tlscerts.html#issuing-client-certificates on how to regenerate %1$s"),
223 key);
225 if (system) {
226 FILE_REQUIRE_ACCESS(scope,
227 key,
228 _("Checking key access"),
229 0, 0, isServer ? 0600 : 0644,
230 isServer ?
231 _("The server key %1$s must not be accessible to unprivileged users. As root run: chown root.root %2$s; chmod 0600 %3$s") :
232 _("The client key %1$s must be accessible to all users. As root run: chown root.root %2$s; chmod 0644 %3$s"),
233 key, key, key);
234 } else {
235 FILE_REQUIRE_ACCESS(scope,
236 key,
237 _("Checking key access"),
238 getuid(), getgid(), 0600,
239 isServer ?
240 _("The server key %1$s must be not be accessible to other users. As this user, run: chown %2$d.%3$d %4$s; chmod 0600 %5$s") :
241 _("The client key %1$s must be not be accessible to other users. As this user, run: chown %2$d.%3$d %4$s; chmod 0600 %5$s"),
242 key, getuid(), getgid(), key, key);
245 FILE_REQUIRE_EXISTS(scope,
246 cert,
247 _("Checking if cert exists"),
248 isServer ?
249 _("The machine cannot act as a server. See https://libvirt.org/kbase/tlscerts.html#issuing-server-certificates on how to regenerate %1$s") :
250 _("The machine cannot act as a client. See https://libvirt.org/kbase/tlscerts.html#issuing-client-certificates on how to regenerate %1$s"),
251 cert);
253 if (system) {
254 FILE_REQUIRE_ACCESS(scope,
255 cert,
256 _("Checking cert access"),
257 0, 0, 0644,
258 isServer ?
259 _("The server cert %1$s must be accessible to all users. As root run: chown root.root %2$s; chmod 0644 %3$s") :
260 _("The client cert %1$s must be accessible to all users. As root run: chown root.root %2$s; chmod 0644 %3$s"),
261 cert, cert, cert);
262 } else {
263 FILE_REQUIRE_ACCESS(scope,
264 cert,
265 _("Checking cert access"),
266 getuid(), getgid(), 0600,
267 isServer ?
268 _("The server cert %1$s must be restricted to this user. As this user, run: chown %2$d.%3$d %4$s; chmod 0600 %5$s") :
269 _("The client cert %1$s must be restricted to this user. As this user, run: chown %2$d.%3$d %4$s; chmod 0600 %5$s"),
270 cert, getuid(), getgid(), cert, cert);
273 virValidateCheck(scope, "%s", _("Checking cert properties"));
275 if (virNetTLSCertSanityCheck(isServer,
276 cacert,
277 cert) < 0) {
278 virValidateFail(VIR_VALIDATE_FAIL, "%s",
279 virGetLastErrorMessage());
280 ok = false;
281 } else {
282 virValidatePass();
285 if (isServer) {
286 gnutls_x509_crt_t crt;
288 virValidateCheck(scope, "%s", _("Checking cert hostname match"));
290 if (!(crt = virNetTLSCertLoadFromFile(cert, true))) {
291 virValidateFail(VIR_VALIDATE_FAIL,
292 _("Unable to load %1$s: %2$s"),
293 cert, virGetLastErrorMessage());
294 } else {
295 g_autofree char *hostname = virGetHostname();
296 int ret = gnutls_x509_crt_check_hostname(crt, hostname);
297 gnutls_x509_crt_deinit(crt);
298 if (!ret) {
299 /* Only warning, since there can be valid reasons for mis-match */
300 virValidateFail(VIR_VALIDATE_WARN,
301 _("Certificate %1$s owner does not match the hostname %2$s"),
302 cert, hostname);
303 ok = false;
304 } else {
305 virValidatePass();
310 done:
311 return ok;
315 static void
316 print_usage(const char *progname,
317 FILE *out)
319 fprintf(out,
320 _("Usage:\n"
321 " %1$s { -v | -h } [TRUST|SERVER|CLIENT]\n"
322 "\n"
323 "Validate TLS certificate configuration\n"
324 "\n"
325 "options:\n"
326 " -s | --system validate system certificates (default)\n"
327 " -u | --user validate user certificates\n"
328 " -p DIR | --path DIR validate custom certificate path\n"
329 " -h | --help display this help and exit\n"
330 " -v | --version output version information and exit\n"),
331 progname);
334 int main(int argc, char **argv)
336 const char *scope = NULL;
337 bool system = false;
338 bool user = false;
339 const char *path = NULL;
340 bool quiet = false;
341 int arg = 0;
342 bool ok = true;
343 const char *progname = argv[0];
344 struct option opt[] = {
345 { "help", no_argument, NULL, 'h' },
346 { "version", no_argument, NULL, 'v' },
347 { "system", no_argument, NULL, 's' },
348 { "user", no_argument, NULL, 'u' },
349 { "path", required_argument, NULL, 'p' },
350 { NULL, 0, NULL, 0 },
353 if (virGettextInitialize() < 0)
354 return EXIT_FAILURE;
356 while ((arg = getopt_long(argc, argv, "hvsup:", opt, NULL)) != -1) {
357 switch (arg) {
358 case 's':
359 system = true;
360 break;
362 case 'u':
363 user = true;
364 break;
366 case 'p':
367 path = optarg;
368 break;
370 case 'v':
371 printf("%s\n", PACKAGE_VERSION);
372 return EXIT_SUCCESS;
374 case 'h':
375 print_usage(progname, stdout);
376 return EXIT_SUCCESS;
378 case 'q':
379 quiet = true;
380 break;
382 case '?':
383 default:
384 print_usage(progname, stderr);
385 return EXIT_FAILURE;
389 if ((argc - optind) > 2) {
390 fprintf(stderr, _("%1$s: too many command line arguments\n"), argv[0]);
391 print_usage(progname, stderr);
392 return EXIT_FAILURE;
395 if (argc > 1)
396 scope = argv[optind];
398 virValidateSetQuiet(quiet);
400 if ((system && user) ||
401 (system && path) ||
402 (user && path)) {
403 g_printerr("--system, --user & --path are mutually exclusive\n");
404 return EXIT_FAILURE;
407 if (!system && !user && !path)
408 system = true;
410 if ((!scope || g_str_equal(scope, "trust")) &&
411 !virPKIValidateTrust(system, path))
412 ok = false;
413 if ((!scope || g_str_equal(scope, "server")) &&
414 !virPKIValidateIdentity(true, system, path))
415 ok = false;
416 if ((!scope || g_str_equal(scope, "client")) &&
417 !virPKIValidateIdentity(false, system, path))
418 ok = false;
420 if (!ok)
421 return EXIT_FAILURE;
423 return EXIT_SUCCESS;