Branch: master

bd30e6c6 2015-09-27 18:07:40 Timothy Pearson
Start to move away from using the kadmin binary to using the kadmin client API
M src/Makefile.am
M src/libtdeldap.cpp
M src/libtdeldap.h
diff --git a/src/Makefile.am b/src/Makefile.am
index edab92e..4d458e2 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -7,7 +7,7 @@
 include_HEADERS = libtdeldap.h ldappasswddlg.h
 
 libtdeldap_la_SOURCES = libtdeldap.cpp ldaplogindlgbase.ui ldaplogindlg.cpp ldappasswddlg.cpp
-libtdeldap_la_LIBADD = -ltdeio $(LIB_TDEUI) -lldap $(LIB_QT) $(LIB_TDECORE) -ltdesu -llber
+libtdeldap_la_LIBADD = -ltdeio $(LIB_TDEUI) -lldap $(LIB_QT) $(LIB_TDECORE) -ltdesu -llber -lkadm5clnt
 libtdeldap_la_LDFLAGS = -version-info $(lt_current):$(lt_revision):$(lt_age) -no-undefined \
 	$(all_libraries)
 
diff --git a/src/libtdeldap.cpp b/src/libtdeldap.cpp
index 1721bc5..d240428 100644
--- a/src/libtdeldap.cpp
+++ b/src/libtdeldap.cpp
@@ -108,13 +108,13 @@
 	}
 }
 
-LDAPManager::LDAPManager(TQString realm, TQString host, TQObject *parent, const char *name) : TQObject(parent, name), m_realm(realm), m_host(host), m_port(0), m_creds(0), m_ldap(0)
+LDAPManager::LDAPManager(TQString realm, TQString host, TQObject *parent, const char *name) : TQObject(parent, name), m_realm(realm), m_host(host), m_port(0), m_creds(0), m_ldap(0), m_krb5admHandle(0), m_krb5admKeytabFilename(0)
 {
 	TQStringList domainChunks = TQStringList::split(".", realm.lower());
 	m_basedc = "dc=" + domainChunks.join(",dc=");
 }
 
-LDAPManager::LDAPManager(TQString realm, TQString host, LDAPCredentials* creds, TQObject *parent, const char *name) : TQObject(parent, name), m_realm(realm), m_host(host), m_port(0), m_creds(creds), m_ldap(0)
+LDAPManager::LDAPManager(TQString realm, TQString host, LDAPCredentials* creds, TQObject *parent, const char *name) : TQObject(parent, name), m_realm(realm), m_host(host), m_port(0), m_creds(creds), m_ldap(0), m_krb5admHandle(0), m_krb5admKeytabFilename(0)
 {
 	TQStringList domainChunks = TQStringList::split(".", realm.lower());
 	m_basedc = "dc=" + domainChunks.join(",dc=");
@@ -405,6 +405,10 @@
 }
 
 int LDAPManager::unbind(bool force, TQString* errstr) {
+	if (m_krb5admHandle) {
+		unbindKAdmin();
+	}
+
 	if (!m_ldap) {
 		return 0;
 	}
@@ -1094,12 +1098,23 @@
 	return result;
 }
 
