diff --git a/src/libtdeldap.cpp b/src/libtdeldap.cpp index d0022f9..dbb4b3f 100644 --- a/src/libtdeldap.cpp +++ b/src/libtdeldap.cpp @@ -1,5 +1,5 @@ /*************************************************************************** - * Copyright (C) 2012-2013 by Timothy Pearson * + * Copyright (C) 2012-2015 by Timothy Pearson * * kb9vqf@pearsoncomputing.net * * * * This program is free software; you can redistribute it and/or modify * @@ -1385,16 +1385,26 @@ KerberosTicketInfoList LDAPManager::getKerberosTicketList(TQString cache, TQStri return list; } -int LDAPManager::getKerberosPassword(LDAPCredentials &creds, TQString prompt, bool requestServicePrincipal, TQWidget* parent) +LDAPRealmConfigList LDAPManager::fetchAndReadTDERealmList(TQString *defaultRealm) { - int i; - KSimpleConfig* systemconfig = new KSimpleConfig( TQString::fromLatin1( KDE_CONFDIR "/ldap/ldapconfigrc" )); systemconfig->setGroup(NULL); - TQString defaultRealm = systemconfig->readEntry("DefaultRealm", TQString::null); + if (defaultRealm != NULL) { + *defaultRealm = systemconfig->readEntry("DefaultRealm", TQString::null); + } LDAPRealmConfigList realms = LDAPManager::readTDERealmList(systemconfig, false); delete systemconfig; + return realms; +} + +int LDAPManager::getKerberosPassword(LDAPCredentials &creds, TQString prompt, bool requestServicePrincipal, TQWidget* parent) +{ + int i; + + TQString defaultRealm; + LDAPRealmConfigList realms = fetchAndReadTDERealmList(&defaultRealm); + if (creds.realm != "") { defaultRealm = creds.realm; } @@ -3540,13 +3550,18 @@ int LDAPManager::setLDAPMasterReplicationSettings(LDAPMasterReplicationInfo repl return -1; } -int LDAPManager::getTDECertificate(TQString certificateName, TQString fileName, TQString *errstr) { +int LDAPManager::getTDECertificate(TQString certificateName, TQFile *fileHandle, TQString *errstr) { int retcode; int returncode; LDAPTDEBuiltinsInfo builtininfo; TQString dn = TQString("cn=certificate store,o=tde,cn=tde realm data,ou=master services,ou=core,ou=realm,%1").arg(m_basedc); + if (!fileHandle) { + if (errstr) *errstr = i18n("Invalid file handle passed by host application"); + return -1; + } + if (bind(errstr) < 0) { return -1; } @@ -3577,25 +3592,19 @@ int LDAPManager::getTDECertificate(TQString certificateName, TQString fileName, TQString ldap_field = attr; i=0; if (ldap_field == certificateName) { - TQFile file(fileName); - if (file.open(IO_WriteOnly)) { - TQByteArray ba; - ba.duplicate(vals[i]->bv_val, vals[i]->bv_len); - file.writeBlock(ba); - file.close(); - if (chmod(fileName.ascii(), S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH) < 0) { - if (errstr) *errstr = i18n("Unable to change permissions of \"%1\"").arg(fileName.ascii()); - returncode = -1; - } - else { - returncode = 0; - } - } + TQByteArray ba; + ba.duplicate(vals[i]->bv_val, vals[i]->bv_len); + fileHandle->writeBlock(ba); + returncode = 0; } ldap_value_free_len(vals); } ldap_memfree(attr); } + if (returncode != 0) { + // Resource not found + if (errstr) *errstr = i18n("Resource \"%s\" not found in LDAP database").arg(certificateName); + } if (ber != NULL) { ber_free(ber, 0); @@ -3611,6 +3620,25 @@ int LDAPManager::getTDECertificate(TQString certificateName, TQString fileName, return -1; } +int LDAPManager::getTDECertificate(TQString certificateName, TQString fileName, TQString *errstr) { + TQFile file(fileName); + if (file.open(IO_WriteOnly)) { + getTDECertificate(certificateName, &file, errstr); + file.close(); + if (chmod(fileName.ascii(), S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH) < 0) { + if (errstr) *errstr = i18n("Unable to change permissions of \"%1\"").arg(fileName.ascii()); + return -1; + } + else { + return 0; + } + } + else { + if (errstr) *errstr = i18n("Unable to open file \"%1\" for writing").arg(fileName.ascii()); + return -1; + } +} + int LDAPManager::writeSudoersConfFile(TQString *errstr) { LDAPTDEBuiltinsInfo tdebuiltins = getTDEBuiltinMappings(errstr); if (!tdebuiltins.informationValid) { @@ -3946,6 +3974,122 @@ int LDAPManager::generatePublicLDAPCertificate(LDAPCertConfig certinfo, LDAPReal return 0; } +int LDAPManager::generateClientCertificatePair(LDAPCertConfig certinfo, LDAPUserInfo user, LDAPRealmConfig realmcfg, TQString signingPrivateKeyFile, TQString privateKeyFile, TQString publicCertFile, TQString *errstr) { + int ret; + + ret = generateClientCertificatePrivateKey(user, realmcfg, privateKeyFile, errstr); + if (ret == 0) { + ret = generateClientCertificatePublicCertificate(certinfo, user, realmcfg, signingPrivateKeyFile, privateKeyFile, publicCertFile, errstr); + } + + return ret; +} + +int LDAPManager::generateClientCertificatePrivateKey(LDAPUserInfo user, LDAPRealmConfig realmcfg, TQString privateKeyFile, TQString *errstr) { + TQString command; + TQString subject; + + TQString client_keyfile = privateKeyFile; + TQString client_reqfile = privateKeyFile + ".req"; + TQString client_cfgfile = privateKeyFile + ".cfg"; + unsigned int client_key_bit_length = 2048; + + if (writeOpenSSLConfigurationFile(realmcfg, user, client_cfgfile, errstr) != 0) { + return -1; + } + + // Create private key + command = TQString("openssl genrsa -out %1 %2").arg(client_keyfile).arg(client_key_bit_length); + if (system(command) < 0) { + if (errstr) *errstr = TQString("Execution of \"%s\" failed").arg(command); + return -1; + } + + // Secure keyfile + if (chmod(client_keyfile.ascii(), S_IRUSR|S_IWUSR) < 0) { + if (errstr) *errstr = TQString("Unable to change permissions of \"%s\"").arg(client_keyfile); + return -1; + } + if (chown(client_keyfile.ascii(), 0, 0) < 0) { + if (errstr) *errstr = TQString("Unable to change owner of \"%s\"").arg(client_keyfile); + return -1; + } + + // Clean up + if (fileExists(client_cfgfile.ascii())) { + if (unlink(client_cfgfile.ascii()) < 0) { + if (errstr) *errstr = TQString("Unable to unlink \"%s\"").arg(client_keyfile); + return -1; + } + } + + return 0; +} + +int LDAPManager::generateClientCertificatePublicCertificate(LDAPCertConfig certinfo, LDAPUserInfo user, LDAPRealmConfig realmcfg, TQString signingPrivateKeyFile, TQString privateKeyFile, TQString publicCertFile, TQString *errstr) { + TQString command; + TQString subject; + + TQString client_certfile = publicCertFile; + TQString client_keyfile = privateKeyFile; + TQString client_reqfile = privateKeyFile + ".req"; + TQString client_cfgfile = privateKeyFile + ".cfg"; + + // The public certificate location varies based on the machine role + // Prefer the bonded realm's certificate if available + TQString signing_public_certfile = KERBEROS_PKI_PUBLICDIR + realmcfg.admin_server + ".ldap.crt"; + if (!TQFile(signing_public_certfile).exists()) { + signing_public_certfile = KERBEROS_PKI_PEM_FILE; + } + + if (writeOpenSSLConfigurationFile(realmcfg, user, client_cfgfile, errstr) != 0) { + return -1; + } + + TQString common_name = TQString::null; + if (user.name != "") { + common_name = TQString("/uid=%1").arg(user.name); + } + + subject = TQString("\"/C=%1/ST=%2/L=%3/O=%4/OU=%5%6%7\"").arg(certinfo.countryName).arg(certinfo.stateOrProvinceName).arg(certinfo.localityName).arg(certinfo.organizationName).arg(certinfo.orgUnitName).arg(openssldcForRealm(realmcfg.name)).arg(common_name); + command = TQString("openssl req -days %1 -new -out %2 -key %3 -config %4 -subj %5").arg(certinfo.kerberosExpiryDays).arg(client_reqfile).arg(client_keyfile).arg(OPENSSL_EXTENSIONS_FILE).arg(subject); + if (system(command) < 0) { + if (errstr) *errstr = TQString("Execution of \"%s\" failed").arg(command); + return -1; + } + command = TQString("openssl x509 -req -days %1 -in %2 -CAkey %3 -CA %4 -out %5 -extfile %6 -extensions pkinit_client_cert -CAcreateserial").arg(certinfo.kerberosExpiryDays).arg(client_reqfile).arg(signingPrivateKeyFile).arg(signing_public_certfile).arg(client_certfile).arg(OPENSSL_EXTENSIONS_FILE); + if (system(command) < 0) { + if (errstr) *errstr = TQString("Execution of \"%s\" failed").arg(command); + return -1; + } + + // Secure certificate + if (chmod(client_certfile.ascii(), S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH) < 0) { + if (errstr) *errstr = TQString("Unable to change permissions of \"%s\"").arg(client_certfile); + return -1; + } + if (chown(client_certfile.ascii(), 0, 0) < 0) { + if (errstr) *errstr = TQString("Unable to change owner of \"%s\"").arg(client_certfile); + return -1; + } + + // Clean up + if (fileExists(client_cfgfile.ascii())) { + if (unlink(client_cfgfile.ascii()) < 0) { + if (errstr) *errstr = TQString("Unable to unlink \"%s\"").arg(client_certfile); + return -1; + } + } + if (fileExists(client_reqfile.ascii())) { + if (unlink(client_reqfile.ascii()) < 0) { + if (errstr) *errstr = TQString("Unable to unlink \"%s\"").arg(client_reqfile); + return -1; + } + } + + return 0; +} + TQString LDAPManager::getMachineFQDN() { struct addrinfo hints, *info, *p; int gai_result; @@ -4205,25 +4349,36 @@ int LDAPManager::writePAMFiles(LDAPPamConfig pamConfig, TQString *errstr) { } int LDAPManager::writeOpenSSLConfigurationFile(LDAPRealmConfig realmcfg, TQString *errstr) { + return writeOpenSSLConfigurationFile(realmcfg, LDAPUserInfo(), TQString::fromLatin1(OPENSSL_EXTENSIONS_FILE), errstr); +} + +int LDAPManager::writeOpenSSLConfigurationFile(LDAPRealmConfig realmcfg, LDAPUserInfo user, TQString opensslConfigFile, TQString *errstr) { TQString crl_url = realmcfg.certificate_revocation_list_url; if (crl_url == "") { // Use a default to preserve certificate validity // crl_url = TQString("http://%1/%2.crl").arg(realmcfg.name).arg(realmcfg.kdc); } - TQDir tde_cert_dir(TDE_CERTIFICATE_DIR); + TQString krb_principal = TQString::null; + if (user.informationValid) { + krb_principal = user.name + "@" + realmcfg.name.upper(); + } + + TQFileInfo tde_cert_file_info(opensslConfigFile); + TQString tde_cert_dir_path = tde_cert_file_info.dirPath(true); + TQDir tde_cert_dir(tde_cert_dir_path); if (!tde_cert_dir.exists()) { - TQString command = TQString("mkdir -p %1").arg(TDE_CERTIFICATE_DIR); + TQString command = TQString("mkdir -p %1").arg(tde_cert_dir_path); if (system(command) < 0) { if (errstr) { - *errstr = i18n("Could not create directory '%1'").arg(TDE_CERTIFICATE_DIR); + *errstr = i18n("Could not create directory '%1'").arg(tde_cert_dir_path); } return 1; } } - TQFile file(TQString::fromLatin1(OPENSSL_EXTENSIONS_FILE)); + TQFile file(opensslConfigFile); if (file.open(IO_WriteOnly)) { - TQTextStream stream( &file ); + TQTextStream stream(&file); stream << "# This file was automatically generated by TDE\n"; stream << "# All changes will be lost!\n"; @@ -4310,31 +4465,33 @@ int LDAPManager::writeOpenSSLConfigurationFile(LDAPRealmConfig realmcfg, TQStrin stream << "\n"; stream << "[v3_ca]" << "\n"; stream << "subjectKeyIdentifier = hash" << "\n"; - stream << "authorityKeyIdentifier=keyid:always,issuer:always" << "\n"; + stream << "authorityKeyIdentifier = keyid:always,issuer:always" << "\n"; stream << "basicConstraints = CA:true" << "\n"; stream << "keyUsage = critical, cRLSign, keyCertSign, keyEncipherment, nonRepudiation, digitalSignature" << "\n"; stream << "\n"; stream << "[usr_cert]" << "\n"; - stream << "basicConstraints=CA:FALSE" << "\n"; + stream << "basicConstraints = CA:FALSE" << "\n"; stream << "keyUsage = critical, nonRepudiation, digitalSignature, keyEncipherment" << "\n"; stream << TQString("crlDistributionPoints = URI:%1").arg(crl_url) << "\n"; stream << "subjectKeyIdentifier = hash" << "\n"; stream << "\n"; stream << "[usr_cert_ke]" << "\n"; - stream << "basicConstraints=CA:FALSE" << "\n"; + stream << "basicConstraints = CA:FALSE" << "\n"; stream << "keyUsage = critical, nonRepudiation, keyEncipherment" << "\n"; stream << TQString("crlDistributionPoints = URI:%1").arg(crl_url) << "\n"; stream << "subjectKeyIdentifier = hash" << "\n"; stream << "\n"; stream << "[proxy_cert]" << "\n"; - stream << "basicConstraints=CA:FALSE" << "\n"; + stream << "basicConstraints = CA:FALSE" << "\n"; stream << "keyUsage = critical, nonRepudiation, digitalSignature, keyEncipherment" << "\n"; stream << TQString("crlDistributionPoints = URI:%1").arg(crl_url) << "\n"; stream << "subjectKeyIdentifier = hash" << "\n"; // stream << "proxyCertInfo=critical,language:id-ppl-anyLanguage,pathlen:0,policy:text:foo" << "\n"; stream << "\n"; - stream << "[pkinitc_principals]" << "\n"; - // stream << "princ1 = GeneralString:bar" << "\n"; + stream << "[pkinitc_principals]" << "\n"; + if (krb_principal != "") { + stream << TQString("princ1 = GeneralString:%1").arg(krb_principal) << "\n"; + } stream << "\n"; stream << "[pkinitc_principal_seq]" << "\n"; stream << "name_type = EXP:0,INTEGER:1" << "\n"; @@ -4345,28 +4502,28 @@ int LDAPManager::writeOpenSSLConfigurationFile(LDAPRealmConfig realmcfg, TQStrin stream << "principal_name = EXP:1,SEQUENCE:pkinitc_principal_seq" << "\n"; stream << "\n"; stream << "[pkinit_client_cert]" << "\n"; - stream << "basicConstraints=CA:FALSE" << "\n"; + stream << "basicConstraints = CA:FALSE" << "\n"; stream << "keyUsage = critical, nonRepudiation, digitalSignature, keyEncipherment" << "\n"; stream << TQString("crlDistributionPoints = URI:%1").arg(crl_url) << "\n"; stream << "subjectKeyIdentifier = hash" << "\n"; - stream << "authorityKeyIdentifier=keyid,issuer" << "\n"; + stream << "authorityKeyIdentifier = keyid,issuer" << "\n"; stream << "issuerAltName=issuer:copy" << "\n"; stream << "subjectAltName=otherName:1.3.6.1.5.2.2;SEQUENCE:pkinitc_princ_name" << "\n"; stream << "\n"; stream << "[https_cert]" << "\n"; - stream << "basicConstraints=CA:FALSE" << "\n"; + stream << "basicConstraints = CA:FALSE" << "\n"; stream << "keyUsage = critical, nonRepudiation, digitalSignature, keyEncipherment" << "\n"; stream << TQString("crlDistributionPoints = URI:%1").arg(crl_url) << "\n"; // stream << "extendedKeyUsage = https-server XXX" << "\n"; stream << "subjectKeyIdentifier = hash" << "\n"; stream << "\n"; stream << "[pkinit_kdc_cert]" << "\n"; - stream << "basicConstraints=CA:FALSE" << "\n"; + stream << "basicConstraints = CA:FALSE" << "\n"; stream << "keyUsage = critical, nonRepudiation, digitalSignature, keyEncipherment" << "\n"; stream << TQString("crlDistributionPoints = URI:%1").arg(crl_url) << "\n"; stream << "extendedKeyUsage = 1.3.6.1.5.2.3.5" << "\n"; stream << "subjectKeyIdentifier = hash" << "\n"; - stream << "authorityKeyIdentifier=keyid,issuer" << "\n"; + stream << "authorityKeyIdentifier = keyid,issuer" << "\n"; stream << "issuerAltName=issuer:copy" << "\n"; stream << "subjectAltName=otherName:1.3.6.1.5.2.2;SEQUENCE:pkinitkdc_princ_name" << "\n"; stream << "\n"; @@ -4383,20 +4540,20 @@ int LDAPManager::writeOpenSSLConfigurationFile(LDAPRealmConfig realmcfg, TQStrin stream << TQString("princ2 = GeneralString:%1").arg(realmcfg.name.upper()) << "\n"; stream << "\n"; stream << "[proxy10_cert]" << "\n"; - stream << "basicConstraints=CA:FALSE" << "\n"; + stream << "basicConstraints = CA:FALSE" << "\n"; stream << "keyUsage = critical, nonRepudiation, digitalSignature, keyEncipherment" << "\n"; stream << TQString("crlDistributionPoints = URI:%1").arg(crl_url) << "\n"; stream << "subjectKeyIdentifier = hash" << "\n"; // stream << "proxyCertInfo=critical,language:id-ppl-anyLanguage,pathlen:10,policy:text:foo" << "\n"; stream << "\n"; stream << "[usr_cert_ds]" << "\n"; - stream << "basicConstraints=CA:FALSE" << "\n"; + stream << "basicConstraints = CA:FALSE" << "\n"; stream << "keyUsage = critical, nonRepudiation, digitalSignature" << "\n"; stream << TQString("crlDistributionPoints = URI:%1").arg(crl_url) << "\n"; stream << "subjectKeyIdentifier = hash" << "\n"; stream << "\n"; stream << "[ocsp_cert]" << "\n"; - stream << "basicConstraints=CA:FALSE" << "\n"; + stream << "basicConstraints = CA:FALSE" << "\n"; stream << "keyUsage = critical, nonRepudiation, digitalSignature, keyEncipherment" << "\n"; stream << TQString("crlDistributionPoints = URI:%1").arg(crl_url) << "\n"; // stream << "ocsp-nocheck and kp-OCSPSigning" << "\n"; @@ -4764,6 +4921,16 @@ KerberosTicketInfo::~KerberosTicketInfo() { // } +LDAPCertConfig::LDAPCertConfig() { + caExpiryDays = KERBEROS_PKI_PEMKEY_EXPIRY_DAYS; + kerberosExpiryDays = KERBEROS_PKI_KRB_EXPIRY_DAYS; + ldapExpiryDays = KERBEROS_PKI_LDAP_EXPIRY_DAYS; +} + +LDAPCertConfig::~LDAPCertConfig() { + // +} + LDAPPamConfig::LDAPPamConfig() { enable_cached_credentials = true; autocreate_user_directories_enable = true; diff --git a/src/libtdeldap.h b/src/libtdeldap.h index 9c356a5..f472c6c 100644 --- a/src/libtdeldap.h +++ b/src/libtdeldap.h @@ -1,5 +1,5 @@ /*************************************************************************** - * Copyright (C) 2012-2013 by Timothy Pearson * + * Copyright (C) 2012-2015 by Timothy Pearson * * kb9vqf@pearsoncomputing.net * * * * This program is free software; you can redistribute it and/or modify * @@ -29,6 +29,7 @@ #include #include #include +#include #include @@ -186,6 +187,10 @@ class LDAPRealmConfig // PRIVATE class LDAPCertConfig { + public: + LDAPCertConfig(); + ~LDAPCertConfig(); + public: bool generate_certs; TQString provided_kerberos_pem; @@ -504,12 +509,14 @@ class LDAPManager : public TQObject { LDAPMasterReplicationInfo getLDAPMasterReplicationSettings(TQString *errstr=0); int setLDAPMasterReplicationSettings(LDAPMasterReplicationInfo replicationinfo, TQString *errstr=0); int writeSudoersConfFile(TQString *errstr=0); + int getTDECertificate(TQString certificateName, TQFile *fileHandle, TQString *errstr=0); int getTDECertificate(TQString certificateName, TQString fileName, TQString *errstr=0); int setPasswordForUser(LDAPUserInfo user, TQString *errstr); static int writePrimaryRealmCertificateUpdateCronFile(TQString *errstr=0); static TQString getMachineFQDN(); static int writeTDERealmList(LDAPRealmConfigList realms, KSimpleConfig* config, TQString *errstr=0); + static LDAPRealmConfigList fetchAndReadTDERealmList(TQString *defaultRealm=0); static LDAPRealmConfigList readTDERealmList(KSimpleConfig* config, bool disableAllBonds=false); static TQDateTime getCertificateExpiration(TQString certfile); @@ -517,6 +524,10 @@ class LDAPManager : public TQObject { static int generatePublicKerberosCertificate(LDAPCertConfig certinfo, LDAPRealmConfig realmcfg); static int generatePublicLDAPCertificate(LDAPCertConfig certinfo, LDAPRealmConfig realmcfg, uid_t ldap_uid, gid_t ldap_gid); + static int generateClientCertificatePair(LDAPCertConfig certinfo, LDAPUserInfo user, LDAPRealmConfig realmcfg, TQString signingPrivateKeyFile, TQString privateKeyFile, TQString publicCertFile, TQString *errstr=0); + static int generateClientCertificatePrivateKey(LDAPUserInfo user, LDAPRealmConfig realmcfg, TQString privateKeyFile, TQString *errstr=0); + static int generateClientCertificatePublicCertificate(LDAPCertConfig certinfo, LDAPUserInfo user, LDAPRealmConfig realmcfg, TQString signingPrivateKeyFile, TQString privateKeyFile, TQString publicCertFile, TQString *errstr=0); + static TQString ldapdnForRealm(TQString realm); static TQString openssldcForRealm(TQString realm); static TQString cnFromDn(TQString dn); @@ -536,6 +547,7 @@ class LDAPManager : public TQObject { static int writeLDAPConfFile(LDAPRealmConfig realmcfg, LDAPMachineRole machineRole, TQString *errstr=0); static int writeNSSwitchFile(TQString *errstr=0); static int writeOpenSSLConfigurationFile(LDAPRealmConfig realmcfg, TQString *errstr=0); + static int writeOpenSSLConfigurationFile(LDAPRealmConfig realmcfg, LDAPUserInfo user, TQString opensslConfigFile, TQString *errstr=0); static int writeClientCronFiles(TQString *errstr=0); static int writePAMFiles(LDAPPamConfig pamConfig, TQString *errstr=0);