2 * SPDX-License-Identifier: GPL-2.0-or-later
12 #include <gnutls/gnutls.h>
13 #include <gnutls/x509.h>
15 #include "virgettext.h"
17 #include "virnettlsconfig.h"
18 #include "virnettlscert.h"
20 #include "virt-validate-common.h"
23 virPKIValidateFile(const char *file
,
29 if (stat(file
, &sb
) < 0)
32 if (sb
.st_uid
!= owner
||
36 return (sb
.st_mode
& 0777) == mode
;
39 #define FILE_REQUIRE_EXISTS(scope, path, message, hint, ...) \
41 virValidateCheck(scope, "%s", message); \
42 if (!virFileExists(path)) { \
43 virValidateFail(VIR_VALIDATE_FAIL, hint, __VA_ARGS__); \
51 #define FILE_REQUIRE_ACCESS(scope, path, message, uid, gid, mode, hint, ...) \
53 virValidateCheck(scope, "%s", message); \
54 if (!virPKIValidateFile(path, uid, gid, mode)) { \
55 virValidateFail(VIR_VALIDATE_FAIL, hint, __VA_ARGS__); \
63 virPKIValidateTrust(bool system
, const char *path
)
65 g_autofree
char *cacert
= NULL
, *cacrl
= NULL
;
69 virNetTLSConfigSystemTrust(&cacert
,
72 FILE_REQUIRE_EXISTS("TRUST",
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"),
78 FILE_REQUIRE_ACCESS("TRUST",
80 _("Checking system PKI dir access"),
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",
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"),
92 FILE_REQUIRE_ACCESS("TRUST",
94 _("Checking system CA dir access"),
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
);
99 virNetTLSConfigCustomTrust(path
,
103 FILE_REQUIRE_EXISTS("TRUST",
105 _("Checking if custom PKI base dir exists"),
106 _("Create the dir %1$s"),
109 FILE_REQUIRE_ACCESS("TRUST",
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
);
116 g_autofree
char *pkipath
= virNetTLSConfigUserPKIBaseDir();
118 virNetTLSConfigUserTrust(&cacert
,
121 FILE_REQUIRE_EXISTS("TRUST",
123 _("Checking if user PKI base dir exists"),
124 _("Create the dir %1$s"),
127 FILE_REQUIRE_ACCESS("TRUST",
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",
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"),
142 FILE_REQUIRE_ACCESS("TRUST",
144 _("Checking CA cert access"),
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
);
149 FILE_REQUIRE_ACCESS("TRUST",
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
);
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
;
167 const char *scope
= isServer
? "SERVER" : "CLIENT";
170 virNetTLSConfigSystemTrust(&cacert
,
172 virNetTLSConfigSystemIdentity(isServer
,
176 FILE_REQUIRE_EXISTS(scope
,
178 _("Checking if system cert dir exists"),
179 _("The system cert dir %1$s is usually installed as part of the libvirt package"),
182 FILE_REQUIRE_ACCESS(scope
,
184 _("Checking system cert dir access"),
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
,
191 _("Checking if system key dir exists"),
192 _("The system key dir %1$s is usually installed as part of the libvirt package"),
195 FILE_REQUIRE_ACCESS(scope
,
197 _("Checking system key dir access"),
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
);
202 virNetTLSConfigCustomTrust(path
,
205 virNetTLSConfigCustomIdentity(path
,
210 virNetTLSConfigUserTrust(&cacert
,
212 virNetTLSConfigUserIdentity(isServer
,
217 FILE_REQUIRE_EXISTS(scope
,
219 _("Checking if key exists"),
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"),
226 FILE_REQUIRE_ACCESS(scope
,
228 _("Checking key access"),
229 0, 0, isServer
? 0600 : 0644,
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"),
235 FILE_REQUIRE_ACCESS(scope
,
237 _("Checking key access"),
238 getuid(), getgid(), 0600,
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
,
247 _("Checking if cert exists"),
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"),
254 FILE_REQUIRE_ACCESS(scope
,
256 _("Checking cert access"),
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"),
263 FILE_REQUIRE_ACCESS(scope
,
265 _("Checking cert access"),
266 getuid(), getgid(), 0600,
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
,
278 virValidateFail(VIR_VALIDATE_FAIL
, "%s",
279 virGetLastErrorMessage());
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());
295 g_autofree
char *hostname
= virGetHostname();
296 int ret
= gnutls_x509_crt_check_hostname(crt
, hostname
);
297 gnutls_x509_crt_deinit(crt
);
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"),
316 print_usage(const char *progname
,
321 " %1$s { -v | -h } [TRUST|SERVER|CLIENT]\n"
323 "Validate TLS certificate configuration\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"),
334 int main(int argc
, char **argv
)
336 const char *scope
= NULL
;
339 const char *path
= NULL
;
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)
356 while ((arg
= getopt_long(argc
, argv
, "hvsup:", opt
, NULL
)) != -1) {
371 printf("%s\n", PACKAGE_VERSION
);
375 print_usage(progname
, stdout
);
384 print_usage(progname
, stderr
);
389 if ((argc
- optind
) > 2) {
390 fprintf(stderr
, _("%1$s: too many command line arguments\n"), argv
[0]);
391 print_usage(progname
, stderr
);
396 scope
= argv
[optind
];
398 virValidateSetQuiet(quiet
);
400 if ((system
&& user
) ||
403 g_printerr("--system, --user & --path are mutually exclusive\n");
407 if (!system
&& !user
&& !path
)
410 if ((!scope
|| g_str_equal(scope
, "trust")) &&
411 !virPKIValidateTrust(system
, path
))
413 if ((!scope
|| g_str_equal(scope
, "server")) &&
414 !virPKIValidateIdentity(true, system
, path
))
416 if ((!scope
|| g_str_equal(scope
, "client")) &&
417 !virPKIValidateIdentity(false, system
, path
))