diff --git a/src/ldaplogindlgbase.ui b/src/ldaplogindlgbase.ui index a3e855b..8432e79 100644 --- a/src/ldaplogindlgbase.ui +++ b/src/ldaplogindlgbase.ui @@ -68,6 +68,25 @@ + + kerberosOtherInfoString + + + Service Principal + + + true + + + + + kerberosServicePrincipal + + + true + + + unnamed @@ -75,12 +94,12 @@ LDAP Realm - + ldapAdminRealm - + ldapUseTLS diff --git a/src/ldappasswddlg.cpp b/src/ldappasswddlg.cpp index 445956a..444e909 100644 --- a/src/ldappasswddlg.cpp +++ b/src/ldappasswddlg.cpp @@ -32,8 +32,8 @@ #include "ldaplogindlg.h" #include "ldappasswddlg.h" -LDAPPasswordDialog::LDAPPasswordDialog(TQWidget* parent, const char* name) - : KDialogBase(parent, name, true, i18n("LDAP Authentication"), Ok|Cancel|User1, Ok, true, i18n("Authenticate with SASL/GSSAPI")) +LDAPPasswordDialog::LDAPPasswordDialog(TQWidget* parent, const char* name, bool allowGSSAPI) + : KDialogBase(parent, name, true, i18n("LDAP Authentication"), (allowGSSAPI)?Ok|Cancel|User1:Ok|Cancel, Ok, true, i18n("Authenticate with SASL/GSSAPI")) { m_base = new LDAPLogin(this); diff --git a/src/ldappasswddlg.h b/src/ldappasswddlg.h index c9ece35..18e1e54 100644 --- a/src/ldappasswddlg.h +++ b/src/ldappasswddlg.h @@ -31,7 +31,7 @@ class LDAPPasswordDialog : public KDialogBase Q_OBJECT public: - LDAPPasswordDialog(TQWidget* parent = 0, const char* name = 0); + LDAPPasswordDialog(TQWidget* parent = 0, const char* name = 0, bool allowGSSAPI = true); public slots: void slotOk(); diff --git a/src/libtdeldap.cpp b/src/libtdeldap.cpp index cbb5b69..6d954a8 100644 --- a/src/libtdeldap.cpp +++ b/src/libtdeldap.cpp @@ -35,6 +35,7 @@ #include #include #include +#include #include #include @@ -153,6 +154,8 @@ printf("[RAJA DEBUG 600.0] In LDAPManager::bind(%p)\n\r", errstr); fflush(stdout return 0; } + KerberosTicketInfoList m_krbTickets = LDAPManager::getKerberosTicketList(); + bool using_ldapi = false; bool using_gssapi = false; if (m_host.startsWith("ldapi://")) { @@ -164,7 +167,7 @@ printf("[RAJA DEBUG 600.0] In LDAPManager::bind(%p)\n\r", errstr); fflush(stdout } else { printf("[RAJA DEBUG 660.1]\n\r"); fflush(stdout); - LDAPPasswordDialog passdlg(0); + LDAPPasswordDialog passdlg(0, 0, (m_krbTickets.count() > 0)); passdlg.m_base->ldapAdminRealm->setEnabled(false); passdlg.m_base->ldapAdminRealm->insertItem(m_realm); passdlg.m_base->ldapUseTLS->setChecked(true); @@ -865,6 +868,11 @@ int LDAPManager::setPasswordForUser(LDAPUserInfo user, TQString *errstr) { // RAJA FIXME // How to handle GSSAPI auth? + // We can't really at this point + // GSSAPI and friends ONLY WORK if 'kinit -S kadmin/admin' was run after the inital TGT was granted + // What we need is a proper ticket management system + // Also, why doesn't 'kgetcred kadmin/admin' work? + // For now, let's just prompt for the password if admincreds.password == "" TQCString command = "kadmin"; QCStringList args; @@ -928,6 +936,314 @@ int LDAPManager::setPasswordForUser(LDAPUserInfo user, TQString *errstr) { return 1; // Failure } +TQString klistDateTimeToRFCDateTime(TQString datetime) { + // HACK HACK HACK + // FIXME + TQString ret; + TQString command = TQString("date -R -d \"%1\"").arg(datetime); + FILE *output = popen(command.ascii(), "r"); + TQFile f; + f.open(IO_ReadOnly, output); + TQTextStream stream(&f); + ret = stream.readLine(); + f.close(); + pclose(output); + + return ret; +} + +#define KLIST_CREDENTIALS_CACHE_STRING "Credentials cache: " +#define KLIST_PRINCIPAL_STRING "Principal: " +#define KLIST_CACHE_VERSION_STRING "Cache version: " +#define KLIST_SERVER_STRING "Server: " +#define KLIST_CLIENT_STRING "Client: " +#define KLIST_ENCTYPE_STRING "Ticket etype: " +#define KLIST_TICKET_LENGTH_STRING "Ticket length: " +#define KLIST_AUTHTIME_STRING "Auth time: " +#define KLIST_STARTTIME_STRING "Start time: " +#define KLIST_STOPTIME_STRING "End time: " +#define KLIST_FLAGS_STRING "Ticket flags: " +#define KLIST_ADDRESSES_STRING "Ticket flags: " +#define KLIST_NO_TICKET_FILE "klist: No ticket file: " +#define KLIST_KVNO_STRING "kvno" + +#define KLIST_ADDRESSLESS_STRING "addressless" + +#define KLIST_KRB5_TICKET_RESERVED "reserved" +#define KLIST_KRB5_TICKET_FORWARDABLE "forwardable" +#define KLIST_KRB5_TICKET_FORWARDED "forwarded" +#define KLIST_KRB5_TICKET_PROXIABLE "proxiable" +#define KLIST_KRB5_TICKET_PROXY "proxy" +#define KLIST_KRB5_TICKET_MAY_POSTDATE "may-postdate" +#define KLIST_KRB5_TICKET_POSTDATED "postdated" +#define KLIST_KRB5_TICKET_INVALID "invalid" +#define KLIST_KRB5_TICKET_RENEWABLE "renewable" +#define KLIST_KRB5_TICKET_INITIAL "initial" +#define KLIST_KRB5_TICKET_PREAUTHENT "pre-authent" +#define KLIST_KRB5_TICKET_HW_AUTHENT "hw-authent" +#define KLIST_KRB5_TICKET_TRANSIT_CHECKED "transited-policy-checked" +#define KLIST_KRB5_TICKET_OK_AS_DELEGATE "ok-as-delegate" +#define KLIST_KRB5_TICKET_ANONYMOUS "anonymous" +#define KLIST_KRB5_TICKET_ENC_PA_REP "enc-pa-rep" + +KerberosTicketInfoList LDAPManager::getKerberosTicketList(TQString cache, TQString *cacheFileName) { + KerberosTicketInfo ticket; + KerberosTicketInfoList list; + + TQString global_ccache; + TQString global_principal; + TQString global_cachevers; + + TQString line; + FILE *output = popen("klist -v 2>&1", "r"); + TQFile f; + f.open(IO_ReadOnly, output); + TQTextStream stream(&f); + while ( !stream.atEnd() ) { + line = stream.readLine(); + line = line.stripWhiteSpace(); + if (line == "") { + if (ticket.informationValid) { + ticket.cacheURL = global_ccache; + ticket.cachePrincipal = global_principal; + ticket.cacheVersion = global_cachevers.toInt(); + list.append(ticket); + } + ticket = KerberosTicketInfo(); + } + else if (line.startsWith(KLIST_NO_TICKET_FILE)) { + line.remove(0, strlen(KLIST_NO_TICKET_FILE)); + line.prepend("FILE:"); + if (cacheFileName) *cacheFileName = line; + } + else if (line.startsWith(KLIST_CREDENTIALS_CACHE_STRING)) { + line.remove(0, strlen(KLIST_CREDENTIALS_CACHE_STRING)); + global_ccache = line; + if (cacheFileName) *cacheFileName = line; + } + else if (line.startsWith(KLIST_PRINCIPAL_STRING)) { + line.remove(0, strlen(KLIST_PRINCIPAL_STRING)); + global_principal = line; + } + else if (line.startsWith(KLIST_CACHE_VERSION_STRING)) { + line.remove(0, strlen(KLIST_CACHE_VERSION_STRING)); + global_cachevers = line; + } + else if (line.startsWith(KLIST_SERVER_STRING)) { + line.remove(0, strlen(KLIST_SERVER_STRING)); + ticket.serverPrincipal = line; + ticket.informationValid = true; + } + else if (line.startsWith(KLIST_CLIENT_STRING)) { + line.remove(0, strlen(KLIST_CLIENT_STRING)); + ticket.clientPrincipal = line; + ticket.informationValid = true; + } + else if (line.startsWith(KLIST_ENCTYPE_STRING)) { + line.remove(0, strlen(KLIST_ENCTYPE_STRING)); + TQString kvno = line; + int commaloc = line.find(","); + kvno.remove(0, commaloc+1); + kvno.replace(KLIST_KVNO_STRING, ""); + kvno = kvno.stripWhiteSpace(); + line.truncate(commaloc); + ticket.encryptionType = line; + ticket.keyVersionNumber = kvno.toInt(); + ticket.informationValid = true; + } + else if (line.startsWith(KLIST_TICKET_LENGTH_STRING)) { + line.remove(0, strlen(KLIST_TICKET_LENGTH_STRING)); + ticket.ticketSize = line.toInt(); + ticket.informationValid = true; + } + else if (line.startsWith(KLIST_AUTHTIME_STRING)) { + line.remove(0, strlen(KLIST_AUTHTIME_STRING)); + line.replace("(expired)", ""); + line = line.simplifyWhiteSpace(); + line = klistDateTimeToRFCDateTime(line); + ticket.authenticationTime.setTime_t(KRFCDate::parseDate(line)); + ticket.informationValid = true; + } + else if (line.startsWith(KLIST_STARTTIME_STRING)) { + line.remove(0, strlen(KLIST_STARTTIME_STRING)); + line.replace("(expired)", ""); + line = line.simplifyWhiteSpace(); + line = klistDateTimeToRFCDateTime(line); + ticket.validStartTime.setTime_t(KRFCDate::parseDate(line)); + ticket.informationValid = true; + } + else if (line.startsWith(KLIST_STOPTIME_STRING)) { + line.remove(0, strlen(KLIST_STOPTIME_STRING)); + line.replace("(expired)", ""); + line = line.simplifyWhiteSpace(); + line = klistDateTimeToRFCDateTime(line); + ticket.validEndTime.setTime_t(KRFCDate::parseDate(line)); + ticket.informationValid = true; + } + else if (line.startsWith(KLIST_FLAGS_STRING)) { + line.remove(0, strlen(KLIST_FLAGS_STRING)); + TQStringList flags = TQStringList::split(",", line, FALSE); + for (TQStringList::Iterator it = flags.begin(); it != flags.end(); ++it) { + if ((*it) == KLIST_KRB5_TICKET_RESERVED) { + ticket.flags = ticket.flags | KRB5_TICKET_RESERVED; + } + else if ((*it) == KLIST_KRB5_TICKET_FORWARDABLE) { + ticket.flags = ticket.flags | KRB5_TICKET_FORWARDABLE; + } + else if ((*it) == KLIST_KRB5_TICKET_FORWARDED) { + ticket.flags = ticket.flags | KRB5_TICKET_FORWARDED; + } + else if ((*it) == KLIST_KRB5_TICKET_PROXIABLE) { + ticket.flags = ticket.flags | KRB5_TICKET_PROXIABLE; + } + else if ((*it) == KLIST_KRB5_TICKET_PROXY) { + ticket.flags = ticket.flags | KRB5_TICKET_PROXY; + } + else if ((*it) == KLIST_KRB5_TICKET_MAY_POSTDATE) { + ticket.flags = ticket.flags | KRB5_TICKET_MAY_POSTDATE; + } + else if ((*it) == KLIST_KRB5_TICKET_POSTDATED) { + ticket.flags = ticket.flags | KRB5_TICKET_POSTDATED; + } + else if ((*it) == KLIST_KRB5_TICKET_INVALID) { + ticket.flags = ticket.flags | KRB5_TICKET_INVALID; + } + else if ((*it) == KLIST_KRB5_TICKET_RENEWABLE) { + ticket.flags = ticket.flags | KRB5_TICKET_RENEWABLE; + } + else if ((*it) == KLIST_KRB5_TICKET_INITIAL) { + ticket.flags = ticket.flags | KRB5_TICKET_INITIAL; + } + else if ((*it) == KLIST_KRB5_TICKET_PREAUTHENT) { + ticket.flags = ticket.flags | KRB5_TICKET_PREAUTHENT; + } + else if ((*it) == KLIST_KRB5_TICKET_HW_AUTHENT) { + ticket.flags = ticket.flags | KRB5_TICKET_HW_AUTHENT; + } + else if ((*it) == KLIST_KRB5_TICKET_TRANSIT_CHECKED) { + ticket.flags = ticket.flags | KRB5_TICKET_TRANSIT_CHECKED; + } + else if ((*it) == KLIST_KRB5_TICKET_OK_AS_DELEGATE) { + ticket.flags = ticket.flags | KRB5_TICKET_OK_AS_DELEGATE; + } + else if ((*it) == KLIST_KRB5_TICKET_ANONYMOUS) { + ticket.flags = ticket.flags | KRB5_TICKET_ANONYMOUS; + } + else if ((*it) == KLIST_KRB5_TICKET_ENC_PA_REP) { + ticket.flags = ticket.flags | KRB5_TICKET_ENC_PA_REP; + } + } + ticket.informationValid = true; + } + else if (line.startsWith(KLIST_ADDRESSES_STRING)) { + line.remove(0, strlen(KLIST_ADDRESSES_STRING)); + if (line != KLIST_ADDRESSLESS_STRING) { + // FIXME + // What is the separator? + ticket.addresses = TQStringList(line); + } + ticket.informationValid = true; + } + } + f.close(); + pclose(output); + + return list; +} + +int LDAPManager::getKerberosPassword(LDAPCredentials &creds, TQString prompt, bool requestServicePrincipal, TQWidget* parent) +{ + int i; + + KSimpleConfig* systemconfig = new KSimpleConfig( TQString::fromLatin1( KDE_CONFDIR "/ldap/ldapconfigrc" )); + systemconfig->setGroup(NULL); + TQString defaultRealm = systemconfig->readEntry("DefaultRealm", TQString::null); + LDAPRealmConfigList realms = LDAPManager::readTDERealmList(systemconfig, false); + delete systemconfig; + + if (creds.realm != "") { + defaultRealm = creds.realm; + } + LDAPPasswordDialog passdlg(parent, 0, false); + passdlg.m_base->ldapAdminRealm->setEnabled(true); + LDAPRealmConfigList::Iterator it; + i=0; + for (it = realms.begin(); it != realms.end(); ++it) { + passdlg.m_base->ldapAdminRealm->insertItem((*it).name); + if ((*it).name == defaultRealm) { + passdlg.m_base->ldapAdminRealm->setCurrentItem(i); + } + i++; + } + passdlg.m_base->passprompt->setText(prompt); + passdlg.m_base->ldapUseTLS->hide(); + if (requestServicePrincipal) { + passdlg.m_base->kerberosOtherInfoString->show(); + passdlg.m_base->kerberosServicePrincipal->show(); + } + if (creds.username != "") { + passdlg.m_base->ldapAdminUsername->setText(creds.username); + passdlg.m_base->ldapAdminPassword->setFocus(); + } + const int ret = passdlg.exec(); + if (ret == KDialog::Accepted) { + creds.username = passdlg.m_base->ldapAdminUsername->text(); + creds.password = passdlg.m_base->ldapAdminPassword->password(); + creds.realm = passdlg.m_base->ldapAdminRealm->currentText(); + creds.service = passdlg.m_base->kerberosServicePrincipal->text(); + creds.use_tls = passdlg.m_base->ldapUseTLS->isOn(); + } + return ret; +} + +int LDAPManager::obtainKerberosTicket(LDAPCredentials creds, TQString principal, TQString *errstr) { + TQCString command = "kinit"; + QCStringList args; + if (principal == "") { + args << TQCString(creds.username + "@" + creds.realm.upper()); + } + else { + args << TQCString("-S") << TQCString(principal) << TQCString(creds.username + "@" + creds.realm.upper()); + } + + TQString prompt; + PtyProcess kadminProc; + kadminProc.exec(command, args); + prompt = kadminProc.readLine(true); + prompt = prompt.stripWhiteSpace(); + if (prompt.endsWith(" Password:")) { + kadminProc.writeLine(creds.password, true); + prompt = kadminProc.readLine(true); // Discard our own input + prompt = kadminProc.readLine(true); + prompt = prompt.stripWhiteSpace(); + } + if (prompt != "") { + if (errstr) *errstr = prompt; + return 1; + } + + // Success! + return 0; +} + +int LDAPManager::destroyKerberosTicket(TQString principal, TQString *errstr) { + TQString ret; + TQString command = TQString("kdestroy --credential=\"%1\"").arg(principal); + FILE *output = popen(command.ascii(), "r"); + TQFile f; + f.open(IO_ReadOnly, output); + TQTextStream stream(&f); + ret = stream.readLine(); + f.close(); + pclose(output); + + if (ret != "") { + if (errstr) *errstr = ret; + return -1; + } + return 0; +} + int LDAPManager::updateGroupInfo(LDAPGroupInfo group) { int retcode; int i; @@ -1992,4 +2308,18 @@ LDAPTDEBuiltinsInfo::~LDAPTDEBuiltinsInfo() { // } +KerberosTicketInfo::KerberosTicketInfo() { + // TQStrings are always initialized to TQString::null, so they don't need initialization here... + informationValid = false; + + cacheVersion = -1; + keyVersionNumber = -1; + ticketSize = -1; + flags = (KRB5TicketFlags)0; +} + +KerberosTicketInfo::~KerberosTicketInfo() { + // +} + #include "libtdeldap.moc" \ No newline at end of file diff --git a/src/libtdeldap.h b/src/libtdeldap.h index 39ce2b0..0edf803 100644 --- a/src/libtdeldap.h +++ b/src/libtdeldap.h @@ -77,6 +77,48 @@ enum LDAPKRB5Flags { KRB5_FLAG_MAX = 0x80000000 }; +inline LDAPKRB5Flags operator|(LDAPKRB5Flags a, LDAPKRB5Flags b) +{ + return static_cast(static_cast(a) | static_cast(b)); +} + +inline LDAPKRB5Flags operator&(LDAPKRB5Flags a, LDAPKRB5Flags b) +{ + return static_cast(static_cast(a) & static_cast(b)); +} + +// Values from krb5.asn1 +enum KRB5TicketFlags { + KRB5_TICKET_RESERVED = 0x00000001, + KRB5_TICKET_FORWARDABLE = 0x00000002, + KRB5_TICKET_FORWARDED = 0x00000004, + KRB5_TICKET_PROXIABLE = 0x00000008, + KRB5_TICKET_PROXY = 0x00000010, + KRB5_TICKET_MAY_POSTDATE = 0x00000020, + KRB5_TICKET_POSTDATED = 0x00000040, + KRB5_TICKET_INVALID = 0x00000080, + KRB5_TICKET_RENEWABLE = 0x00000100, + KRB5_TICKET_INITIAL = 0x00000200, + KRB5_TICKET_PREAUTHENT = 0x00000400, + KRB5_TICKET_HW_AUTHENT = 0x00000800, + KRB5_TICKET_TRANSIT_CHECKED = 0x00001000, + KRB5_TICKET_OK_AS_DELEGATE = 0x00002000, + KRB5_TICKET_ANONYMOUS = 0x00004000, + KRB5_TICKET_ENC_PA_REP = 0x00008000, + + KRB5_TICKET_FLAG_MAX = 0x80000000 +}; + +inline KRB5TicketFlags operator|(KRB5TicketFlags a, KRB5TicketFlags b) +{ + return static_cast(static_cast(a) | static_cast(b)); +} + +inline KRB5TicketFlags operator&(KRB5TicketFlags a, KRB5TicketFlags b) +{ + return static_cast(static_cast(a) & static_cast(b)); +} + typedef TQValueList UserList; typedef TQValueList GroupList; @@ -91,6 +133,7 @@ class LDAPCredentials TQCString password; TQString realm; bool use_tls; + TQString service; }; // PRIVATE @@ -262,9 +305,33 @@ class LDAPTDEBuiltinsInfo TQString builtinStandardUserGroup; }; +class KerberosTicketInfo +{ + public: + KerberosTicketInfo(); + ~KerberosTicketInfo(); + + public: + bool informationValid; + TQString cacheURL; + TQString cachePrincipal; + int cacheVersion; + TQString serverPrincipal; + TQString clientPrincipal; + TQString encryptionType; + int keyVersionNumber; + int ticketSize; + TQDateTime authenticationTime; + TQDateTime validStartTime; + TQDateTime validEndTime; + KRB5TicketFlags flags; + TQStringList addresses; +}; + typedef TQValueList LDAPUserInfoList; typedef TQValueList LDAPGroupInfoList; typedef TQValueList LDAPMachineInfoList; +typedef TQValueList KerberosTicketInfoList; class LDAPManager : public TQObject { Q_OBJECT @@ -315,6 +382,11 @@ class LDAPManager : public TQObject { static TQString ldapdnForRealm(TQString realm); static TQString cnFromDn(TQString dn); + static KerberosTicketInfoList getKerberosTicketList(TQString cache=TQString::null, TQString *cacheFileName=0); + static int getKerberosPassword(LDAPCredentials &creds, TQString prompt, bool requestServicePrincipal=false, TQWidget* parent=0); + static int obtainKerberosTicket(LDAPCredentials creds, TQString principal, TQString *errstr=0); + static int destroyKerberosTicket(TQString principal, TQString *errstr=0); + private: LDAPUserInfo parseLDAPUserRecord(LDAPMessage* entry); LDAPGroupInfo parseLDAPGroupRecord(LDAPMessage* entry);