35 #include <unordered_set>
44 #include "XrdVersion.hh"
54 typedef std::unique_ptr<FILE, int(*)(FILE*)> file_smart_ptr;
57 static uint64_t monotonic_time_s() {
59 clock_gettime(CLOCK_MONOTONIC, &tp);
60 return tp.tv_sec + (tp.tv_nsec >= 500000000);
69 Set(
int output_fd,
XrdSysError & err) : m_log(err),m_output_fp(file_smart_ptr(fdopen(XrdSysFD_Dup(output_fd),
"w"), &
fclose)){
70 if(!m_output_fp.get()) {
74 virtual ~Set() =
default;
79 file_smart_ptr m_output_fp;
82 class CASet :
public Set {
84 CASet(
int output_fd,
XrdSysError &err):Set(output_fd,err){}
97 bool processFile(file_smart_ptr &fd,
const std::string &fname);
104 std::unordered_set<std::string> m_known_cas;
109 CASet::processFile(file_smart_ptr &fp,
const std::string &fname)
116 auto ca = chain.
Begin();
117 if (!m_output_fp.get()) {
118 m_log.Emsg(
"CAset",
"No output file has been opened", fname.c_str());
123 auto hash_ptr = ca->SubjectHash();
127 auto iter = m_known_cas.find(hash_ptr);
128 if (iter != m_known_cas.end()) {
134 m_known_cas.insert(hash_ptr);
137 m_log.Emsg(
"CAset",
"Failed to write out CA", fname.c_str());
143 fflush(m_output_fp.get());
150 class CRLSet :
public Set {
152 CRLSet(
int output_fd,
XrdSysError &err):Set(output_fd,err){}
164 bool processFile(file_smart_ptr &fd,
const std::string &fname);
170 bool atLeastOneValidCRLFound()
const;
177 bool processCRLWithCriticalExt();
184 std::unordered_set<std::string> m_known_crls;
185 std::atomic<bool> m_atLeastOneValidCRLFound;
188 std::vector<std::unique_ptr<XrdCryptosslX509Crl>> m_crls_critical_extension;
193 CRLSet::processFile(file_smart_ptr &fp,
const std::string &fname)
195 if (!m_output_fp.get()) {
196 m_log.Emsg(
"CRLSet",
"No output file has been opened", fname.c_str());
201 for (std::unique_ptr<XrdCryptosslX509Crl> xrd_crl(
new XrdCryptosslX509Crl(fp.get(), fname.c_str()));
203 xrd_crl = std::unique_ptr<XrdCryptosslX509Crl>(
new XrdCryptosslX509Crl(fp.get(), fname.c_str())))
205 auto hash_ptr = xrd_crl->IssuerHash(1);
209 m_atLeastOneValidCRLFound =
true;
210 auto iter = m_known_crls.find(hash_ptr);
211 if (iter != m_known_crls.end()) {
216 m_known_crls.insert(hash_ptr);
218 if(xrd_crl->hasCriticalExtension()) {
221 m_crls_critical_extension.emplace_back(std::move(xrd_crl));
224 if (!xrd_crl->ToFile(m_output_fp.get())) {
225 m_log.Emsg(
"CRLset",
"Failed to write out CRL", fname.c_str());
226 fflush(m_output_fp.get());
231 fflush(m_output_fp.get());
236 bool CRLSet::atLeastOneValidCRLFound()
const {
237 return m_atLeastOneValidCRLFound;
240 bool CRLSet::processCRLWithCriticalExt() {
241 if(!m_crls_critical_extension.empty()) {
242 if (!m_output_fp.get()) {
243 m_log.Emsg(
"CRLSet",
"No output file has been opened to add CRLs with critical extension");
246 for (
const auto &crl: m_crls_critical_extension) {
247 if (!crl->ToFile(m_output_fp.get())) {
248 m_log.Emsg(
"CRLset",
"Failed to write out CRL with critical extension", crl->ParentFile());
249 fflush(m_output_fp.get());
253 fflush(m_output_fp.get());
261 std::unique_ptr<XrdTlsTempCA::TempCAGuard>
264 if (-1 ==
mkdir(ca_tmp_dir.c_str(), S_IRWXU) && errno != EEXIST) {
265 err.
Emsg(
"TempCA",
"Unable to create CA temp directory", ca_tmp_dir.c_str(), strerror(errno));
268 std::stringstream ss;
269 ss << ca_tmp_dir <<
"/ca_file.XXXXXX.pem";
270 std::vector<char> ca_fname;
271 ca_fname.resize(ss.str().size() + 1);
272 memcpy(ca_fname.data(), ss.str().c_str(), ss.str().size());
274 int ca_fd = mkstemps(ca_fname.data(), 4);
276 err.
Emsg(
"TempCA",
"Failed to create temp file:", strerror(errno));
277 return std::unique_ptr<TempCAGuard>();
280 std::stringstream ss2;
281 ss2 << ca_tmp_dir <<
"/crl_file.XXXXXX.pem";
282 std::vector<char> crl_fname;
283 crl_fname.resize(ss2.str().size() + 1);
284 memcpy(crl_fname.data(), ss2.str().c_str(), ss2.str().size());
286 int crl_fd = mkstemps(crl_fname.data(), 4);
288 err.
Emsg(
"TempCA",
"Failed to create temp file:", strerror(errno));
289 return std::unique_ptr<TempCAGuard>();
291 return std::unique_ptr<TempCAGuard>(
new TempCAGuard(ca_fd, crl_fd, ca_tmp_dir, ca_fname.data(), crl_fname.data()));
297 unlink(m_ca_fname.c_str());
301 unlink(m_crl_fname.c_str());
309 if (m_ca_fd < 0 || m_ca_tmp_dir.empty()) {
return false;}
312 std::string ca_fname = m_ca_tmp_dir +
"/ca_file.pem";
313 if (-1 ==
rename(m_ca_fname.c_str(), ca_fname.c_str())) {
316 m_ca_fname = ca_fname;
318 if (m_crl_fd < 0 || m_ca_tmp_dir.empty()) {
return false;}
321 std::string crl_fname = m_ca_tmp_dir +
"/crl_file.pem";
322 if (-1 ==
rename(m_crl_fname.c_str(), crl_fname.c_str())) {
325 m_crl_fname = crl_fname;
332 : m_ca_fd(ca_fd), m_crl_fd(crl_fd), m_ca_tmp_dir(ca_tmp_dir), m_ca_fname(ca_fname), m_crl_fname(crl_fname)
343 if (-1 == XrdSysFD_Pipe(pipes)) {
344 m_log.
Emsg(
"XrdTlsTempCA",
"Failed to create communication pipes", strerror(errno));
347 m_maintenance_pipe_r = pipes[0];
348 m_maintenance_pipe_w = pipes[1];
349 if (-1 == XrdSysFD_Pipe(pipes)) {
350 m_log.
Emsg(
"XrdTlsTempCA",
"Failed to create communication pipes", strerror(errno));
353 m_maintenance_thread_pipe_r = pipes[0];
354 m_maintenance_thread_pipe_w = pipes[1];
355 if (!Maintenance()) {
return;}
359 static_cast<void*
>(
this), 0,
"CA/CRL refresh");
361 m_log.
Emsg(
"XrdTlsTempCA",
"Failed to launch CA monitoring thread");
371 if (m_maintenance_pipe_w >= 0) {
374 do {rval =
write(m_maintenance_pipe_w, indicator, 1);}
while (rval != -1 || errno == EINTR);
375 if (m_maintenance_thread_pipe_r >= 0) {
376 do {rval =
read(m_maintenance_thread_pipe_r, indicator, 1);}
while (rval != -1 || errno == EINTR);
377 close(m_maintenance_thread_pipe_r);
378 close(m_maintenance_thread_pipe_w);
380 close(m_maintenance_pipe_r);
381 close(m_maintenance_pipe_w);
387 XrdTlsTempCA::Maintenance()
389 m_log.
Emsg(
"TempCA",
"Reloading the list of CAs and CRLs in directory");
391 auto adminpath = getenv(
"XRDADMINPATH");
393 m_log.
Emsg(
"TempCA",
"Admin path is not set!");
396 std::string ca_tmp_dir = std::string(adminpath) +
"/.xrdtls";
400 m_log.
Emsg(
"TempCA",
"Failed to create a new temp CA / CRL file");
404 int fddir = XrdSysFD_Open(m_ca_dir.c_str(), O_DIRECTORY);
406 m_log.
Emsg(
"TempCA",
"Failed to open the CA directory", m_ca_dir.c_str());
410 DIR *dirp = fdopendir(fddir);
412 m_log.
Emsg(
"Maintenance",
"Failed to allocate a directory pointer");
416 struct dirent *result;
419 CASet ca_builder(new_file->getCAFD(), m_log);
420 CRLSet crl_builder(new_file->getCRLFD(), m_log);
421 while ((result =
readdir(dirp))) {
423 if (result->d_name[0] ==
'.') {
continue;}
424 if (result->d_type != DT_REG)
425 {
if (result->d_type != DT_UNKNOWN && result->d_type != DT_LNK)
428 if (fstatat(fddir, result->d_name, &
Stat, 0))
429 {m_log.
Emsg(
"Maintenance",
"Failed to stat certificate file",
430 result->d_name, strerror(errno));
433 if (!S_ISREG(
Stat.st_mode))
continue;
435 int fd = XrdSysFD_Openat(fddir, result->d_name, O_RDONLY);
437 m_log.
Emsg(
"Maintenance",
"Failed to open certificate file", result->d_name, strerror(errno));
441 file_smart_ptr fp(fdopen(fd,
"r"), &
fclose);
443 if (!ca_builder.processFile(fp, result->d_name)) {
444 m_log.
Emsg(
"Maintenance",
"Failed to process file for CAs", result->d_name);
447 if (!crl_builder.processFile(fp, result->d_name)) {
448 m_log.
Emsg(
"Maintenance",
"Failed to process file for CRLs", result->d_name);
453 m_log.
Emsg(
"Maintenance",
"Failure during readdir", strerror(errno));
459 if (!crl_builder.processCRLWithCriticalExt()) {
460 m_log.
Emsg(
"Maintenance",
"Failed to insert CRLs with critical extension for CRLs", result->d_name);
462 m_atLeastOneCRLFound = crl_builder.atLeastOneValidCRLFound();
465 if (!new_file->commit()) {
466 m_log.
Emsg(
"Maintenance",
"Failed to finalize new CA / CRL files");
471 m_ca_file.reset(
new std::string(new_file->getCAFilename()));
472 m_crl_file.reset(
new std::string(new_file->getCRLFilename()));
478 void *XrdTlsTempCA::MaintenanceThread(
void *myself_raw)
482 auto now = monotonic_time_s();
483 auto next_update = now + m_update_interval;
485 now = monotonic_time_s();
486 auto remaining = next_update - now;
488 fds.fd = myself->m_maintenance_pipe_r;
490 auto rval = poll(&fds, 1, remaining*1000);
492 if (rval == EINTR)
continue;
494 }
else if (rval == 0) {
495 if (myself->Maintenance()) {
496 next_update = monotonic_time_s() + m_update_interval;
498 next_update = monotonic_time_s() + m_update_interval_failure;
501 if (fds.revents & POLLIN) {
503 do {rval =
read(myself->m_maintenance_pipe_r, indicator, 1);}
while (rval != -1 || errno == EINTR);
508 myself->m_log.Emsg(
"Maintenance",
"Failed to poll for events from parent object");
510 char indicator =
'1';
512 do {rval =
write(myself->m_maintenance_thread_pipe_w, &indicator, 1);}
while (rval != -1 || errno == EINTR);
int XrdCryptosslX509ToFile(XrdCryptoX509 *x509, FILE *file, const char *fname)
int XrdCryptosslX509ParseFile(const char *fname, XrdCryptoX509Chain *chain, const char *fkey)
int stat(const char *path, struct stat *buf)
struct dirent * readdir(DIR *dirp)
int unlink(const char *path)
int rename(const char *oldpath, const char *newpath)
int mkdir(const char *path, mode_t mode)
ssize_t write(int fildes, const void *buf, size_t nbyte)
ssize_t read(int fildes, void *buf, size_t nbyte)
void Cleanup(bool keepCA=0)
int Emsg(const char *esfx, int ecode, const char *text1, const char *text2=0)
static int Run(pthread_t *, void *(*proc)(void *), void *arg, int opts=0, const char *desc=0)
static std::unique_ptr< TempCAGuard > create(XrdSysError &, const std::string &ca_tmp_dir)
TempCAGuard(const TempCAGuard &)=delete
XrdTlsTempCA(XrdSysError *log, std::string ca_dir)