-int LDAPManager::setPasswordForUser(LDAPUserInfo user, TQString *errstr) {
-	if (user.new_password == "") {
-		return 0;
-	}
+int LDAPManager::bindKAdmin(LDAPUserInfo user, TQString *errstr) {
+	int retcode = 1;
+
+	kadm5_ret_t krb5adm_ret;
+	kadm5_config_params params;
 
 	LDAPCredentials admincreds = currentLDAPCredentials();
+	if (admincreds.use_gssapi) {
+		// FIXME
+		// Heimdal has issues parsing the keytab file, so for now just prompt for password
+		TQCString password;
+		int result = KPasswordDialog::getPassword(password, i18n("Enter password for %1").arg(admincreds.username));
+		if (result == KPasswordDialog::Accepted) {
+			admincreds.password = password;
+			admincreds.use_gssapi = false;
+		}
+	}
 	if ((admincreds.username == "") && (admincreds.password == "")) {
 		// Probably GSSAPI
 		// Get active ticket principal...
@@ -1107,107 +1122,107 @@
 		TQStringList principalParts = TQStringList::split("@", tickets[0].cachePrincipal, false);
 		admincreds.username = principalParts[0];
 		admincreds.realm = principalParts[1];
+		admincreds.use_gssapi = true;
 	}
 
-	TQCString command = "kadmin";
-	QCStringList args;
-	if (m_host.startsWith("ldapi://")) {
-		args << TQCString("-l") << TQCString("-r") << TQCString(admincreds.realm.upper());
+	TQString ticketFile;
+	LDAPManager::getKerberosTicketList(TQString::null, &ticketFile);
+
+	memset(&params, 0, sizeof(params));
+	params.mask |= KADM5_CONFIG_REALM;
+	params.realm = const_cast<char *>(admincreds.realm.upper().ascii());
+	
+	TQString adminPrincipal = TQString::null;
+	if (admincreds.username != "") {
+		adminPrincipal = admincreds.username.lower() + "@" + admincreds.realm.upper();
+	}
+
+	krb5adm_ret = krb5_init_context(&m_krb5admContext);
+	if (krb5adm_ret) {
+		if (errstr) *errstr = TQString("Internal Error<p>Failed to execute kadm5_init_krb5_context (code %1)").arg(krb5adm_ret);
 	}
 	else {
-		if (admincreds.username == "") {
-			args << TQCString("-r") << TQCString(admincreds.realm.upper());
+		if (m_host.startsWith("ldapi://")) {
+			// Local bind
+			krb5adm_ret = kadm5_init_with_password_ctx(m_krb5admContext, KADM5_ADMIN_SERVICE, admincreds.password.data(), KADM5_ADMIN_SERVICE, &params, KADM5_STRUCT_VERSION, KADM5_API_VERSION_2, &m_krb5admHandle);
+			if (krb5adm_ret) {
+				if (errstr) *errstr = TQString("%1<p>Details:<br>Failed to execute kadm5_init_with_password (code %2)").arg(krb5_get_error_message(m_krb5admContext, krb5adm_ret)).arg(krb5adm_ret);
+			}
+		}
+		else if (admincreds.use_gssapi) {
+			// Keytab authentication / bind
+			if (m_krb5admKeytabFilename) {
+				free(m_krb5admKeytabFilename);
+			}
+			m_krb5admKeytabFilename = strdup(ticketFile.ascii());
+			krb5adm_ret = kadm5_init_with_skey_ctx(m_krb5admContext, KADM5_ADMIN_SERVICE, m_krb5admKeytabFilename, KADM5_ADMIN_SERVICE, &params, KADM5_STRUCT_VERSION, KADM5_API_VERSION_2, &m_krb5admHandle);
+			if (krb5adm_ret) {
+				if (errstr) *errstr = TQString("%1<p>Details:<br>Failed to execute kadm5_init_with_skey (code %2)").arg(krb5_get_error_message(m_krb5admContext, krb5adm_ret)).arg(krb5adm_ret);
+			}
 		}
 		else {
-			args << TQCString("-p") << TQCString(admincreds.username.lower()+"@"+(admincreds.realm.upper())) << TQCString("-r") << TQCString(admincreds.realm.upper());
+			// Password authentication / bind
+			krb5adm_ret = kadm5_init_with_password_ctx(m_krb5admContext, adminPrincipal.ascii(), admincreds.password.data(), KADM5_ADMIN_SERVICE, &params, KADM5_STRUCT_VERSION, KADM5_API_VERSION_2, &m_krb5admHandle);
+			if (krb5adm_ret) {
+				if (errstr) *errstr = TQString("%1<p>Details:<br>Failed to execute kadm5_init_with_password (code %2)").arg(krb5_get_error_message(m_krb5admContext, krb5adm_ret)).arg(krb5adm_ret);
+			}
+		}
+		if (!krb5adm_ret) {
+			// Success!
+			retcode = 0;
 		}
 	}
 
-	TQString prompt;
-	PtyProcess kadminProc;
-	kadminProc.exec(command, args);
-	prompt = readFullLineFromPtyProcess(&kadminProc);
-	prompt = prompt.stripWhiteSpace();
-	if (prompt == "kadmin>") {
-		command = TQCString("passwd "+user.name);
-		kadminProc.enableLocalEcho(false);
-		kadminProc.writeLine(command, true);
-		do { // Discard our own input
-			prompt = readFullLineFromPtyProcess(&kadminProc);
-			printf("(kadmin) '%s'\n", prompt.ascii());
-		} while ((prompt == TQString(command)) || (prompt == ""));
-		prompt = prompt.stripWhiteSpace();
-		if ((prompt.endsWith(" Password:")) && (prompt.startsWith(TQString(user.name + "@")))) {
-			kadminProc.enableLocalEcho(false);
-			kadminProc.writeLine(user.new_password, true);
-			do { // Discard our own input
-				prompt = readFullLineFromPtyProcess(&kadminProc);
-				printf("(kadmin) '%s'\n", prompt.ascii());
-			} while (prompt == "");
-			prompt = prompt.stripWhiteSpace();
-			if ((prompt.endsWith(" Password:")) && (prompt.startsWith("Verify"))) {
-				kadminProc.enableLocalEcho(false);
-				kadminProc.writeLine(user.new_password, true);
-				do { // Discard our own input
-					prompt = readFullLineFromPtyProcess(&kadminProc);
-					printf("(kadmin) '%s'\n", prompt.ascii());
-				} while (prompt == "");
-				prompt = prompt.stripWhiteSpace();
-			}
-			if (prompt.endsWith(" Password:")) {
-				if (admincreds.password == "") {
-					if (tqApp->type() != TQApplication::Tty) {
-						TQCString password;
-						int result = KPasswordDialog::getPassword(password, prompt);
-						if (result == KPasswordDialog::Accepted) {
-							admincreds.password = password;
-						}
-					}
-					else {
-						TQFile file;
-						file.open(IO_ReadOnly, stdin);
-						TQTextStream qtin(&file);
-						admincreds.password = qtin.readLine();
-					}
-				}
-				if (admincreds.password != "") {
-					kadminProc.enableLocalEcho(false);
-					kadminProc.writeLine(admincreds.password, true);
-					do { // Discard our own input
-						prompt = readFullLineFromPtyProcess(&kadminProc);
-						printf("(kadmin) '%s'\n", prompt.ascii());
-					} while (prompt == "");
-					prompt = prompt.stripWhiteSpace();
-				}
-			}
-			if (prompt != "kadmin>") {
-				if (errstr) *errstr = detailedKAdminErrorMessage(prompt);
-				kadminProc.enableLocalEcho(false);
-				kadminProc.writeLine("quit", true);
-				return 1;
-			}
+	return retcode;
+}
 
-			// Success!
-			kadminProc.enableLocalEcho(false);
-			kadminProc.writeLine("quit", true);
-			return 0;
-		}
-		else if (prompt == "kadmin>") {
-			// Success!
-			kadminProc.enableLocalEcho(false);
-			kadminProc.writeLine("quit", true);
-			return 0;
-		}
-
-		// Failure
-		if (errstr) *errstr = detailedKAdminErrorMessage(prompt);
-		kadminProc.enableLocalEcho(false);
-		kadminProc.writeLine("quit", true);
-		return 1;
+int LDAPManager::unbindKAdmin(TQString *errstr) {
+	if (m_krb5admKeytabFilename) {
+		free(m_krb5admKeytabFilename);
 	}
 
-	if (errstr) *errstr = "Internal error.  Verify that kadmin exists and can be executed.";
-	return 1;	// Failure
+	kadm5_destroy(m_krb5admHandle);
+	krb5_free_context(m_krb5admContext);
+
+	m_krb5admHandle = NULL;
+
+	return 0;
+}
+
+int LDAPManager::setPasswordForUser(LDAPUserInfo user, TQString *errstr) {
+	if (user.new_password == "") {
+		return 0;
+	}
+
+	int retcode;
+	kadm5_ret_t krb5adm_ret;
+
+	retcode = bindKAdmin(user, errstr);
+	if (retcode == 0) {
+		retcode = 1;
+		krb5_principal user_kadm5_principal;
+		krb5adm_ret = krb5_parse_name(m_krb5admContext, user.name.ascii(), &user_kadm5_principal);
+		if (krb5adm_ret) {
+			if (errstr) *errstr = TQString("%1<p>Details:<br>Failed to execute krb5_parse_name for user '%2' (code %3)").arg(krb5_get_error_message(m_krb5admContext, krb5adm_ret)).arg(user.name).arg(krb5adm_ret);
+		}
+		else {
+			krb5adm_ret = kadm5_chpass_principal(m_krb5admHandle, user_kadm5_principal, user.new_password.data());
 ** Diff limit reached (max: 250 lines) **