diff --git a/src/libtdeldap.cpp b/src/libtdeldap.cpp index eef035a..c065eeb 100644 --- a/src/libtdeldap.cpp +++ b/src/libtdeldap.cpp @@ -1,5 +1,5 @@ /*************************************************************************** - * Copyright (C) 2012 by Timothy Pearson * + * Copyright (C) 2012-2013 by Timothy Pearson * * kb9vqf@pearsoncomputing.net * * * * This program is free software; you can redistribute it and/or modify * @@ -56,12 +56,21 @@ // FIXME // This assumes Debian! +#define KRB5_FILE "/etc/krb5.conf" + +#define NSSWITCH_FILE "/etc/nsswitch.conf" + +#define PAMD_DIRECTORY "/etc/pam.d/" +#define PAMD_COMMON_ACCOUNT "common-account" +#define PAMD_COMMON_AUTH "common-auth" + #define LDAP_FILE "/etc/ldap/ldap.conf" #define LDAP_SECONDARY_FILE "/etc/ldap.conf" #define LDAP_TERTIARY_FILE "/etc/libnss-ldap.conf" + #define TDELDAP_SUDO_D_FILE "/etc/sudoers.d/tde-realm-admins" -#define CRON_UPDATE_NSS_FILE "/etc/cron.daily/upd-local-nss-db" +#define CRON_UPDATE_NSS_FILE "/etc/cron.daily/upd-local-nss-db" #define CRON_UPDATE_NSS_COMMAND "/usr/sbin/nss_updatedb ldap" int requested_ldap_version = LDAP_VERSION3; @@ -2229,7 +2238,7 @@ int LDAPManager::moveKerberosEntries(TQString newSuffix, TQString* errstr) { return -1; } -void LDAPManager::writeLDAPConfFile(LDAPRealmConfig realmcfg) { +int LDAPManager::writeLDAPConfFile(LDAPRealmConfig realmcfg, TQString *errstr) { KSimpleConfig* systemconfig; TQString m_defaultRealm; int m_ldapVersion; @@ -2276,37 +2285,39 @@ void LDAPManager::writeLDAPConfFile(LDAPRealmConfig realmcfg) { } if (chmod(LDAP_FILE, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH) < 0) { - printf("ERROR: Unable to change permissions of \"%s\"\n\r", LDAP_FILE); - return; + if (errstr) *errstr = TQString("Unable to change permissions of \"%1\"").arg(LDAP_FILE); + return -1; } // Create symbolic link to secondary LDAP configuration file if (fileExists(LDAP_SECONDARY_FILE)) { if (unlink(LDAP_SECONDARY_FILE) < 0) { - printf("ERROR: Unable to unlink \"%s\"\n\r", LDAP_SECONDARY_FILE); - return; + if (errstr) *errstr = TQString("Unable to unlink \"%s\"").arg(LDAP_SECONDARY_FILE); + return -1; } } command = TQString("ln -s %1 %2").arg(LDAP_FILE).arg(LDAP_SECONDARY_FILE); if (system(command) < 0) { - printf("ERROR: Execution of \"%s\" failed!\n\r", command.ascii()); - return; + if (errstr) *errstr = TQString("Execution of \"%s\" failed").arg(command.ascii()); + return -1; } // Create symbolic link to tertiary LDAP configuration file if (fileExists(LDAP_TERTIARY_FILE)) { if (unlink(LDAP_TERTIARY_FILE) < 0) { - printf("ERROR: Unable to unlink \"%s\"\n\r", LDAP_TERTIARY_FILE); - return; + if (errstr) *errstr = TQString("Unable to unlink \"%s\"").arg(LDAP_TERTIARY_FILE); + return -1; } } command = TQString("ln -s %1 %2").arg(LDAP_FILE).arg(LDAP_TERTIARY_FILE); if (system(command) < 0) { - printf("ERROR: Execution of \"%s\" failed!\n\r", command.ascii()); - return; + if (errstr) *errstr = TQString("Execution of \"%s\" failed").arg(command.ascii()); + return -1; } delete systemconfig; + + return 0; } LDAPTDEBuiltinsInfo LDAPManager::parseLDAPTDEBuiltinsRecord(LDAPMessage* entry) { @@ -2486,7 +2497,7 @@ int LDAPManager::writeSudoersConfFile(TQString *errstr) { return 0; } -void LDAPManager::writeCronFiles() { +int LDAPManager::writeClientCronFiles(TQString *errstr) { TQFile file(CRON_UPDATE_NSS_FILE); if (file.open(IO_WriteOnly)) { TQTextStream stream( &file ); @@ -2501,9 +2512,11 @@ void LDAPManager::writeCronFiles() { } if (system(CRON_UPDATE_NSS_COMMAND) < 0) { - printf("ERROR: Execution of \"%s\" failed!\n\r", CRON_UPDATE_NSS_COMMAND); - return; + if (errstr) *errstr = TQString("Execution of \"%s\" failed").arg(CRON_UPDATE_NSS_COMMAND); + return -1; } + + return 0; } void LDAPManager::writePrimaryRealmCertificateUpdateCronFile() { @@ -2565,7 +2578,7 @@ LDAPRealmConfigList LDAPManager::readTDERealmList(KSimpleConfig* config, bool di return realms; } -void LDAPManager::writeTDERealmList(LDAPRealmConfigList realms, KSimpleConfig* config) { +int LDAPManager::writeTDERealmList(LDAPRealmConfigList realms, KSimpleConfig* config, TQString *errstr) { LDAPRealmConfigList::Iterator it; for (it = realms.begin(); it != realms.end(); ++it) { LDAPRealmConfig realmcfg = it.data(); @@ -2599,6 +2612,8 @@ void LDAPManager::writeTDERealmList(LDAPRealmConfigList realms, KSimpleConfig* c } } } + + return 0; } TQDateTime LDAPManager::getCertificateExpiration(TQString certfile) { @@ -2744,7 +2759,170 @@ TQString LDAPManager::getMachineFQDN() { return fqdn; } -int LDAPManager::bondRealm(LDAPRealmConfig realmcfg, TQString adminUserName, const char * adminPassword, TQString adminRealm, TQString *errstr) { +LDAPClientRealmConfig LDAPManager::loadClientRealmConfig(KSimpleConfig* config, bool useDefaults) { + LDAPClientRealmConfig clientRealmConfig; + + config->setReadDefaults(useDefaults); + + config->setGroup(NULL); + clientRealmConfig.enable_bonding = config->readBoolEntry("EnableLDAP", false); + clientRealmConfig.defaultRealm = config->readEntry("DefaultRealm", TQString::null); + clientRealmConfig.ticketLifetime = config->readNumEntry("TicketLifetime", 86400); + clientRealmConfig.ldapRole = config->readEntry("LDAPRole", "Workstation"); + if (LDAPManager::getMachineFQDN() == config->readEntry("HostFQDN", "")) { + clientRealmConfig.configurationVerifiedForLocalMachine = true; + } + else { + clientRealmConfig.configurationVerifiedForLocalMachine = false; + } + + clientRealmConfig.ldapVersion = config->readNumEntry("ConnectionLDAPVersion", 3); + clientRealmConfig.ldapTimeout = config->readNumEntry("ConnectionLDAPTimeout", 2); + clientRealmConfig.bindPolicy = config->readEntry("ConnectionBindPolicy", "soft"); + clientRealmConfig.ldapBindTimeout = config->readNumEntry("ConnectionBindTimeout", 2); + clientRealmConfig.passwordHash = config->readEntry("ConnectionPasswordHash", "exop"); + clientRealmConfig.ignoredUsers = config->readEntry("ConnectionIgnoredUsers", DEFAULT_IGNORED_USERS_LIST); + + return clientRealmConfig; +} + +int LDAPManager::saveClientRealmConfig(LDAPClientRealmConfig clientRealmConfig, KSimpleConfig* config, TQString *errstr) { + config->setGroup(NULL); + config->writeEntry("EnableLDAP", clientRealmConfig.enable_bonding); + config->writeEntry("HostFQDN", clientRealmConfig.hostFQDN); + + if (clientRealmConfig.defaultRealm != "") { + config->writeEntry("DefaultRealm", clientRealmConfig.defaultRealm); + } + else { + config->deleteEntry("DefaultRealm"); + } + config->writeEntry("TicketLifetime", clientRealmConfig.ticketLifetime); + + config->writeEntry("ConnectionLDAPVersion", clientRealmConfig.ldapVersion); + config->writeEntry("ConnectionLDAPTimeout", clientRealmConfig.ldapTimeout); + config->writeEntry("ConnectionBindPolicy", clientRealmConfig.bindPolicy); + config->writeEntry("ConnectionBindTimeout", clientRealmConfig.ldapBindTimeout); + config->writeEntry("ConnectionPasswordHash", clientRealmConfig.passwordHash); + config->writeEntry("ConnectionIgnoredUsers", clientRealmConfig.ignoredUsers); + + return 0; +} + +int LDAPManager::writeClientKrb5ConfFile(LDAPClientRealmConfig clientRealmConfig, LDAPRealmConfigList realmList, TQString *errstr) { + TQFile file(KRB5_FILE); + if (file.open(IO_WriteOnly)) { + TQTextStream stream( &file ); + + stream << "# This file was automatically generated by TDE\n"; + stream << "# All changes will be lost!\n"; + stream << "\n"; + + // Defaults + stream << "[libdefaults]\n"; + stream << " ticket_lifetime = " << clientRealmConfig.ticketLifetime << "\n"; + if (clientRealmConfig.defaultRealm != "") { + stream << " default_realm = " << clientRealmConfig.defaultRealm << "\n"; + } + stream << "\n"; + + // Realms + stream << "[realms]\n"; + LDAPRealmConfigList::Iterator it; + for (it = realmList.begin(); it != realmList.end(); ++it) { + LDAPRealmConfig realmcfg = it.data(); + stream << " " << realmcfg.name << " = {\n"; + stream << " kdc = " << realmcfg.kdc << ":" << realmcfg.kdc_port << "\n"; + stream << " admin_server = " << realmcfg.admin_server << ":" << realmcfg.admin_server_port << "\n"; + stream << " pkinit_require_eku = " << (realmcfg.pkinit_require_eku?"true":"false") << "\n"; + stream << " pkinit_require_krbtgt_otherName = " << (realmcfg.pkinit_require_krbtgt_otherName?"true":"false") << "\n"; + stream << " win2k_pkinit = " << (realmcfg.win2k_pkinit?"yes":"no") << "\n"; + stream << " win2k_pkinit_require_binding = " << (realmcfg.win2k_pkinit_require_binding?"yes":"no") << "\n"; + stream << " }\n"; + } + stream << "\n"; + + // Domain aliases + stream << "[domain_realm]\n"; + LDAPRealmConfigList::Iterator it2; + for (it2 = realmList.begin(); it2 != realmList.end(); ++it2) { + LDAPRealmConfig realmcfg = it2.data(); + TQStringList domains = realmcfg.domain_mappings; + for (TQStringList::Iterator it3 = domains.begin(); it3 != domains.end(); ++it3 ) { + stream << " " << *it3 << " = " << realmcfg.name << "\n"; + } + } + + file.close(); + } + + return 0; +} + +int LDAPManager::writeNSSwitchFile(TQString *errstr) { + TQFile file(NSSWITCH_FILE); + if (file.open(IO_WriteOnly)) { + TQTextStream stream( &file ); + + stream << "# This file was automatically generated by TDE\n"; + stream << "# All changes will be lost!\n"; + stream << "\n"; + stream << "passwd: files ldap [NOTFOUND=return] db" << "\n"; + stream << "group: files ldap [NOTFOUND=return] db" << "\n"; + stream << "shadow: files ldap [NOTFOUND=return] db" << "\n"; + stream << "\n"; + stream << "hosts: files mdns4_minimal [NOTFOUND=return] dns mdns4" << "\n"; + stream << "networks: files" << "\n"; + stream << "\n"; + stream << "protocols: db files" << "\n"; + stream << "services: db files" << "\n"; + stream << "ethers: db files" << "\n"; + stream << "rpc: db files" << "\n"; + stream << "\n"; + stream << "netgroup: nis" << "\n"; + + file.close(); + } + + return 0; +} + +int LDAPManager::writePAMFiles(TQString *errstr) { + TQFile file(PAMD_DIRECTORY PAMD_COMMON_ACCOUNT); + if (file.open(IO_WriteOnly)) { + TQTextStream stream( &file ); + + stream << "# This file was automatically generated by TDE\n"; + stream << "# All changes will be lost!\n"; + stream << "\n"; + stream << "account sufficient pam_unix.so nullok_secure" << "\n"; + stream << "account sufficient pam_ldap.so" << "\n"; + stream << "account required pam_permit.so" << "\n"; + + file.close(); + } + + TQFile file2(PAMD_DIRECTORY PAMD_COMMON_AUTH); + if (file2.open(IO_WriteOnly)) { + TQTextStream stream( &file2 ); + + stream << "# This file was automatically generated by TDE\n"; + stream << "# All changes will be lost!\n"; + stream << "\n"; + stream << "auth [default=ignore success=ignore] pam_mount.so" << "\n"; + stream << "auth sufficient pam_unix.so nullok try_first_pass" << "\n"; + stream << "auth [default=ignore success=1 service_err=reset] pam_krb5.so ccache=/tmp/krb5cc_%u use_first_pass" << "\n"; + stream << "auth [default=die success=done] pam_ccreds.so action=validate use_first_pass" << "\n"; + stream << "auth sufficient pam_ccreds.so action=store use_first_pass" << "\n"; + stream << "auth required pam_deny.so" << "\n"; + + file2.close(); + } + + return 0; +} + +int LDAPManager::bondRealm(TQString adminUserName, const char * adminPassword, TQString adminRealm, TQString *errstr) { TQCString command = "kadmin"; QCStringList args; args << TQCString("-p") << TQCString(adminUserName+"@"+(adminRealm.upper())) << TQCString("-r") << TQCString(adminRealm.upper()); @@ -2778,6 +2956,10 @@ int LDAPManager::bondRealm(LDAPRealmConfig realmcfg, TQString adminUserName, con return 1; } else if (prompt.endsWith("Principal does not exist")) { + do { // Wait for command prompt + prompt = readFullLineFromPtyProcess(&kadminProc); + printf("(kadmin) '%s'\n\r", prompt.ascii()); + } while (prompt == ""); command = TQCString("ank --random-key "+hoststring); kadminProc.writeLine(command, true); do { // Discard our own input @@ -2834,14 +3016,12 @@ int LDAPManager::bondRealm(LDAPRealmConfig realmcfg, TQString adminUserName, con // Success! kadminProc.writeLine("quit", true); - realmcfg.bonded = true; return 0; } else if (prompt == "kadmin>") { // Success! kadminProc.writeLine("quit", true); - realmcfg.bonded = true; return 0; } @@ -2898,7 +3078,7 @@ int LDAPManager::unbondRealm(LDAPRealmConfig realmcfg, TQString adminUserName, c kadminProc.writeLine("quit", true); // Delete keys from keytab - command = TQString("ktutil remove -p %1").arg(hostprinc); + command = TQString("ktutil remove -p %1").arg(hoststring+"@"+adminRealm.upper()); if (system(command) < 0) { printf("ERROR: Execution of \"%s\" failed!\n\r", command.data()); return 1; // Failure diff --git a/src/libtdeldap.h b/src/libtdeldap.h index f9bea53..a6d47b4 100644 --- a/src/libtdeldap.h +++ b/src/libtdeldap.h @@ -1,5 +1,5 @@ /*************************************************************************** - * Copyright (C) 2012 by Timothy Pearson * + * Copyright (C) 2012-2013 by Timothy Pearson * * kb9vqf@pearsoncomputing.net * * * * This program is free software; you can redistribute it and/or modify * @@ -35,7 +35,11 @@ // Connect this to CMake/Automake #define TDE_BINDIR "/opt/trinity/bin" +#define TDE_LDAP_PID_DIR "/etc/trinity/ldap/pid/" +#define TDE_LDAP_CERT_UPDATER_PID_FILE TDE_LDAP_PID_DIR "/tdeldapcertupdater.pid" + #define TDE_CERTIFICATE_DIR "/etc/trinity/ldap/tde-ca/" + #define KERBEROS_PKI_ANCHORDIR "/etc/trinity/ldap/tde-ca/anchors/" #define KERBEROS_PKI_PRIVATEDIR "/etc/trinity/ldap/tde-ca/private/" #define KERBEROS_PKI_PUBLICDIR "/etc/trinity/ldap/tde-ca/public/" @@ -185,6 +189,26 @@ class LDAPCertConfig TQString emailAddress; }; +// PRIVATE +class LDAPClientRealmConfig +{ + public: + bool enable_bonding; + TQString hostFQDN; + TQString defaultRealm; + int ticketLifetime; + TQString ldapRole; + + int ldapVersion; + int ldapTimeout; + TQString bindPolicy; + int ldapBindTimeout; + TQString passwordHash; + TQString ignoredUsers; + + bool configurationVerifiedForLocalMachine; +}; + typedef TQMap LDAPRealmConfigList; class LDAPUserInfo @@ -402,11 +426,9 @@ class LDAPManager : public TQObject { int getTDECertificate(TQString certificateName, TQString fileName, TQString *errstr=0); int setPasswordForUser(LDAPUserInfo user, TQString *errstr); - static void writeCronFiles(); static void writePrimaryRealmCertificateUpdateCronFile(); static TQString getMachineFQDN(); - static void writeLDAPConfFile(LDAPRealmConfig realmcfg); - static void writeTDERealmList(LDAPRealmConfigList realms, KSimpleConfig* config); + static int writeTDERealmList(LDAPRealmConfigList realms, KSimpleConfig* config, TQString *errstr=0); static LDAPRealmConfigList readTDERealmList(KSimpleConfig* config, bool disableAllBonds=false); static TQDateTime getCertificateExpiration(TQString certfile); @@ -426,7 +448,15 @@ class LDAPManager : public TQObject { static TQString detailedKAdminErrorMessage(TQString initialMessage); static TQString readFullLineFromPtyProcess(PtyProcess* proc); - static int bondRealm(LDAPRealmConfig realmcfg, TQString adminUserName, const char * adminPassword, TQString adminRealm, TQString *errstr=0); + static LDAPClientRealmConfig loadClientRealmConfig(KSimpleConfig* config, bool useDefaults=false); + static int saveClientRealmConfig(LDAPClientRealmConfig clientRealmConfig, KSimpleConfig* config, TQString *errstr=0); + static int writeClientKrb5ConfFile(LDAPClientRealmConfig clientRealmConfig, LDAPRealmConfigList realmList, TQString *errstr=0); + static int writeLDAPConfFile(LDAPRealmConfig realmcfg, TQString *errstr=0); + static int writeNSSwitchFile(TQString *errstr=0); + static int writeClientCronFiles(TQString *errstr=0); + static int writePAMFiles(TQString *errstr=0); + + static int bondRealm(TQString adminUserName, const char * adminPassword, TQString adminRealm, TQString *errstr=0); static int unbondRealm(LDAPRealmConfig realmcfg, TQString adminUserName, const char * adminPassword, TQString adminRealm, TQString *errstr=0); private: